2008-10-15 4 views
9

Я работаю над приложением для работы, которое будет запрашивать нашу базу данных сотрудников. Конечным пользователям требуется возможность поиска на основе стандартных критериев имени/отдела, но они также хотят, чтобы гибкость запрашивала у всех людей с именем «Джеймс», который работает в отделе здравоохранения. Единственное, чего я хочу избежать, - просто сохранить хранимую процедуру в списке параметров и сгенерировать инструкцию SQL для выполнения, поскольку это откроет двери для SQL-инъекций на внутреннем уровне.Как создать хранимую процедуру, которая будет необязательно искать столбцы?

Можно ли это сделать?

+0

Я хотел бы упомянуть здесь, что решение Кейд Ру работал лучше для меня, так как я имел много значений данных NULL в целевой таблице, но я мог видеть, как хорошо COALESCE бы работать, если бы я имел данные во всех моих столбцы, поэтому преимущество в решении BoltBait определенно гарантирует достоинства. – 2008-10-15 21:06:09

+1

Аарон Бертран называет это «Процедурой кухонной раковины» и имеет некоторые хорошие мысли о решении этой проблемы, которую можно увидеть на http://sqlsentry.tv/the-kitchen-sink-procedure/ и http: // blogs .sqlsentry.com/aaronbertrand/backtobasics обновляемый-натуралистический-пример /. – JamieSee 2016-09-21 21:21:57

ответ

16

Хотя COALESCE трюка аккуратно, мой предпочтительный метод:

CREATE PROCEDURE ps_Customers_SELECT_NameCityCountry 
    @Cus_Name varchar(30) = NULL 
    ,@Cus_City varchar(30) = NULL 
    ,@Cus_Country varchar(30) = NULL 
    ,@Dept_ID int = NULL 
    ,@Dept_ID_partial varchar(10) = NULL 
AS 
SELECT Cus_Name 
     ,Cus_City 
     ,Cus_Country 
     ,Dept_ID 
FROM Customers 
WHERE (@Cus_Name IS NULL OR Cus_Name LIKE '%' + @Cus_Name + '%') 
     AND (@Cus_City IS NULL OR Cus_City LIKE '%' + @Cus_City + '%') 
     AND (@Cus_Country IS NULL OR Cus_Country LIKE '%' + @Cus_Country + '%') 
     AND (@Dept_ID IS NULL OR Dept_ID = @DeptID) 
     AND (@Dept_ID_partial IS NULL OR CONVERT(varchar, Dept_ID) LIKE '%' + @Dept_ID_partial + '%') 

Эти типы SP могут быть легко сгенерированы кодом (и сгенерированы для изменений таблицы).

У вас есть несколько вариантов обработки чисел - в зависимости от точной семантики или поисковой семантики.

-1

Моя первая мысль была, чтобы написать что-то вроде этого запроса ...

SELECT EmpId, NameLast, NameMiddle, NameFirst, DepartmentName 
    FROM dbo.Employee 
     INNER JOIN dbo.Department ON dbo.Employee.DeptId = dbo.Department.Id 
WHERE IdCrq IS NOT NULL 
     AND 
     (
      @bitSearchFirstName = 0 
      OR 
      Employee.NameFirst = @vchFirstName 
     ) 
     AND 
     (
      @bitSearchMiddleName = 0 
      OR 
      Employee.NameMiddle = @vchMiddleName 
     ) 
     AND 
     (
      @bitSearchFirstName = 0 
      OR 
      Employee.NameLast = @vchLastName 
     ) 
     AND 
     (
      @bitSearchDepartment = 0 
      OR 
      Department.Id = @intDeptID 
     ) 

... который бы тогда звонящий предоставить немного флаг, если они хотят, чтобы найти конкретное поле, а затем поставить значение, если они должны его искать, но я не знаю, создает ли это sloppy предложение WHERE или я могу уйти с оператором CASE в предложении WHERE.

Как вы можете видеть, этот код находится в T-SQL, но я также с удовольствием рассмотрю некоторый код PL-SQL/MySQL и соответствующим образом адаптирую его.

+0

Мне просто интересно, что не так с этим запросом, который получает отрицательные голоса? – 2013-08-02 19:25:23

+0

@Teomanshipahi Проблема с моим запросом заключается в том, что для этого требуются две переменные: бит для поиска в поле, а затем сам параметр, который связывает очень большую привязку переменной хранимой процедуры. Используя coalesce, вы можете использовать одну переменную для каждого поля, которое вы хотите запросить. Думаю, я должен удалить свой собственный ответ, учитывая, сколько лет этот поток 8^D – 2013-08-02 20:44:06

9

Наиболее эффективным способом реализации этого типа поиска является хранимая процедура. Вышеприведенный оператор создает процедуру, которая принимает требуемые параметры. Если значение параметра не указано, оно равно NULL.

