Входные значения хранимой процедуры T-SQL NULL приводят к сбою оператора select

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

Звучит совершенно просто, но SP терпит неудачу. Проблема в том, что некоторые параметры, переданные в SP, могут иметь нулевое значение, и поэтому sql должен читать «равно нулю», а не «= null». Я пробовал isnull(), операторы case, объединение() и динамический 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

person Community    schedule 19.12.2008    source источник


Ответы (6)


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

AND (aCode = @aCode OR (aCode IS NULL AND @aCode IS NULL))
person n8wrl    schedule 19.12.2008
comment
Большое спасибо за вашу помощь Мердад. Сейчас это кажется таким очевидным, но это действительно поставило меня в тупик. - person ; 19.12.2008
comment
Мне нравится ISNULL(@aCode, aCode) = aCode. - person recursive; 22.12.2008
comment
Да, это то же самое, что обертывание с помощью IsNull, которое короче и проще в обслуживании. - person Ian Varley; 22.12.2008
comment
Но это не сработает - NULL != NULL. Все, что вы делаете, это игнорировать параметр, если он равен NULL, а это не то, чего хочет OP. - person n8wrl; 18.08.2011

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

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

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

person George Mastros    schedule 19.12.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

Удачи!

Джейсон

person Jason Stevenson    schedule 19.12.2008
comment
Это должно работать независимо от того, являются ли ваши входные значения NULL или если ваши столбцы содержат NULL. - person Jason Stevenson; 19.12.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 проверяется только, если значения не равны значениям по умолчанию.

person Learning    schedule 19.12.2008
comment
Проголосовал за, это было самым полезным в моем случае. Это позволяет мне вообще не передавать параметр, когда он мне не нужен. С# ноль != SQL ноль. - person KristianB; 01.09.2011

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

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)

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

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

SET ANSI_NULLS OFF
GO
CREATE PROC sp_myDuplicateCheck....

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

person Eric Sabine    schedule 19.12.2008

SET ANSI_NULLS OFF/On

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

person Ankur Jain    schedule 18.06.2010