查询耗时太长 - 优化

时间:2013-11-19 21:56:49

标签: sql sql-server-2008 optimization

我遇到以下查询的问题,返回结果有点太慢,我怀疑我遗漏了一些基本的东西。我最初的猜测是'CASE'语句花费太长时间处理底层数据的结果。但它也可能是派生表中的某些东西。

问题是,我怎样才能加快速度呢?我提取数据的方式是否有任何明显的错误?我是否在某处遇到排序或循环问题?查询运行大约40秒,这似乎很长。 C#是我的主要专长,SQL是一项正在进行中的工作。

注意我不是要求“编写我的代码”或“修复我的代码”。只是指向正确的方向,我似乎无法弄清楚减速发生的位置。每个派生表自己运行得非常快(不到一秒),连接似乎是正确的,结果集正好返回我需要的。它太慢了,我确信有更好的SQL脚本编写器;)任何提示都将不胜感激!

  SELECT 
hdr.taker
, hdr.order_no
, hdr.po_no as display_po
, cust.customer_name
, hdr.customer_id
, 'INCORRECT-LARGE ORDER' + CASE 
                    WHEN (ext_price_calc >= 600.01 and ext_price_calc <= 800) and fee_price.unit_price <>  round(ext_price_calc * -.01,2)
                        THEN '-1%: $' + cast(cast(ext_price_calc * -.01 as decimal(18,2)) as varchar(255))
                    WHEN ext_price_calc >= 800.01 and ext_price_calc <= 1000 and fee_price.unit_price <>  round(ext_price_calc * -.02,2)
                        THEN '-2%: $' + cast(cast(ext_price_calc * -.02 as decimal(18,2)) as varchar(255))
                    WHEN ext_price_calc > 1000 and fee_price.unit_price <>  round(ext_price_calc * -.03,2)
                        THEN '-3%: $' + cast(cast(ext_price_calc * -.03 as decimal(18,2)) as varchar(255))
                    ELSE
                        'OK'
                  END AS Status
FROM
(myDb_view_oe_hdr hdr
LEFT OUTER JOIN myDb_view_customer cust 
ON hdr.customer_id = cust.customer_id)
LEFT OUTER JOIN wpd_view_sales_territory_by_customer territory
ON cust.customer_id = territory.customer_id
LEFT OUTER JOIN 
    (select
        order_no,
        SUM(ext_price_calc) as ext_price_calc
    from 
    (select
        hdr.order_no,
        line.item_id,
        (line.qty_ordered - isnull(qty_canceled,0)) * unit_price as ext_price_calc 
    from myDb_view_oe_hdr hdr
    left outer join myDb_view_oe_line line
    on hdr.order_no = line.order_no
    where 
        line.delete_flag = 'N'
        AND line.cancel_flag = 'N'
        AND hdr.projected_order = 'N'
        AND hdr.delete_flag = 'N'
        AND hdr.cancel_flag = 'N'
        AND line.item_id not in ('LARGE-ORDER-1%','LARGE-ORDER-2%', 'LARGE-ORDER-3%', 'FUEL','NET-FUEL', 'CONVENIENCE-FEE')) as line
    group by order_no)  as order_total
    on hdr.order_no = order_total.order_no
 LEFT OUTER JOIN 
    (select 
        order_no,
        count(order_no) as convenience_count
    from oe_line with (nolock)
    left outer join inv_mast inv with (nolock)
    on oe_line.inv_mast_uid = inv.inv_mast_uid
    where inv.item_id in ('LARGE-ORDER-1%','LARGE-ORDER-2%', 'LARGE-ORDER-3%')
        and oe_line.delete_flag <> 'Y'
    group by order_no) as fee_count
on hdr.order_no = fee_count.order_no
INNER JOIN 
    (select 
        order_no, 
        unit_price 
    from oe_line line with (nolock) 
    where line.inv_mast_uid in (select inv_mast_uid from inv_mast with (nolock) where item_id in ('LARGE-ORDER-1%','LARGE-ORDER-2%', 'LARGE-ORDER-3%'))) as fee_price
