Хранимая процедура T-SQL Входные значения NULL приводят к сбою оператора select - PullRequest
6 голосов
/ 19 декабря 2008

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

Звучит совершенно просто, но SP не работает. Проблема в том, что некоторые параметры, передаваемые в SP, могут иметь нулевое значение, и поэтому sql должен читать «is null», а не «= null». Я пробовал isnull (), операторы case, coalesce () и динамический sql с exec () и sp_executesql и не смог реализовать ни один из них. Вот код ...

CREATE PROCEDURE sp_myDuplicateCheck
 @userId int,
 @noteType char(1),
 @aCode char(3),
 @bCode char(3), 
 @cCode char(3),
 @outDuplicateFound int OUT
AS
BEGIN
SET @outDuplicateFound = (SELECT Top 1 id FROM codeTable 
                          WHERE userId = @userId
                          AND noteType = @noteType
                          AND aCode = @aCode
                          AND bCode = @bCode
                          AND cCode = @cCode 
                          )
-- Now set the duplicate output flag to a 1 or a 0
IF (@outDuplicateFound IS NULL) OR (@outDuplicateFound = '') OR (@outDuplicateFound = 0)
 SET @outDuplicateFound = 0
ELSE 
 SET @outDuplicateFound = 1
END

Ответы [ 6 ]

10 голосов
/ 19 декабря 2008

Я думаю, вам нужно что-то вроде этого для каждого возможно нулевого параметра:

AND (aCode = @aCode OR (aCode IS NULL AND @aCode IS NULL))
7 голосов
/ 19 декабря 2008

Если я правильно понимаю ваш вопрос, то я призываю вас провести небольшое исследование:

SET ANSI_NULLS OFF

Если вы используете эту команду в своей хранимой процедуре, тогда вы можете использовать = NULL в своем сравнении. Посмотрите на следующий пример кода, чтобы увидеть, как это работает.

Declare @Temp Table(Data Int)

Insert Into @Temp Values(1)
Insert Into @Temp Values(NULL)

-- No rows from the following query
select * From @Temp Where Data = NULL

SET ANSI_NULLS OFF

-- This returns the rows where data is null
select * From @Temp Where Data = NULL

SET ANSI_NULLS ON

Всякий раз, когда вы отключаете ANSI_NULLS, рекомендуется как можно скорее установить его обратно в положение ON, поскольку это может повлиять на другие запросы, которые вы выполняете позже. Все команды SET влияют только на текущий сеанс, но в зависимости от вашего приложения это может охватывать несколько запросов, поэтому я предлагаю вам снова включить ANSI NULL сразу после этого запроса.

1 голос
/ 19 декабря 2008

Я думаю, что это должно работать с функцией COALESCE. Попробуйте это:

CREATE PROCEDURE sp_myDuplicateCheck
 @userId int,
 @noteType char(1),
 @aCode char(3),
 @bCode char(3), 
 @cCode char(3),
 @outDuplicateFound int OUT
AS
BEGIN

SET @outDuplicateFound = (SELECT Top 1 id FROM codeTable 
                          WHERE userId = @userId
                          AND noteType = @noteType
                          AND COALESCE(aCode,'NUL') = COALESCE(@aCode,'NUL')
                          AND COALESCE(bCode,'NUL') = COALESCE(@bCode,'NUL')
                          AND COALESCE(cCode,'NUL') = COALESCE(@cCode,'NUL')
                          )
-- Now set the duplicate output flag to a 1 or a 0
IF (@outDuplicateFound IS NULL) OR (@outDuplicateFound = '') OR (@outDuplicateFound = 0)
 SET @outDuplicateFound = 0
ELSE 
 SET @outDuplicateFound = 1
END

Удачи!

Jason

0 голосов
/ 18 июня 2010

SET ANSI_NULLS OFF/On

Таким образом, вы можете сделать colName = null

0 голосов
/ 19 декабря 2008

Попробуйте это:

CREATE PROCEDURE sp_myDuplicateCheck
     @userId int = 0,
     @noteType char(1) = "",
     @aCode char(3) = "", 
     @bCode char(3) = "", 
     @cCode char(3) = "",
     @outDuplicateFound int OUT
    AS
    BEGIN
    SET @outDuplicateFound = (SELECT Top 1 id FROM codeTable 
                              WHERE @userId in (userId ,0)
                              AND @noteType in (noteType,"")
                              AND @aCode in (aCode , "")
                              AND @bCode in (bCode , "")
                              AND @cCode in (cCode ,"")
                              )
    -- Now set the duplicate output flag to a 1 or a 0
    IF (@outDuplicateFound IS NULL) OR (@outDuplicateFound = '') OR (@outDuplicateFound = 0)
     SET @outDuplicateFound = 0
    ELSE 
     SET @outDuplicateFound = 1
    END

Что в основном это делает, так это предоставлять значения по умолчанию для входных параметров в случае нулевого значения, а затем в условии where проверяет только , если значения не равны значениям по умолчанию.

0 голосов
/ 19 декабря 2008

Я бы сначала добавил проверку, чтобы увидеть, все ли параметры были нулевыми во время выполнения, т.е.

IF(COALESCE(@userId, @noteType, @aCode, @bCode, @cCode) IS NULL)
   BEGIN
       -- do something here, log, print, return, etc.
   END

Затем, после того, как вы подтвердите, что пользователь передал что-то в вас, вы можете использовать что-то подобное в предложении WHERE

WHERE userId = COALESCE(@userId, userId)
AND noteType = COALESCE(@noteType, noteType)
AND aCode    = COALESCE(@aCode, aCode)
AND bCode    = COALESCE(@bCode, bCode)
AND cCode    = COALESCE(@cCode, cCode)

РЕДАКТИРОВАТЬ: Возможно, я пропустил намерение, что если параметр был передан как NULL, это означает, что вы явно хотите проверить столбец на NULL. В предложении выше, где предполагается, что нулевой параметр означает «пропустить тест по этому столбцу».

В качестве альтернативы я полагаю, что вы можете использовать исходный запрос и добавить опцию установки ANSI_NULLS во время создания хранимой процедуры. Например,

SET ANSI_NULLS OFF
GO
CREATE PROC sp_myDuplicateCheck....

По сути, это должно позволить вашему коду оценить значение column = null, а значение column = null. Я думаю, что Кален Делани однажды придумал опции ANSI _NULLS и QUOTED_IDENTIFIER как «липкие опции», потому что если они установлены во время создания процедуры, они остаются с процедурой во время выполнения, независимо от того, как соединение в это время установлено.

...