Использование произвольного количества параметров в T-SQL - PullRequest
3 голосов
/ 19 ноября 2008

Можно ли создать параметризованный оператор SQL, который будет принимать произвольное количество параметров? Я пытаюсь разрешить пользователям фильтровать список по нескольким ключевым словам, каждое из которых разделяется точкой с запятой. Таким образом, ввод будет выглядеть примерно так: «Окленд; Город; Планирование», а предложение WHERE будет выглядеть примерно так:

WHERE ProjectName LIKE '%Oakland%' AND ProjectName Like '%City%' AND ProjectName Like '%Planning%'

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

Производительность не является большой проблемой, потому что таблица сейчас насчитывает всего около 900 строк и не будет расти очень быстро, возможно, от 50 до 100 строк в год.

Ответы [ 9 ]

6 голосов
/ 19 ноября 2008

Базовое подтверждение концепции ... Фактического кода было бы меньше, но, поскольку я не знаю имен ваших таблиц / полей, это полный код, поэтому любой может проверить, работает ли он, настроить его и т. Д.

--Search Parameters

DECLARE @SearchString VARCHAR(MAX)
SET @SearchString='Oakland;City;Planning' --Using your example search
DECLARE @Delim CHAR(1)
SET @Delim=';' --Using your deliminator from the example

--I didn't know your table name, so I'm making it... along with a few extra rows...

DECLARE @Projects TABLE (ProjectID INT, ProjectName VARCHAR(200))
INSERT INTO @Projects (ProjectID, ProjectName) SELECT 1, 'Oakland City Planning'
INSERT INTO @Projects (ProjectID, ProjectName) SELECT 2, 'Oakland City Construction'
INSERT INTO @Projects (ProjectID, ProjectName) SELECT 3, 'Skunk Works'
INSERT INTO @Projects (ProjectID, ProjectName) SELECT 4, 'Oakland Town Hall'
INSERT INTO @Projects (ProjectID, ProjectName) SELECT 5, 'Oakland Mall'
INSERT INTO @Projects (ProjectID, ProjectName) SELECT 6, 'StackOverflow Answer Planning'

--*** MAIN PROGRAM CODE STARTS HERE ***

DECLARE @Keywords TABLE (Keyword VARCHAR(MAX))

DECLARE @index int 
SET @index = -1 

--Each keyword gets inserted into the table
--Single keywords are handled, but I did not add code to remove duplicates
--since that affects performance only, not the result.

WHILE (LEN(@SearchString) > 0) 
  BEGIN  
    SET @index = CHARINDEX(@Delim , @SearchString)  
    IF (@index = 0) AND (LEN(@SearchString) > 0)  
      BEGIN   
        INSERT INTO @Keywords VALUES (@SearchString)
          BREAK  
      END  
    IF (@index > 1)  
      BEGIN   
        INSERT INTO @Keywords VALUES (LEFT(@SearchString, @index - 1))   
        SET @SearchString = RIGHT(@SearchString, (LEN(@SearchString) - @index))  
      END  
    ELSE 
      SET @SearchString = RIGHT(@SearchString, (LEN(@SearchString) - @index)) 
END


--This way, only a project with all of our keywords will be shown...

SELECT * 
FROM @Projects
WHERE ProjectID NOT IN (SELECT ProjectID FROM @Projects Projects INNER JOIN @Keywords Keywords ON CHARINDEX(Keywords.Keyword,Projects.ProjectName)=0)

Я решил смешать несколько разных ответов в один: -P

Предполагается, что вы передадите список строк поиска с разделителями (переданный через @SearchString) как VARCHAR (MAX) , что - реально - вы не будете натолкнуться на ограничение для поиска по ключевым словам .

Каждое ключевое слово вычеркивается из списка и добавляется в таблицу ключевых слов. Возможно, вы захотите добавить код для удаления дублирующихся ключевых слов, но в моем примере это не повредит. Просто немного менее эффективно, так как в идеале нам нужно оценивать только один раз по ключевому слову.

Оттуда, любое ключевое слово, которое не является частью имени проекта, удаляет этот проект из списка ...

Таким образом, поиск "Окленд" дает 4 результата, а "Окленд; Город; Планирование" дает только 1 результат.

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

Кроме того, из-за объединений, а не из-за динамического SQL, он не подвергается риску SQL-инъекции, как вас беспокоило.

2 голосов
/ 19 ноября 2008

Вы также можете рассмотреть полнотекстовый поиск и использовать CONTAINS или CONTAINSTABLE для более «естественного» поиска.

Может быть излишним для 1K строк, но оно написано и не может быть легко подорвано инъекцией.

1 голос
/ 19 ноября 2008

Как насчет использования типа данных XML для хранения параметров? Он может быть неограниченным и собран во время выполнения ...

Я передаю неизвестное количество PK для обновления таблицы, а затем закачиваю их во временную таблицу. Затем легко обновить, где PK в PKTempTable.

Вот код для анализа типа данных XML ...

    INSERT INTO #ERXMLRead (ExpenseReportID)
    SELECT ParamValues.ID.value('.','VARCHAR(20)')
    FROM @ExpenseReportIDs.nodes('/Root/ExpenseReportID') as ParamValues(ID)
1 голос
/ 19 ноября 2008

Уловка обычно заключается в простой передаче списка в виде строки, разделенной запятыми (стиль csv), разбора этой строки в цикле и динамического построения запроса.

Над постом также верно, что, возможно, лучшим подходом является не T-SQL, а уровень бизнеса / приложений.

0 голосов
/ 24 февраля 2009

Как и в некоторых других ответах, вы можете анализировать строку с разделителями или XML-документ. См. эту превосходную ссылку , которая демонстрирует оба метода с SQL Server.

0 голосов
/ 19 ноября 2008

Каким бы ни был ваш путь, следите за ограничением параметров SQL Server: ~ 2000 параметров.

0 голосов
/ 19 ноября 2008

Если вы используете SQL Server 2008, ознакомьтесь с этим художественным передачей табличного параметра

0 голосов
/ 19 ноября 2008

Если вы используете хранимые процедуры, вы можете включить значение по умолчанию для параметров, затем вы можете передать их или не передавать их в коде клиента, но вам все равно придется объявить их отдельно в хранимой процедуре ... только если вы используете хранимый процесс, вы можете передать один параметр в виде строки значений с разделителями и проанализировать отдельные значения внутри sproc (есть несколько «стандартных» функций T-SQL, которые разделяют записи в переменную динамической таблицы для вас)

0 голосов
/ 19 ноября 2008

с помощью такого инструмента, как NHibernate, позволит вам безопасно создавать динамические запросы без необходимости хранимых процедур.

У Франса Бума есть отличная статья о хранимых процессах и динамическом sql, а также о том, какие преимущества использования генератора SQL можно получить с помощью операторов, сгенерированных вручную

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...