ON fee_count.order_no = fee_price.order_no
WHERE
    hdr.projected_order = 'N'
    AND hdr.cancel_flag = 'N'
    AND hdr.delete_flag = 'N'
    AND hdr.completed = 'N'
    AND territory.territory_id = ‘CUSTOMERTERRITORY’
    AND ext_price_calc > 600.00
    AND hdr.carrier_id <> '100004'
    AND fee_count.convenience_count is not null
    AND CASE 
            WHEN (ext_price_calc >= 600.01 and ext_price_calc <= 800) and fee_price.unit_price <>  round(ext_price_calc * -.01,2)
                THEN '-1%: $' + cast(cast(ext_price_calc * -.01 as decimal(18,2)) as varchar(255))
            WHEN ext_price_calc >= 800.01 and ext_price_calc <= 1000 and fee_price.unit_price <>  round(ext_price_calc * -.02,2)
                THEN '-2%: $' + cast(cast(ext_price_calc * -.02 as decimal(18,2)) as varchar(255))
            WHEN ext_price_calc > 1000 and fee_price.unit_price <>  round(ext_price_calc * -.03,2)
                THEN '-3%: $' + cast(cast(ext_price_calc * -.03 as decimal(18,2)) as varchar(255))
            ELSE
                'OK' END <> 'OK'

4 个答案:

答案 0 :(得分:2)

正如优化正确方向的线索一样:

  • 对具有计算列的查询执行OUTER JOIN时,不仅要保证全表扫描,还要保证必须对连接表中的每一行执行这些计算。您似乎可以在没有列计算的情况下实际加入oe_line(即通过将 ext_price_calc 过滤到特定范围)。

  • 您不需要执行查询中的大多数子查询 - 可以重新收集主查询以使用常规表连接语法。连接到包含子查询的子查询对SQL优化器提出了一个可能无法满足的挑战。但是通过使用常规连接,优化器可以更好地识别更有效的查询策略。

  • 您没有标记您正在使用的SQL引擎。每个数据库都有专有扩展,可以实现更快或更高效的查询。如果您表明您使用的是MySQL,SQL Server,Oracle等,那么提供有用的反馈会更容易。

  • 无论您使用何种数据库,查看查询计划始终是一个好的起点。这将告诉您查询中的大部分I / O和时间在哪里。

  • 根据一般原则,请确保您的统计信息是最新的。

答案 1 :(得分:1)

如果没有要测试的真实内容,我们任何人都可能无法解决这个问题。

如果是这种情况,没有其他人发布答案,我仍然可以提供帮助。这是如何解决它的麻烦。

(1)逐一加入连接。
   (2)这会导致错误。删除或伪造参考文献以摆脱它们 (3)看看它是如何工作的 (4)在尝试取出其他东西之前放回物品 (5)跟踪...
(6)也要注意删除某些东西可能会大大减少结果集。

你可能会发现你错过了一个索引或其他一些吸烟枪。

答案 2 :(得分:0)

我强烈怀疑问题在于您正在进行的联接数量。很多数据库基本上通过系统地检查各种表的所有可能组合是否有效来加入 - 所以如果你在C列上加入表A和B,A看起来像: 名称:C 弗雷德:1 爱丽丝:2 贝:3

B看起来像: C:宠物 1:鳄鱼 2:狮子 3:T-雷克斯

当您进行连接时,它会检查所有9种可能性: 弗雷德:1:1:鳄鱼 佛瑞德:1:2:狮子 佛瑞德:1:3:T-雷克斯 爱丽丝:2:1:短吻鳄 爱丽丝:2:2:狮子 爱丽丝:2:3:T-雷克斯 贝:3:1:短吻鳄 贝:3:2:狮子 贝:3:3:T-雷克斯

然后浏览并删除不匹配的: 弗雷德:1:1:鳄鱼 爱丽丝:2:2:狮子 贝:3:3:T-雷克斯

...这意味着在每个表中有三个条目,它会创建九个临时记录,对它们进行排序,并删除其中的六个...所有这些都在它实际对结果进行排序之前(所以如果你正在寻找贝蒂的宠物,你只想在最后的结果上排一排)。

...你正在做多少个连接和子查询?

答案 3 :(得分:0)

我遇到了同样的问题,我能够通过索引其中一个表并设置主键来解决该问题。