CREATE PROCEDURE ps_Customers_SELECT_NameCityCountry 
@Cus_Name varchar(30) = NULL, 
@Cus_City varchar(30) = NULL, 
@Cus_Country varchar(30) =NULL 
AS 
SELECT Cus_Name, 
     Cus_City, 
     Cus_Country 
FROM Customers 
WHERE Cus_Name = COALESCE(@Cus_Name,Cus_Name) AND 
     Cus_City = COALESCE(@Cus_City,Cus_City) AND 
     Cus_Country = COALESCE(@Cus_Country,Cus_Country) 

Взятые с этой страницы: http://www.sqlteam.com/article/implementing-a-dynamic-where-clause

Я сделал это раньше. Это работает хорошо.

+0

Я не думаю, что они работают хорошо. Вы получаете огромный процент сканирования таблиц, потому что эти предикаты не являются SARGable. – 2008-10-15 17:03:36

+0

Когда вы говорите, что значение установлено в NULL, вы имеете в виду поиск NULL в имени столбца или его игнорирование. Единственное беспокойство, которое я вижу в этом, - это то, что если я ищу людей с фамилией Шмоу, то, что имя «Джо» будет исключено, поскольку значение не равно NULL. – 2008-10-15 17:03:45

+0

Dillie-O, найдите команду COALESCE, чтобы узнать, почему это работает (или следуйте ссылке, приведенной в моем сообщении).Что касается производительности такого типа ... система, которую я реализовал, имела от 1 до 2 миллионов строк, и она работала нормально. Это казалось совсем не медленным. YMMV. – BoltBait 2008-10-15 17:13:16

2

Это можно сделать, но обычно эти процедуры для кухонной мойки приводят к некоторым плохим запросам.

Сказав это, вот тактика, наиболее часто используемая для «необязательных» параметров. Обычный подход заключается в том, чтобы рассматривать NULL как «опускаемый».

SELECT 
    E.EmployeeID, 
    E.LastName, 
    E.FirstName 
WHERE 
    E.FirstName = COALESCE(@FirstName, E.FirstName) AND 
    E.LastName = COALESCE(@LastName, E.LastName) AND 
    E.DepartmentID = COALESCE(@DepartmentID, E.DepartmentID) 

EDIT: Гораздо лучше было бы параметризованных запросов. Вот блог одного из ведущих мировых специалистов в этой области, Frans Баума из LLBLGen Pro славы: статья

Stored Procedures vs. Dynamic Queries

+0

В предыдущем ответе вы сказали, что это не так хорошо работает, вы думаете, что я просто кусаю пулю, делаю много дезинфекции ввода и создаю запрос AdHoc или создаю более специализированные процедуры для всех разных опций? – 2008-10-15 17:08:06

5

Erland Sommarskog в Dynamic Search Conditions in T-SQL является хорошим справочником о том, как это сделать. Erland предлагает ряд стратегий, как это сделать, не используя динамический SQL (только простые блоки IF, OR, COALESCE и т. Д.) И даже перечисляет характеристики производительности каждого метода.

В случае, если вы должны стиснуть зубы и идти по пути Dynamic SQL, вы должны также прочитать Erland-х Curse and Blessings of Dynamic SQL, где он дает некоторые советы о том, как правильно писать динамическую SQLs

-1

Я бы придерживался метода NULL/COALESCE по запросам AdHoc, а затем тестировал, чтобы убедиться, что у вас нет проблем с производительностью.

Если выясняется, что у вас медленные запросы, потому что он выполняет сканирование таблицы при поиске индексированных столбцов, вы всегда можете дополнить общую поисковую хранимую процедуру дополнительными конкретными, которые позволяют осуществлять поиск по этим индексированным поля. Например, у вас может быть специальный SP, который выполняет поиск по CustomerID, или Last/First Name.

3

Использование метода COALESCE имеет проблему в том, что если столбец имеет значение NULL, то передача в режиме поиска NULL (что означает игнорирование условия поиска) не будет возвращать строку во многих базах данных.

Например, попробуйте следующий код на SQL Server 2000:

CREATE TABLE dbo.Test_Coalesce (
    my_id INT NOT NULL IDENTITY, 
    my_string VARCHAR(20) NULL) 
GO 
INSERT INTO dbo.Test_Coalesce (my_string) VALUES (NULL) 
INSERT INTO dbo.Test_Coalesce (my_string) VALUES ('t') 
INSERT INTO dbo.Test_Coalesce (my_string) VALUES ('x') 
INSERT INTO dbo.Test_Coalesce (my_string) VALUES (NULL) 
GO 
DECLARE @my_string VARCHAR(20) 
SET @my_string = NULL 
SELECT * FROM dbo.Test_Coalesce WHERE my_string = COALESCE(@my_string, my_string) 
GO 

Вы только получите обратно два ряда, так как в строках, где столбец my_string является NULL вы эффективны получаете:

