Для первого запроса вы хотите строки, которые отвечают обоим из следующих критериев:
-
Name
в строке появляется в таблице в той же строке, в которой LeftId
и RightId
равны NULL.
-
Name
в строке появляется в таблице в той же строке, где хотя бы один из LeftId
и RightId
равен , а не NULL.
Ну, # 1 делается:
SELECT Name FROM Tbl WHERE (LeftId IS NULL) AND (RightId IS NULL)
И № 2 выполняется:
SELECT Name FROM Tbl WHERE (LeftId IS NOT NULL) OR (RightId IS NOT NULL)
Вы можете пересечь их , чтобы увидеть, какие Name
появятся в обоих списках:
SELECT Name FROM Tbl WHERE (LeftId IS NULL) AND (RightId IS NULL)
INTERSECT
SELECT Name FROM Tbl WHERE (LeftId IS NOT NULL) OR (RightId IS NOT NULL)
Что возвращает:
Name
----
Cat
Cow
Но вам нужны LeftId
и RightId
, и вам все равно, какие, поэтому, я думаю, мы сгруппируемся по Имени:
SELECT Name, MIN(LeftId) AS LeftId, MIN(RightId) AS RightId
FROM Tbl WHERE Tbl.Name IN (
SELECT Name FROM Tbl WHERE (LeftId IS NULL) AND (RightId IS NULL)
INTERSECT
SELECT Name FROM Tbl WHERE (LeftId IS NOT NULL) OR (RightId IS NOT NULL)
)
GROUP BY Name
Который возвращает
Name LeftId RightId
---- ------ -------
Cat 1
Cow 6 7
lc уже предложил использовать COALESE, чтобы превратить эти два идентификатора в один. Так как насчет этого:
SELECT Name, COALESCE(MIN(LeftId),MIN(RightId)) AS Id
FROM Tbl WHERE Tbl.Name IN (
SELECT Name FROM Tbl WHERE (LeftId IS NULL) AND (RightId IS NULL)
INTERSECT
SELECT Name FROM Tbl WHERE (LeftId IS NOT NULL) OR (RightId IS NOT NULL)
)
GROUP BY Name
Что возвращает:
Name Id
---- --
Cat 1
Cow 6
Для второго запроса вы хотите строки, которые соответствуют следующим критериям:
-
Name
появляется только в строках, которые не имеют LeftId
и RightId
Я не могу придумать, как выполнить такой запрос в SQL с помощью самоссылки в одном наборе критериев, поэтому я разбью его на два критерия. Оба должны быть выполнены, чтобы быть приемлемыми:
-
Name
появляется в строках, которые не имеют LeftId
и RightId
-
Name
действительно не появляются в строках, которые имеют LeftId
или RightId
Выполнение # 1 просто:
SELECT Name FROM Tbl WHERE (LeftId IS NULL) AND (RightId IS NULL)
Но № 2 сложно. Конечно, выполнение противоположного # 2 («все Name
, которые появляются в строках с LeftId
или RightId
) такое же, как и раньше:
SELECT Name FROM Tbl WHERE (LeftId IS NOT NULL) OR (RightId IS NOT NULL)
Теперь наступает хитрый момент - мы хотим, чтобы все строки подчинялись # 1, но не подчинялись противоположности # 2. Вот где полезно EXCEPT :
SELECT Name FROM Tbl WHERE (LeftId IS NULL) AND (RightId IS NULL)
EXCEPT
SELECT Name FROM Tbl WHERE (LeftId IS NOT NULL) OR (RightId IS NOT NULL)
Что возвращает:
Name
----
Bird
Что мы и хотели!