#sql #query-optimization #subquery
#sql #оптимизация запросов #подзапрос
Вопрос:
У меня есть две таблицы:
TableA: (a temporary table)
ItemId (int)
TableB:
ItemId (int), ParentID (int)
Я хочу получить все элементы в таблице A, где родительский идентификатор любого из элементов в таблице A не существует в качестве идентификатора ItemId. (т. Е. я хочу получить корень элементов в TableA)
Этот запрос выполняет то, что я хочу:
SELECT a.ItemID
FROM TableA a
INNER JOIN TableB b ON a.ItemId = b.ItemID
WHERE b.ParentID NOT IN ( SELECT * from TableA )
как и этот:
SELECT b.ItemID
FROM TableB b
WHERE b.ItemID IN ( SELECT * FROM TableA)
AND b.ParentID NOT IN ( SELECT * FROM TableA )
Я не удовлетворен ни одним из запросов, особенно из-за использования NOT IN /В. Есть ли способ сделать это без них? Возможно, более чистый способ, который не требует подзапросов?
Примеры данных:
Table A
-------
2
3
5
6
Table B
--------
1 | NULL
2 | 1
3 | 1
4 | 3
5 | 3
6 | 3
Желаемый результат:
2
3
Спасибо
Комментарии:
1. Есть ли причина, по которой вы против этих подзапросов? В данном случае они должны выполняться быстрее, чем a
LEFT JOIN
.2. Ну, мне определенно не нравится повторять с помощью subqueries…it просто кажется странным это делать. Сказав это, я просто предположил, что они были плохими, потому что … это то, что … я … читал … везде….
3. Возможно, вы можете объяснить, почему это будет быстрее в этом экземпляре, а не в других?
4. Для подзапроса использование
IN
илиEXISTS
вычисляет подзапрос один раз и приводит к короткому замыканию. ДляLEFT JOIN
вы возвращаете весь результирующий набор вJOIN
отредактированной таблице, а затем отфильтровываете.IN
может быть медленнее, если вы используете определенные значения или набор, но для подзапроса план выполнения обычно такой же, как дляEXISTS
подзапроса, который замыкается.5. @Swati — это также короткое замыкание, но
NOT IN
может быть опасным из-за того, как оно обрабатываетNULL
Ответ №1:
Без подзапросов:
SELECT ItemID
FROM TableA
INTERSECT
SELECT b.ItemID
FROM TableB AS b
LEFT OUTER JOIN TableA AS a
ON b.ParentID = a.ItemID
WHERE a.ItemID IS NULL;
…но обоснован ли ваш страх перед подзапросами? 🙂 Я бы счел этот эквивалентный запрос более легким для чтения и понимания:
SELECT ItemID
FROM TableA
INTERSECT
SELECT ItemID
FROM TableB
WHERE NOT EXISTS (
SELECT *
FROM TableA AS a
WHERE a.ItemID = TableB.ParentID
);
Комментарии:
1. Спасибо 🙂 Я думаю, это иррациональный страх 🙂
2. Единственное, чего я боюсь
in (subquery)
, — это ограничения количества результатов, которые вы можете получить вin
предложении. Фактически, недавно я столкнулся с этой ошибкой, в которой говорилось, что в моемin
предложении sql server 2008 r2 Express может быть не более 2100 идентификаторов.3. @&oku_da_master: звучит так, будто на самом деле вы используете не
IN ( <table expression, could be a subquery&&t; )
, а скорееIN (<comma separated list&&t;)
, или, возможноIN (<table constructor&&t;)
, у каждого анализатора есть свои ограничения!
Ответ №2:
Взгляните на Выберите все строки из одной таблицы, которые не существуют в другой таблице, чтобы увидеть 5 различных способов выполнения такого рода запросов с помощью
НЕ В
НЕ СУЩЕСТВУЕТ
ЛЕВОГО и ПРАВОГО СОЕДИНЕНИЯ
OUTER APPLY (2005 )
ЗА ИСКЛЮЧЕНИЕМ (2005 )
Вот скрипт, который вы можете запустить
CREATE TABLE #TableA( ItemId int)
INSERT #TableA values(1)
INSERT #TableA values(2)
INSERT #TableA values(3)
INSERT #TableA values(4)
INSERT #TableA values(5)
INSERT #TableA values(6)
CREATE TABLE #TableB( ItemId int, ParentID int)
INSERT #TableB values(1,1)
INSERT #TableB values(2,2)
INSERT #TableB values(4,3)
INSERT #TableB values(5,4)
это сделает это для родительского
SELECT a.ItemID
FROM #TableA a
LEFT JOIN #TableB b ON a.ItemId = b.ParentID
WHERE b.ItemID IS NULL
SELECT a.ItemID
FROM #TableA a
WHERE NOT EXISTS (SELECT 1 FROM #TableB b WHERE a.ItemId = b.ParentID)
Вывод
ItemID
5
6
Комментарии:
1. Это дает мне nullset. У ВСЕХ элементов есть родительские элементы, поэтому я не уверен, что это когда-нибудь будет null.
2. У меня ничего из этого не работает. Я не верю, что связанный вопрос применим — мне нужны записи, которые существуют в обеих таблицах, НО где родительский идентификатор (в TableB) не является идентификатором элемента в TableA
3. Для меня это недопустимый набор данных. ВСЕ идентификаторы элементов в таблице A также существуют в таблице B в качестве идентификаторов элементов. (Таблица A является подмножеством таблицы B, если хотите). TableB также хранит родительские идентификаторы, все из которых также являются допустимыми идентификаторами элементов.
4. Обновлено, я думаю, я неправильно понял
5. -1 Используя данные операционной системы, ваши запросы не дают желаемого результата операционной системы.
Ответ №3:
Вы можете использовать внешние соединения. Что-то вроде этого:
SELECT a.ItemID
FROM TableA a
INNER JOIN TableB b ON a.ItemId = b.ItemID
LEFT JOIN TableB parentB on a.ItemID = parentB.ParentID
WHERE parentB.ParentID IS NULL
Ответ №4:
Похоже, что ваши таблицы A и B хранят древовидную структуру. Я бы интерпретировал таблицу A как «Узлы» (хранящие элементы дерева), а таблицу B как «Ребра» (связывающие узел с его родительским элементом). Вариант внутреннего соединения очень элегантен, поскольку он охватывает все случаи «нет ребра к родительскому элементу», «ребро к PrantID null» и «ребро к несуществующему родительскому элементу» сразу. приветствия