0

我有一个存储过程最近出现了一些问题,我最终将其缩小到 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

这个查询实际上永远不会完成运行。对于我们拥有的一些更大的工作,它实际上会超时。

如果我将选择中的 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

数据看起来与我几乎完全相同(尽管总共有数千行,所以我可能是错的),并且运行得非常快。

任何想法表示赞赏..

4

2 回答 2

2
  • 性能

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

  • 正确性

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

如果两个语句:

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

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

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

于 2013-02-22T23:44:01.143 回答
1

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

就性能而言,优化器是一种启发式方法——它有时会使用非常糟糕的查询计划,即使是最小的更改有时也会导致完全不同的查询计划。您最好的机会是比较查询计划并查看导致差异的原因。

于 2013-02-23T00:58:04.357 回答