SELECT(SUBQUERY)出现奇怪的性能问题

时间:2013-02-22 22:12:09

标签: sql sql-server

我有一个存储过程,最近遇到了一些问题,最后我把它缩小到1 SELECT。问题是我无法弄清楚究竟发生了什么事情来破坏这一个查询的性能。我重写了它,但我不确定重写是完全相同的数据。

原始查询:

SELECT 
    @userId, p.job, p.charge_code, p.code
, (SELECT SUM(b.total) FROM dbo.[backorder w/total] b WHERE b.ponumber = p.ponumber AND b.code = p.code)
, ISNULL(jm.markup, 0)
, (SELECT SUM(b.TOTAL_TAX) FROM dbo.[backorder w/total] b WHERE b.ponumber = p.ponumber AND b.code = p.code)
, p.ponumber
, p.billable
, p.[date]
FROM dbo.PO p
INNER JOIN dbo.JobCostFilter jcf 
    ON p.job = jcf.jobno AND p.charge_code = jcf.chargecode AND jcf.userno = @userId
LEFT JOIN dbo.JobMarkup jm
    ON jm.jobno = p.job
    AND jm.code = p.code
LEFT JOIN dbo.[Working Codes] wc
    ON p.code = wc.code
INNER JOIN dbo.JOBFILE j
    ON j.JOB_NO = p.job
WHERE (wc.brcode <> 4 OR @BmtDb = 0)
GROUP BY p.job, p.charge_code, p.code, p.ponumber, p.billable, p.[date], jm.markup, wc.brcode

此查询实际上永远不会完成运行。它实际上超过了我们的一些更大的工作。

如果我将select中的2个子查询更改为像连接一样读取:

SELECT
    @userid, p.job, p.charge_code, p.code
    , (SELECT SUM(b.TOTAL))
    , ISNULL(jm.markup, 0)
    , (SELECT SUM(b.TOTAL_TAX))
    , p.ponumber, p.billable, p.[date]
FROM dbo.PO p
INNER JOIN dbo.JobCostFilter jcf 
    ON p.job = jcf.jobno AND p.charge_code = jcf.chargecode AND jcf.userno = 11190030
INNER JOIN [BACKORDER W/TOTAL] b
    ON P.PONUMBER = b.ponumber AND P.code = b.code
LEFT JOIN dbo.JobMarkup jm
    ON jm.jobno = p.job
    AND jm.code = p.code
LEFT JOIN dbo.[Working Codes] wc
    ON p.code = wc.code
INNER JOIN dbo.JOBFILE j
    ON j.JOB_NO = p.job
WHERE (wc.brcode <> 4 OR @BmtDb = 0)
GROUP BY p.job, p.charge_code, p.code, p.ponumber, p.billable, p.[date], jm.markup, wc.brcode

数据看起来与我几乎完全相同(尽管整体上有数千行,所以我可能错了),并且它运行得非常快。

任何想法都赞赏..

2 个答案:

答案 0 :(得分:2)

  • 服务表现

在第二个查询中,您的逻辑读取次数较少,因为表[BACKORDER W / TOTAL]仅扫描一次。在第一个查询中,两个独立的子查询以独立方式处理,并且表被扫描两次,尽管两个子查询具有相同的谓词。

  • 正确性

如果要检查两个查询是否返回相同的结果集,可以使用EXCEPT运算符:

如果两个陈述:

第一个SELECT查询... 除了 第二个SELECT查询...

第二个SELECT查询.. 除了 第一个SELECT查询...

返回一个空集,结果集是相同的。

答案 1 :(得分:1)

就正确性而言,您在第二个查询中inner join [BACKORDER W/TOTAL],因此如果第一个查询在子查询中具有Null值,则第二个查询中的这些行将丢失查询。

对于性能,优化器是一种启发式方法 - 它有时会使用非常糟糕的查询计划,即使是最小的更改也有时会导致完全不同的查询计划。您最好的机会是比较查询计划,看看是什么导致了差异。