powered by simpleCommunicator - 2.0.59     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Тонкости использования предиката '=' с NULL-значениями в SQL
11 сообщений из 11, страница 1 из 1
Тонкости использования предиката '=' с NULL-значениями в SQL
    #32015292
Alexf
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Hi, All!

В таблице имеется целочисленное поле (NS), которое допускает логический нуль (NULL).
Нужно выполнять поиск по этому полю на предмет совпадения со значением переменной (@lNS_ID).
В случае, когда параметр = NULL, требуется, чтобы возвращались записи со значением поля = NULL.

Вот определение таблицы:

CREATE TABLE dbo.ClassInf(
ID bigint,
Name varchar(255) NOT NULL,
NS bigint NULL DEFAULT NULL,
Parent bigint NULL DEFAULT NULL,

CONSTRAINT PK_ClassInf PRIMARY KEY NONCLUSTERED (ID),
CONSTRAINT FK_ClassInf_NS FOREIGN KEY (NS) REFERENCES dbo.Namespace,
CONSTRAINT FK_ClassInf_Parent FOREIGN KEY (Parent) REFERENCES dbo.ClassInf,
CONSTRAINT CKC_ClassInf_Unique UNIQUE CLUSTERED (Name, NS),
CONSTRAINT CKC_ClassInf_Same CHECK( NULLIF(ID,Parent) is not NULL )

) ON [PRIMARY]
GO

Пусть в dbo.ClassInf будет такое содержимое:

ID Name NS Parent
-------------------------------
1; n11; 15; NULL
2; n22; NULL; NULL
3; n11; 10; 1
4; n22; 10; 1
5; n55; NULL; NULL

Так выполняется запрос (значение @lNS_ID задано ранее и может быть NULL):

declare @lParent_ID bigint

select @lParent_ID = a.Parent from ClassInf a
where a.Name = @vcName AND a.NS = @lNS_ID

Проблема в том, что предикат '=' возвращает NULL в случае, когда один из аргументов NULL и,
в результате, не находятся записи, где поле NS = NULL.

Например, пусть @vcName = 'n22', а @lNS_ID = 10 - всё работает правильно.
Найдётся запись: 4; n22; 10; 1
Если же @vcName = 'n22', а @lNS_ID = NULL, то надо, чтобы нашлась: 2; n22; NULL; NULL,
но не найдётся ничего!

Так как я не хочу зависеть от режима ANSI_NULLS (set ANSI_NULLS ON/OFF), то пришлось написать
и использовать функцию:


CREATE FUNCTION dbo.EQ3( @l1 bigint, @l2 bigint )
RETURNS int
AS
begin
if( @l1 is NULL )
begin
if( @l2 is NULL ) return 1 else return 0
end
else
begin
if( @l2 is NULL ) return 0 else if( @l1 = @l2 ) return 1
else return 0
end

return 0
end
GO

Исправить запрос:
select @lParent_ID = a.Parent from ClassInf a
where a.Name = @vcName AND dbo.EQ3(a.NS, @lNS_ID) = 1

---------------------------------------
У кого какие идеи - как это ещё можно реализовать, не используя составного предиката вида:
where a.Name = @vcName AND (a.NS = @lNS_ID OR ((a.NS is null) and (@lNS_ID is null))) ?


Thanks...
...
Рейтинг: 0 / 0
Тонкости использования предиката '=' с NULL-значениями в SQL
    #32015302
Slava
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
declare @tmp table(s varchar(10))
insert into @tmp values('1')
insert into @tmp values(null)
select * from @tmp where s is null

Что SET ANSI_NULLS ON, что SET ANSI_NULLS OFF работает правильно.
Ведь Вы же сами в этой функции is null пишите, что там set не работает?
В запросах всегда надо is null писать
...
Рейтинг: 0 / 0
Тонкости использования предиката '=' с NULL-значениями в SQL
    #32015311
qu-qu
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
BIGINT, говорите?
ну-ну... не иначе как каждый атом в Солнечной системе собрались пронумеровать и подписать к нему - в какую молекулу он входит, а та - в какой белок... а тот - в какую клетку... etc.
(флаг в руки, конечно) может вот это вам поможет:


SET NOCOUNT ON
SET ANSI_DEFAULTS ON

CREATE TABLE #join_source (parent_db char(5), parent_id int, firstname varchar(15))
CREATE TABLE #join_reference (child_db char(5), child_id int, surname varchar(15))

INSERT INTO #join_source SELECT 'MAIN1',1,'Андрей'
INSERT INTO #join_source SELECT 'MAIN1',2,'Анатолий'
INSERT INTO #join_source SELECT 'MAIN1',NULL,'Владимир'
INSERT INTO #join_source SELECT 'MAIN1',4,'Олег'

INSERT INTO #join_reference SELECT 'MAIN1',1,'Ларин'
INSERT INTO #join_reference SELECT 'MAIN1',2,'Дукалис'
INSERT INTO #join_reference SELECT 'MAIN1',NULL,'Казанцев'
INSERT INTO #join_reference SELECT 'MAIN1',4,'Половцев'

