2014-01-15 4 views
2

Я очень много нового для SQL и изо всех сил стараюсь учиться, поэтому я предупреждаю всех, что мои ошибки могут быть очевидными - не думайте, что я знаю, что я делаю ! Это находится в SQL Server 2005.SQL Server 2005 Dynamic Select

EDIT: приведенный ниже код случайно вводит в заблуждение; my_table - НЕ таблица, это вид. Упомянутая информация просмотра стежков вместе по двум схемам использует LEFT OUTER JOIN для одного из своих столбцов, использует материал ((SELECT .....)) для четырех из них, и когда я разместил это, не было столбца, для которого нет двух строк использовать одни и те же данные; С тех пор я изменил его, чтобы использовать SELECT DISTINCT.

Я пытаюсь написать функцию, которая принимает в качестве аргумента четыре части имени - первый, средний, последний и заголовок - и запускает выбор по данной таблице на основе этих 4 частей, используя LIKE. Этот наивный подход возвращает правильные результаты:

ALTER FUNCTION [my_schema].[my_table] (
    @first NVARCHAR(max) = null 
    ,@middle NVARCHAR(max) = null 
    ,@last NVARCHAR(max) = null 
    ,@title NVARCHAR(max) = null 
) 
RETURNS TABLE AS RETURN(
    SELECT DISTINCT 
     l.foo, 
     l.bar, 
     l.spam 
    FROM my_database.my_schema.my_table l 
    WHERE 
     l.FirstName like ISNULL('%'[email protected]+'%', '%') 
     AND l.LastName like ISNULL('%'[email protected]+'%', '%') 
     AND l.MiddleName like ISNULL('%'[email protected]+'%', '%') 
     AND l.TitleName like ISNULL('%'[email protected]+'%', '%') 
); 

Проблема в том, что это невероятно медленно. Самая очевидная причина, по которой я могу видеть медленность, - это очень глупое поведение, когда сравниваются «%»; я бы хотел, чтобы SQL отвечал на NULL, не выполняя сравнения вообще. Мне еще предстоит разработать способ сделать это; каждое решение, которое я придумываю, запускается в стену, которая является синтаксисом SQL.

Мой вопрос: «Как избежать сравнения LIKE с«% », когда соответствующая переменная равна null?». Я как можно противник использовать EXEC в любом контексте, потому что я беру пользовательский ввод из дикой природы и возвращаю потенциально чувствительные данные, и я хотел бы быть как можно более параноидальным в отношении инъекций - эти четыре переменные - это дикий вход , и я хотел бы доверять им как можно меньше.

EDIT: Я сделал тесты скорости и с тех пор пришел к выводу, что избежать лишних LIKE не помогает ни в какой значимой степени. Я приветствую любые рекомендации по фактическому достижению ускорения и, скорее всего, вскоре опубликую новый вопрос по этим направлениям, как только я передумаю, как это происходит.

+0

Это не NULL, это 'LIKE'. Утверждение типа '% anything%' гарантируется принудительным сканированием таблицы, даже если в текстовых полях указаны индексы. Вы запрашиваете строки с некоторым значением * в любом месте * в поле. Такие поиски, как 'something%', представляют собой поиск по диапазону, чтобы они могли использовать индексы. Если вы действительно хотите выполнять полнотекстовый поиск, вам лучше использовать функции полнотекстового поиска (http://technet.microsoft.com/en-us/library/ms142583.aspx), такие как CONTAINS и FREETEXT –

+0

К сожалению, для этого требуется уникальный кластерный индекс в базовом представлении, и есть несколько причин, по которым SQL Server отказывается создавать его для меня, включая использование LEFT OUTER JOIN, использование трех отдельных подзапросов и использование DISTINCT. Я не уверен, как реорганизовать представление, чтобы избежать этих проблем, сохраняя при этом свою полезность. – quindraco

+0

Какой вид? Является ли 'my_table' не таблицей? Если это таблица, * почему * вы не можете создать кластеризованный индекс, если его еще нет, потому что, например, ваш первичный ключ? * Почему * вы пытаетесь создать индекс FTS для представления вместо таблицы?Если вы не предоставите все детали, это невозможно. Во всяком случае, проблема * - это то, как вы используете 'LIKE', чтобы сделать по существу FTS –

ответ

0

У вас есть несколько вариантов

1-й. имеют необязательный параметр, такой как

WHERE 
    (@first IS null or l.FirstName like @first) 
    AND 
    (@last IS null or l.LastName like @last) 
AND 
(@middle IS null or l.MiddleName like @middle) 
    AND 
    (@title IS null or l.TitleNamelike @title) 

2-й. построить динамический SQL

CREATE PROCEDURE GetCustomer 
@FirstN nvarchar(20) = NULL, 
@LastN nvarchar(20) = NULL, 
@CUserName nvarchar(10) = NULL, 
@CID nvarchar(15) = NULL as 
DECLARE @sql nvarchar(4000), 
SELECT @sql = 'C_FirstName, C_LastName, C_UserName, C_UserID ' + 
'FROM CUSTOMER ' + 
'WHERE 1=1 ' + 

IF @FirstN IS NOT NULL 
SELECT @sql = @sql + ' AND C_FirstName like @FirstN ' 
IF @LastN IS NOT NULL 
SELECT @sql = @sql + ' AND C_LastName like @LastN ' 
IF @CUserName IS NOT NULL 
SELECT @sql = @sql + ' AND C_UserName like @CUserName ' 
IF @CID IS NOT NULL 
SELECT @sql = @sql + ' AND C_UserID like @CID ' 
EXEC sp_executesql @sql, N'@C_FirstName nvarchar(20), @C_LastName nvarchar(20),  @CUserName nvarchar(10), @CID nvarchar(15)', 
       @FirstN, @LastN, @CUserName, @CID 
+0

Первый подход означает, что запрос ALTER выполняется навсегда, но сейчас я попытаюсь применить этот второй подход. – quindraco

+0

Несомненно, второй подход является принципиально незаконным; вы не можете выполнять динамический SQL внутри функции. – quindraco

+0

Нет проблемы с выполнением динамического SQL внутри хранимой процедуры. В зависимости от того, с кем вы разговариваете, некоторые люди просто отказываются его использовать, другие используют его. Вы сказали, что 1-й запрос будет работать вечно, это не так, если вы пройдете все нули, все будет выбрано. если только 1 параметр имеет значение null, что будет оцениваться только этот случай. –

2

Я не уверен, если вы получите лучшую производительность или нет, но я думаю, что следующий случай заявление в вашем Если заявление будет работать.

WHERE l.FirstName LIKE CASE WHEN @first IS NULL THEN '%' ELSE '%' + @First + '%' END 
AND l.LastName LIKE CASE WHEN @last IS NULL THEN '%' ELSE '%' + @Last + '%' END 
AND ...