【问题标题】:My subquery is slowing down the process. Any optimization method?我的子查询减慢了这个过程。有什么优化方法吗?
【发布时间】:2017-06-30 16:12:11
【问题描述】:

我用子查询写过代码,但是太慢了。我正在寻找可以优化此查询过程的解决方案。它旨在返回 6 列 - PO 编号、PO 行编号、每个 PO 行的金额、每个 PO 编号的 PO 行计数、每个 PO 编号的行金额总和、按行金额对每个 PO 编号的分类。下面是我写的示例表和实际查询。非常感谢您的帮助!

|PO NUMBER |PO LINE|LINE AMOUNT|TOTAL PO LINES|TOTAL PO AMOUNT|TOTAL PO GROUP|
|1721574   |   1   |   10.76   |      1       |     10.76     |   $0-100     |
|1722154   |   1   |   30.00   |      1       |     30.00     |   $0-100     |
|1723538   |   1   |   15.97   |      4       |     65.63     |   $0-100     |
|1723538   |   2   |   11.23   |      4       |     65.63     |   $0-100     |
|1723538   |   3   |   15.97   |      4       |     65.63     |   $0-100     |
|1723538   |   4   |   22.46   |      4       |     65.63     |   $0-100     |
|1723877   |   1   |   15.70   |      1       |     15.70     |   $0-100     |

查询

select ph.ponumber, 
       pl.poline, 
       pl.polinebasemerchamount, 
       (select count(pl2.poline) 
               from dbo.polineflat as pl2 
               inner join dbo.poheader as ph2 
               on ph2.pokey = pl2.pokey 
               where ph2.pokey = ph.pokey 
               group by ph2.ponumber), 
       (select sum(pl2.polinebasemerchamount) 
               from dbo.polineflat as pl2 
               inner join dbo.poheader as ph2 
               on ph2.pokey = pl2.pokey 
               where ph2.pokey = ph.pokey 
               group by ph2.ponumber), 
       (select case 
                   when sum(pl2.PoLineBaseMerchAmount) between 0 and 100 then '$0-100'
                   when sum(pl2.polinebasemerchamount) between 101 and 500 then '$101-500'
                   when sum(pl2.polinebasemerchamount) between 501 and 1000 then '$501-1000'
                   else '1000+' end
               from dbo.polineflat as pl2 
               inner join dbo.poheader as ph2 
               on ph2.pokey = pl2.pokey 
               where ph2.pokey = ph.pokey 
               group by ph2.ponumber) 
from dbo.poheader as ph
inner join dbo.polineflat as pl on ph.pokey = pl.pokey

【问题讨论】:

  • 看起来你可能正在计算三个不同的总和三到四次..
  • 查询显示不完整
  • 是的,计算四次总和会要了你的命。我的解决方案计算一次,然后有一个执行逻辑的父查询。
  • 您使用的是什么版本的 SQL Server?
  • 大家好,我使用的是最新的 Sql server 2016 Ver。 13.

标签: sql sql-server performance subquery


【解决方案1】:

我会计算一次总和,然后在父查询中执行您的案例:

SELECT ponumber, poline, polinebasemerchamount, polineCount, polineFlatSum,
    CASE 
        WHEN polineFlatSum between 0 and 100 then '$0-100'
        WHEN polineFlatSum between 101 and 500 then '$101-500'
        WHEN polineFlatSum  between 501 and 1000 then '$501-1000'
        ELSE '1000+' 
    END AS polineFlatSumString
from
(
    select ph.ponumber, 
           pl.poline, 
           pl.polinebasemerchamount, 
           (select count(pl2.poline) 
                   from dbo.polineflat as pl2 
                   inner join dbo.poheader as ph2 
                   on ph2.pokey = pl2.pokey 
                   where ph2.pokey = ph.pokey 
                   group by ph2.ponumber) AS polineCount, 
           (select sum(pl2.PoLineBaseMerchAmount) 
                   from dbo.polineflat as pl2 
                   inner join dbo.poheader as ph2 
                   on ph2.pokey = pl2.pokey 
                   where ph2.pokey = ph.pokey 
                   group by ph2.ponumber) AS polineFlatSum,        
    from dbo.poheader as ph
        inner join dbo.polineflat as pl on ph.pokey = pl.pokey
) T