my_string = COALESCE(@my_string, my_string) => 
my_string = COALESCE(NULL, my_string) => 
my_string = my_string => 
NULL = NULL 

Но, конечно, NULL не равен NULL.

Я стараюсь придерживаться:

SELECT 
    my_id, 
    my_string 
FROM 
    dbo.Test_Coalesce 
WHERE 
    (@my_string IS NULL OR my_string = @my_string) 

Конечно, вы можете настроить, что использовать дикие карты или то, что вы хотите сделать.

0

Копирование это от моего сообщения в блоге:

USE [AdventureWorks] 
GO 

CREATE PROCEDURE USP_GET_Contacts_DynSearch 
(
    -- Optional Filters for Dynamic Search 
    @ContactID   INT = NULL, 
    @FirstName   NVARCHAR(50) = NULL, 
    @LastName   NVARCHAR(50) = NULL, 
    @EmailAddress  NVARCHAR(50) = NULL, 
    @EmailPromotion  INT = NULL, 
    @Phone    NVARCHAR(25) = NULL 
) 
AS 
BEGIN 
    SET NOCOUNT ON 

    DECLARE 
     @lContactID   INT, 
     @lFirstName   NVARCHAR(50), 
     @lLastName   NVARCHAR(50), 
     @lEmailAddress  NVARCHAR(50), 
     @lEmailPromotion INT, 
     @lPhone    NVARCHAR(25) 

    SET @lContactID   = @ContactID 
    SET @lFirstName   = LTRIM(RTRIM(@FirstName)) 
    SET @lLastName   = LTRIM(RTRIM(@LastName)) 
    SET @lEmailAddress  = LTRIM(RTRIM(@EmailAddress)) 
    SET @lEmailPromotion = @EmailPromotion 
    SET @lPhone    = LTRIM(RTRIM(@Phone)) 

    SELECT 
     ContactID, 
     Title, 
     FirstName, 
     MiddleName, 
     LastName, 
     Suffix, 
     EmailAddress, 
     EmailPromotion, 
     Phone 
    FROM [Person].[Contact] 
    WHERE 
     (@lContactID IS NULL OR ContactID = @lContactID) 
    AND (@lFirstName IS NULL OR FirstName LIKE '%' + @lFirstName + '%') 
    AND (@lLastName IS NULL OR LastName LIKE '%' + @lLastName + '%') 
    AND (@lEmailAddress IS NULL OR EmailAddress LIKE '%' + @lEmailAddress + '%') 
    AND (@lEmailPromotion IS NULL OR EmailPromotion = @lEmailPromotion) 
    AND (@lPhone IS NULL OR Phone = @lPhone) 
    ORDER BY ContactID 

END 
GO 
-3

Напишите процедуру, чтобы вставить все данные сотрудника, чье имя начать с в таблице ??

0

Мы можем использовать Generic @Search Parameter и передавать любое значение для его поиска.

GO 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
-- ============================================= 
-- Author: -- 
-- Create date: 
-- Description: -- 
-- ============================================= 
CREATE PROCEDURE [dbo].[usp_StudentList] 
    @PageNumber INT = 1, -- Paging parameter 
    @PageSize INT = 10,-- Paging parameter 
    @Search VARCHAR(MAX) = NULL, --Generic Search Parameter 
    @OrderBy VARCHAR(MAX) = 'FirstName', --Default Column Name 'FirstName' for records ordering 
    @SortDir VARCHAR(MAX) = 'asc' --Default ordering 'asc' for records ordering 
AS 
BEGIN 
    SET NOCOUNT ON; 

    --Query required for paging, this query used to show total records 
    SELECT COUNT(StudentId) AS RecordsTotal FROM Student 

    SELECT Student.*, 
     --Query required for paging, this query used to show total records filtered 
     COUNT(StudentId) OVER (PARTITION BY 1) AS RecordsFiltered 
    FROM Student 
    WHERE 
    --Generic Search 
    -- Below is the column list to add in Generic Serach 
    (@Search IS NULL OR Student.FirstName LIKE '%'+ @Search +'%') 
    OR (@Search IS NULL OR Student.LastName LIKE '%'+ @Search +'%') 
    --Order BY 
    -- Below is the column list to allow sorting 
    ORDER BY 
    CASE WHEN @SortDir = 'asc' AND @OrderBy = 'FirstName' THEN Student.FirstName END, 
    CASE WHEN @SortDir = 'desc' AND @OrderBy = 'FirstName' THEN Student.FirstName END DESC, 
    CASE WHEN @SortDir = 'asc' AND @OrderBy = 'LastName' THEN Student.LastName END, 
    CASE WHEN @SortDir = 'desc' AND @OrderBy = 'LastName' THEN Student.LastName END DESC, 
    OFFSET @PageSize * (@PageNumber - 1) ROWS FETCH NEXT @PageSize ROWS ONLY; 
END