Использование подзапроса для создания списка для сравнения внутри оператора case - PullRequest
0 голосов
/ 25 сентября 2019

У меня есть таблица ниже:

+----+------+
| id | p_id |
+----+------+
| 1  | null |
| 2  | 1    |
| 3  | 1    |
| 4  | 2    |
| 5  | 2    |
+----+------+

Мне нужно написать запрос для отображения следующего результата:

+----+------+
| id | Type |
+----+------+
| 1  | Root |
| 2  | Inner|
| 3  | Leaf |
| 4  | Leaf |
| 5  | Leaf |
+----+------+

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

Запрос 1:

SELECT id,
CASE WHEN p_id is null THEN 'Root'
     WHEN id not in (select distinct p_id from tree) THEN 'Leaf'
     ELSE 'Inner' 
END AS Type
FROM tree;

+----+------+
| id | Type |
+----+------+
| 1  | Root |
| 2  | Inner|
| 3  | Inner|
| 4  | Inner|
| 5  | Inner|
+----+------+

Запрос 2:

SELECT id,
CASE WHEN p_id is null THEN 'Root'
     WHEN id in (select distinct p_id from tree) THEN 'Inner'
     ELSE 'Leaf' 
END AS Type
FROM tree;

+----+------+
| id | Type |
+----+------+
| 1  | Root |
| 2  | Inner|
| 3  | Leaf |
| 4  | Leaf |
| 5  | Leaf |
+----+------+

Ответы [ 4 ]

0 голосов
/ 25 сентября 2019

Ваш подзапрос (выберите отличный p_id из дерева) выдает следующие результаты (NULL, 1, 2).

Кроме вашей первой строки, нет другого места, где id равен нулю, поэтому вы получаетевнутреннее повсюду.

Значение Где id ни один из них (Null, 1, 2) дают мне внутреннее.

0 голосов
/ 25 сентября 2019
 SELECT
 a2.id,
 CASE
 WHEN a2.p_id is null THEN 'Root'
 WHEN EXISTS
 (
    SELECT DISTINCT a1.p_id FROM tree a1
    WHERE a1.id = a2.id
 )
    THEN 'Inner'
    ELSE 'Leaf'
END
from
tree a2

Примечание: - Я бы предложил EXIST вместо IN:

0 голосов
/ 25 сентября 2019
declare @tbl table (id int, p_id int);
insert into @tbl values
(1, null),
(2, 1),
(3, 1),
(4, 2),
(5, 2);

select distinct t1.*,
       case when t2.id is not null and t3.p_id is not null then 'inner'
       when t2.id is null then 'root'
       when t3.p_id is null then 'leaf' end
from @tbl t1
left join @tbl t2 on t1.p_id = t2.id
left join @tbl t3 on t1.id = t3.p_id

Разница заключается в том, что при использовании not in против набора настроек null оно всегда равно false, поэтому, например, 1 not in (null) вернет false (или специальное значение unknown, если быть точным).

Предлагаю прочитать о трехзначной логике в SQL:)

0 голосов
/ 25 сентября 2019

Все, что вам нужно изменить в запросе 1, - это удалить NULL значения из запроса SELECT DISTINCT.

SELECT id,
CASE WHEN p_id is null THEN 'Root'
     WHEN id not in (select distinct p_id from tree where p_id is NOT NULL) THEN 'Leaf'
     ELSE 'Inner' 
END AS Type
FROM tree;

Это связано с тем, как значения NULL обрабатываются в вашей базе данных на основеANSI_NULLS настроек.

Из документов:

"Указывает ISO-совместимое поведение равно (=) и не равно(<>) Операторы сравнения, когда они используются с нулевыми значениями.

(...)

Когда ANSI_NULLS включен, оператор SELECT, использующий WHERE column_name = NULL, возвращает ноль строк, дажеесли в column_name есть нулевые значения.

(...)

Когда ANSI_NULLS включен, все сравнения с нулевым значением оцениваются как UNKNOWN. "

Я не рекомендую изменять настройки ANSI_NULLS, если вы не знаете, что делаете.Вместо этого я рекомендую изменить ваш запрос и убедиться, что каждый раз, когда вы используете NOT IN, ваш подзапрос никогда не возвращает значения NULL, добавив условие WHERE is NOT NULL в конце.

Вы можетеНайдите больше ответов на эту проблему здесь, на SO, например, здесь (1) и здесь (2) .

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