Грязный оператор SQL - PullRequest
       5

Грязный оператор SQL

1 голос
/ 14 апреля 2009

У меня есть случай, когда я хочу выбрать любую запись в базе данных, имеющую недопустимый идентификатор страны, региона или региона, под недействительным, я имею в виду идентификатор страны, региона или области, которого больше нет в моих таблицах. четыре таблицы: свойства, страны, регионы, районы. Я думал сделать это так:

SELECT * FROM Properties WHERE 
Country_ID NOT IN 
(
SELECT CountryID FROM Countries
)
OR
RegionID NOT IN
(
SELECT RegionID FROM Regions
)
OR
AreaID NOT IN
(
SELECT AreaID FROM Areas
)

Теперь мой запрос прав? и что вы думаете, что я могу сделать и достичь того же результата с лучшей производительностью?!

Ответы [ 6 ]

4 голосов
/ 14 апреля 2009

Ваш запрос на самом деле является оптимальным.

LEFT JOIN, предложенные другими, хуже, так как они выбирают ВСЕ значения и затем отфильтровывают их.

Скорее всего, ваш подзапрос будет оптимизирован для этого:

SELECT  *
FROM    Properties p
WHERE   NOT EXISTS
        (
        SELECT  1
        FROM    Countries i
        WHERE   i.CountryID = p.CountryID
        )
        OR
        NOT EXISTS
        (
        SELECT  1
        FROM    Regions i
        WHERE   i.RegionID = p.RegionID
        )
        OR
        NOT EXISTS
        (
        SELECT  1
        FROM    Areas i
        WHERE   i.AreaID = p.AreaID
        )

, который вы должны использовать.

Этот запрос выбирает не более 1 строки из каждой таблицы и переходит к следующей итерации вправо, когда находит эту строку (т. Е. Если он не находит Country для данного свойства, он даже не потрудится проверить Region).

Опять же, SQL Server достаточно умен, чтобы построить тот же план для этого запроса и ваш первоначальный.

Обновление:

Проверено на 512K строках в каждой таблице.

Все соответствующие ID в таблицах измерений - CLUSTERED PRIMARY KEY, все поля мер в Properties проиндексированы.

Для каждой строки в Property, PropertyID = CountryID = RegionID = AreaID нет фактически отсутствующих строк (наихудший случай с точки зрения времени выполнения).

NOT EXISTS    00:11 (11 seconds)
LEFT JOIN     01:08 (68 seconds)
3 голосов
/ 14 апреля 2009

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

3 голосов
/ 14 апреля 2009

Вы можете переписать его по-другому следующим образом:

SELECT p.* 
FROM Properties p
LEFT JOIN Countries c ON p.Country_ID = c.CountryID
LEFT JOIN Regions r on p.RegionID = r.RegionID
LEFT JOIN Areas a on p.AreaID = a.AreaID
WHERE c.CountryID IS NULL
OR r.RegionID IS NULL
OR a.AreaID IS NULL

Проверьте разницу в производительности (если таковая имеется - должен быть такой же, как NOT IN, - это грязный поиск, особенно по многим элементам, поскольку он должен проверять каждый).

Вы также можете сделать это быстрее, проиндексировав искомую IDS - в каждой основной таблице (Страна, Регион, Область) они должны быть кластеризованы первичными ключами.

1 голос
/ 14 апреля 2009

Что ж, вы могли бы попробовать такие вещи, как UNION (вместо OR) - но я ожидаю , что оптимизатор уже делает все возможное, учитывая доступную информацию :

SELECT  * FROM  Properties
WHERE   NOT EXISTS (SELECT 1 FROM Areas WHERE Areas.AreaID = Properties.AreaID)
UNION
SELECT  * FROM  Properties
WHERE   NOT EXISTS (SELECT 1 FROM Regions WHERE Regions.RegionID = Properties.RegionID)
UNION
SELECT  * FROM  Properties
WHERE   NOT EXISTS (SELECT 1 FROM Countries WHERE Countries.CountryID = Properties.CountryID)
0 голосов
/ 14 апреля 2009

Если вы не получаете данные строк из стран / регионов / областей, вы можете попробовать использовать «существует»:

SELECT Properties.*
FROM Properties
WHERE Properties.CountryID IS NOT NULL AND NOT EXISTS (SELECT 1 FROM Countries WHERE Countries.CountryID = Properties.CountryID)
OR Properties.RegionID IS NOT NULL AND NOT EXISTS (SELECT 1 FROM Regions WHERE Regions.RegionID = Properties.RegionID)
OR Properties.AreaID IS NOT NULL AND NOT EXISTS (SELECT 1 FROM Areas WHERE Areas.AreaID = Properties.AreaID)

Это, как правило, намекает на использование индексов pkey стран и других для проверки существования ... но зависит ли это улучшение от вашей статистики данных, вам просто нужно подключить его к анализатору запросов и попробовать. *

0 голосов
/ 14 апреля 2009

Подзапросы в условиях могут быть довольно неэффективными. Вместо этого вы можете делать левые соединения с соответствующими таблицами. Там, где нет подходящей записи, вы получаете нулевое значение. Вы можете использовать это в условии, чтобы выбрать только те записи, в которых отсутствует соответствующая запись:

select p.*
from Properties p
left join Countries c on c.CountryID = p.Country_ID
left join Regions r on r.RegionID = p.RegionID
left join Areas a on a.AreaID = p.AreaID
where c.CountryID is null or r.RegionID is null or a.AreaID is null
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...