一个凌乱的SQL语句

时间:2009-04-14 11:39:42

标签: sql database sql-server-2005 tsql

我有一个案例,我想选择任何无效的国家,地区或区域ID的数据库条目,无效,我的意思是我的表格中不再存在的国家或地区或区域的ID,我有四个表:属性,国家,地区,区域。 我想这样做:

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 个答案:

答案 0 :(得分:4)

您的查询实际上是最佳的。

其他人提出的

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行,并在找到该行时跳转到下一个迭代(即如果找不到给定Property的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)

答案 1 :(得分:3)

您可以按以下方式重写它:

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来加快速度 - 在每个主表(国家,地区,区域)中它们应该是群集主键。

答案 2 :(得分:3)

由于这似乎是清理sql,这应该没问题。但是如何使用foreign keys以便下次不打扰你?

答案 3 :(得分:1)

好吧,您可以尝试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)

答案 4 :(得分:0)

条件中的子查询可能效率很低。相反,您可以对相关表执行左连接。如果没有匹配的记录,则会获得空值。您可以在条件中使用它来仅选择缺少匹配记录的记录:

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

答案 5 :(得分:0)

如果您没有从国家/地区/地区获取行数据,可以尝试使用“存在”:

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)

这通常暗示使用countries等人的pkey索引进行存在检查......但是否这是一项改进取决于您的数据统计数据,您只需将其插入查询分析器并尝试它。