与OR连接的性能

时间:2011-11-29 03:58:17

标签: sql sql-server performance tsql

假设我有两张桌子:

Table A
ProdID | PartNumber | Data...
1      | ABC-a      | "Data A"
2      | (null)     | "Data B"
3      | ABC-c      | "Data C"
...

Table B
ProdID | PartNumber | DataB
(null) | ABC-a      | "Data D"
2      | (null)     | "Data E"
3      | (null)     | "Data F"
(null) | ABC-z      | "Data G"
...

不理想,但无论如何。我想要

ProdID | PartNumber | Data     | DataB...
1      | ABC-a      | "Data A" |  "Data D"
2      | (null)     | "Data B" |  "Data E"
3      | ABC-c      | "Data C" |  "Data F"
(null) | ABC-z      | (null)   |  "Data G"

所以我用

SELECT * 
FROM Table1 T1
     RIGHT JOIN Table2 T2 ON
          T1.ProdID = T2.ProdID OR T1.PartNumber = T2.PartNumber

这正是我想要的,但似乎需要大约100倍于单独的任何一方。作为更复杂查询的一部分,OR需要2分钟,而仅int需要1秒,而nvarchar(50)需要1秒。表“A”有~13k行,表“b”有~35k,整个查询返回〜40k。

查询计划 OR query int nvarchar

我认为这个“表盘管”可能是问题所在。 enter image description here

SQL Server 2008 R2 Express。想法?

5 个答案:

答案 0 :(得分:4)

单独加入每个方式,然后合并结果:

SELECT T1.ProdID, T1.PartNumber, T1.Data, ISNULL(tprodid.DataB, tpartno.DataB) as DataB
FROM Table1 T1
LEFT JOIN Table2 tprodid ON T1.ProdID = tprodid.ProdID
LEFT JOIN Table2 tpartno ON T1.PartNumber = tpartno.PartNumber;

这将使用两个索引,并将表现良好。您可能希望根据自己的喜好调整ISNULL逻辑。

答案 1 :(得分:0)

将查询更改为联合,您应该获得更好的性能:

Select * from Table1 Left Join Table2 On Table1.ProdID = Table2.ProdID 
where Table1.PartNumber is null

union

Select * from Table1 Left Join Table2 On Table1.PartNumber =  Table2.PartNumber
where Table1.ProdId is null

union运算符将消除重复的行。也就是说,两个查询返回的行只返回一次。所以这应该返回与主查询相同的数据。

答案 2 :(得分:0)

你仍然需要OR,但你可以通过FULL JOIN做得更好:

SELECT COALESCE(t1.ProdID,t2.ProdID) ProdID, 
    COALESCE(t1.PartNumber,t2.PartNumber) PartNumber, 
    t1.Data, t2.DataB
FROM TableA t1
FULL JOIN TableB t2 ON t1.ProdID = t2.ProdID OR t1.PartNumber = t2.PartNumber

性能较慢的原因是因为OR强制它与索引不匹配,强制手动比较一个整个表与另一个整个表。如果您仍然遇到FULL JOIN的性能问题,您可以通过添加部件号索引或使用索引提示告诉优化器您的ProdID索引仍然有用来修复它。

答案 3 :(得分:0)

虽然我不太了解MSSQL,但我会尝试至少为您的问题提供解决方案!

对于您可能想要加入的每个列使用LEFT JOIN,然后合并结果如下所示,您应该会获得更好的结果:

SELECT
COALESCE(TA.ProdID, TB2.ProdID) AS ProdID,
COALESCE(TA.PartNumber, TB.PartNumber) AS PartNumber,
TA.Data,
COALESCE(TB.Data2, TB2.Data2) AS Data2
FROM TableA TA
LEFT JOIN TableB TB On TA.ProdID = TB.ProdID
LEFT JOIN TableB TB2 On TA.PartNumber = TB2.PartNumber
GROUP BY ProdId

虽然完全猜测,但我会说它可能仅限于每个连接只使用一个索引,强制它使用全表扫描来执行其中一个列。您可以尝试将两列放入一个索引中,并将该索引用作连接的索引提示,并查看其执行情况。

答案 4 :(得分:0)

我喜欢Jeff Siver关于使用UNION的建议,尽管他建议的查询是错误的。这是一个可能的解决方案:

SELECT *
  FROM Table1 T1
       JOIN Table2 T2 
          ON T1.ProdID = T2.ProdID
UNION
SELECT *
  FROM Table1 T1
       JOIN Table2 T2 
          ON T1.PartNumber = T2.PartNumber
UNION
SELECT NULL, NULL, NULL, *
  FROM Table2 T2
 WHERE NOT EXISTS (
                   SELECT * 
                     FROM Table1 T1
                    WHERE T1.ProdID = T2.ProdID
                  )
       AND NOT EXISTS (
                       SELECT * 
                         FROM Table1 T1
                        WHERE T1.PartNumber = T2.PartNumber
                      );