【讨论】:

    【解决方案2】:

    三个sub-queries 可以使用Cross Apply 组合成一个查询。使用Cross Apply 而非相关子查询的这一优势可以在select 列表中返回多于一列

    SELECT ph.ponumber, 
           pl.poline, 
           pl.polinebasemerchamount, 
           oa.poline_count, 
           oa.polinebasemerchamount_sum, 
           CASE 
             WHEN oa.polinebasemerchamount_sum BETWEEN 0 AND 100 THEN '$0-100' 
             WHEN oa.polinebasemerchamount_sum BETWEEN 101 AND 500 THEN '$101-500' 
             WHEN oa.polinebasemerchamount_sum BETWEEN 501 AND 1000 THEN '$501-1000' 
             ELSE '1000+' 
           END AS Range
    FROM   dbo.poheader AS ph 
           CROSS Apply (SELECT Count(pl2.poline)              AS poline_count, 
                               Sum(pl2.polinebasemerchamount) AS polinebasemerchamount_sum                               
                        FROM   dbo.polineflat AS pl2 
                        WHERE  pl2.pokey = ph.pokey) oa 
    

    注意:我已经删除了子查询中的Group By,因为它应该没用,否则它会在原始查询中引发错误

    更新:要进一步改进查询,请创建以下Non clustered indexes

    --polineflat table
    CREATE NONCLUSTERED INDEX IX_polineflat_pokey
        ON dbo.polineflat (pokey) Include (poline,polinebasemerchamount);  
    
    --poheader table
    CREATE NONCLUSTERED INDEX IX_poheader_pokey
        ON dbo.poheader (pokey) Include (ponumber,poline,polinebasemerchamount); 
    

    【讨论】:

    • 非常感谢 Prdp。你的解决方案确实有效,我花了大约 4 分 40 秒才得到结果,比我原来的要快得多......仍然有点好奇是否有更快的方法来做到这一点......
    • @SanK - 我对代码做了一些小改动并添加了 Index.在您的数据库中运行此查询并在此处发布执行计划 brentozar.com/pastetheplan 并分享它。还要提到两个表的索引详细信息
    • 哇,与您以前的解决方案相比,即使没有索引,这也快得惊人。非常感谢 Prdp!
    • @SanK Gald 它帮助了你。如果你对这个答案感到满意,你可以将此标记为答案
    【解决方案3】:

    试试这个...

    select ph2.ponumber, 
           pl2.poline, 
           pl2.polinebasemerchamount, 
           count(pl2.poline) ,
           sum(pl2.polinebasemerchamount) ,
    case  when sum(pl2.PoLineBaseMerchAmount) between 0 and 100 then '$0-100'
          when sum(pl2.polinebasemerchamount) between 101 and 500 then '$101-500'
          when sum(pl2.polinebasemerchamount) between 501 and 1000 then '$501-1000'
               else '1000+' end
    
    from dbo.poheader as ph2
    inner join dbo.polineflat as pl2
    on ph.pokey = pl.pokey
    inner join dbo.polineflat as pl2 
    on ph.pokey = pl2.pokey
    group by  ph2.ponumber, 
           pl2.poline, 
           pl2.polinebasemerchamount
    

    【讨论】:

    • 谢谢,我刚试过这个。显然,这个查询返回值非常快,但不幸的是,它以与我尝试做的不同的方式生成结果..
    【解决方案4】:

    看起来你正在使用选择进行分组

    select ph.ponumber, 
       pl.poline, 
       pl.polinebasemerchamount, 
       count(pl.poline) as total_count,
       sum(pl.polinebasemerchamount) as total_sum,
    case  when sum(pl.PoLineBaseMerchAmount) between 0 and 100 then '$0-100'
      when sum(pl.polinebasemerchamount) between 101 and 500 then '$101-500'
      when sum(pl.polinebasemerchamount) between 501 and 1000 then '$501-1000'
           else '1000+' end as total_group
    
    from dbo.poheader as ph
    inner join dbo.polineflat as pl
    on ph.pokey = pl.pokey
    group by     ph.ponumber, pl.poline, pl.polinebasemerchamount 
    

    【讨论】:

    • 感谢 Mehrad,我能够执行您的查询,但我没有得到我想要的结果..尤其是在 Total Sum 和 Total Group 列中..
    【解决方案5】:

    在这些情况下,我实际上使用#temp 表。我发现它们的性能要好得多,而且我可以更好地控制我想要的结果。它也更容易理解(至少对我和其他阅读它们的人来说)。我回答这篇文章的目的是帮助您利用这种类型的编码。您可以复制粘贴并按 F5,而不是直接给出答案。

    注意:请随意命名对您有意义的临时表。您还可以使用 LEFT JOIN/RIGHT JOIN 从 dbo.poheader 获取所有值,并查看这些值是否与您的 #temp 表匹配。您可能需要切换 ID 的 /temp 表以适应您需要的结果。我希望这可以帮助您了解这个想法。

     --Insert Subquery 1 into a #temp1
    
     select count(pl2.poline) as Count1, ph2.ponumber 
     Into #temp1
     from dbo.polineflat as pl2 
               inner join dbo.poheader as ph2 
               on ph2.pokey = pl2.pokey 
               where ph2.pokey = ph.pokey 
               group by ph2.ponumber)
    
      --Insert Subquery 2 into #temp2
    
      select sum(pl2.polinebasemerchamount) as Sum1, ph2.ponumber
      into #temp2
               from dbo.polineflat as pl2 
               inner join dbo.poheader as ph2 
               on ph2.pokey = pl2.pokey 
               where ph2.pokey = ph.pokey 
               group by ph2.ponumber
    
      --Insert Subquery 3 into #temp3. Pull pl2.polinebasemerchant from #temp2
    
       select case 
        when sum(#temp2.PoLineBaseMerchAmount) between 0 and 100 then '$0-100'
        when sum(#temp2.polinebasemerchamount) between 101 and 500 then'$101-500'
        when sum(#temp2..polinebasemerchamount) between 501 and 1000 then '$501-1000'
        else '1000+' end 'DollarAmounts',ph2.ponumber
        into #temp3
        from dbo.polineflat as pl2 
        inner join dbo.poheader as ph2 
         on ph2.pokey = pl2.pokey 
         where ph2.pokey = ph.pokey 
         group by ph2.ponumber
    
    
     -- Select here with the temp table groups
      select ph.ponumber, 
             pl.poline, 
             pl.polinebasemerchamount,
             #temp1.Count1,
             #temp2.Sum1,
             #temp3.DollarAmounts
    
       from dbo.poheader as ph
       join dbo.polineflat as pl on ph.pokey = pl.pokey
    
       --join your temp tables here
       Join #temp1 on #temp1.ponumber = ph.ponumber
       join #temp2 on #temp2.ponumber = ph.ponumber
       join #temp3 on #temp3.ponumber = ph.ponumber
    
    
       TRUNCATE TABLE #temp1
       TRUNCATE TABLE #temp2
       TRUNCATE TABLE #temp3
    

    【讨论】:

      猜你喜欢
      • 2013-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-12
      相关资源
      最近更新 更多