DECLARE @unreal_value int
SELECT @unreal_value = 0x7FFFFFFF

SELECT s.firstname, r.surname
FROM #join_source s, #join_reference r
WHERE ISNULL(s.parent_id,@unreal_value) = ISNULL(r.child_id,@unreal_value)
AND s.parent_db = r.child_db
...
Рейтинг: 0 / 0
Тонкости использования предиката '=' с NULL-значениями в SQL
    #32015312
Фотография SergSuper
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
1. допустим есть некое значение, которое в NS заведомо не будет(допустим -1)

declare @lParent_ID bigint

select @lParent_ID = a.Parent from ClassInf a
where a.Name = @vcName AND coalesce(a.NS,-1) = coalesce(@lNS_ID,-1)

2.
declare @lParent_ID bigint

select @lParent_ID =
(select a.Parent from ClassInf a
where a.Name = @vcName AND a.NS = @lNS_ID
union
select a.Parent from ClassInf a
where a.Name = @vcName and @lNS_ID is null and a.NS is null)


Наверное есть еще варианты, но в любом случае получается некрасиво
...
Рейтинг: 0 / 0
Тонкости использования предиката '=' с NULL-значениями в SQL
    #32015315
Slava
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
А почему бы перед всем сравнением set ansi_nulls не сказать?
...
Рейтинг: 0 / 0
Тонкости использования предиката '=' с NULL-значениями в SQL
    #32015346
Alexf
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Всем спасибо!

Похоже, что наилучший вариант - использовать функцию: CREATE FUNCTION dbo.EQ3( @l1 bigint, @l2 bigint ).

А насчёт bigint - тут важно не только число строк в таблице, но и частота обновления, ведь PK всё время увеличивается на 1 и когда-то произойдёт overwrap. PK снова станет 0, 1,...
...
Рейтинг: 0 / 0
Тонкости использования предиката '=' с NULL-значениями в SQL
    #32015355
Фотография SergSuper
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
используя функцию Вы должны осознавать, что отказываетесь от индексов. Если время пока не критично то это лучший вариант.

а насчет bigint поддерживаю - нечегов в наше-то время экономить на байтах!(хотя и обычного Int наверняка вам хватит - если допустим нечто обновляется 10 раз в секунду, то у вас лет 300 есть. Или надеетесь что Ваша база проживёт дольше?)
...
Рейтинг: 0 / 0
Тонкости использования предиката '=' с NULL-значениями в SQL
    #32015373
Slava
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Вы меня конечно профи извините за точто я в вашу умную дисскуссию лезу, но почему не сказать

if null = null
set ansi_nulls off
else
set ansi_nulls off

а потом

set ansi_nulls on?
или isnull(Exp, 1) не использовать?
...
Рейтинг: 0 / 0
Тонкости использования предиката '=' с NULL-значениями в SQL
    #32015448
Slava
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Я там с if напутал сгореча, но думаю придумать как можно.
...
Рейтинг: 0 / 0
Тонкости использования предиката '=' с NULL-значениями в SQL
    #32015525
Фотография Garya
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не знаю, попал в струю или нет, но приведу прием, который использую сам.
Задача: выбрать значения из таблиц, у которых некие поля имеют одинаковые значения, либо в первой из этих таблиц поле содержит Null.
Решение:
select * from Table1 T1, Table2 T2
where IsNull(T1.SomeFld,T2.SomeFld)=T2.SomeFld
Замечание. Прием работает для случаев, когда Table2 в поле SomeFld не содержит значений Null. В тех задачах, которыми я занимаюсь, обычно всегда это условие выполняется, поскольку речь идет о связке двух таблиц один-ко-многим, когда SomeFld является ключевым полем на стороне "один" (Table2) и одновременно Allow Nulls на стороне "многие" (Table1).
...
Рейтинг: 0 / 0
Тонкости использования предиката '=' с NULL-значениями в SQL
    #32015657
Если значение -1 не встретится в выборке ( а так оно, скорее всего, и есть в Вашей задаче), то ниже приведённое решение будет использовать индексы.

declare @lParent_ID bigint

select
top 1
@lParent_ID = a.Parent
from
ClassInf a
where
(a.Name = @vcName)
AND (ISNULL(a.NS, -1) = ISNULL(@lNS_ID, -1))

Другое дело, что есть и ещё более правильный вариант

declare @lParent_ID bigint

select
top 1
@lParent_ID = a.Parent
from
ClassInf a
where
(a.Name = @vcName)
AND (
((a.NS IS NOT NULL) AND (@lNS_ID IS NOT NULL) AND (a.NS = @lNS_ID))
OR
((a.NS IS NULL) AND (@lNS_ID IS NULL))
)
...
Рейтинг: 0 / 0
11 сообщений из 11, страница 1 из 1
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Тонкости использования предиката '=' с NULL-значениями в SQL
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]