【问题标题】:How to optimize SQL query to achieve the minimum execution time如何优化 SQL 查询以实现最短执行时间
【发布时间】:2013-05-08 08:17:58
【问题描述】:

我有一个简单的表'TABLE_1'

Org   Customer   Code   Ordered   Deleted   Confirmed

RU     Cust_1      A      1000       800        200 
RU     Cust_2      B      300        0          300
US     Cust_3      C      800        100        700
RU     Cust_4      B      100        100        0
US     Cust_5      C      400        200        200 
RU     Cust_6      B      500        300        200   

现在我需要为那些行转换此表,其中 'Deleted'0 like

Org   Code    Customers          Ordered   Confirmed 

RU     A      Cust_1               1000       200
RU     B      Cust_4, Cust_6       600        200
US     C      Cust_3, Cust_5       1200       900

我正在使用以下查询和函数

SELECT T1.Org,
       T1.Code,
       dbo.FUNC(T1.Code, T1.Org) AS 'Customers',
       'Ordered' = (SELECT SUM(Ordered) FROM TABLE_1 AS T2 WHERE T2.Customer = T1.Customer AND T2.Code = T1.Code AND T2.Deleted<>0),
       'Confirmed' = (SELECT SUM(Confirmed) FROM TABLE_1 AS T3 WHERE T3.Customer = T1.Customer AND T3.Code = T1.Code AND T3.Deleted<>0)
FROM TABLE_1 AS T1 
WHERE T1.Deleted <> 0

函数'FUNC':

ALTER FUNCTION [dbo].[FUNC] (@c VARCHAR(MAX), @org VARCHAR(MAX))
RETURNS VARCHAR(MAX) AS BEGIN
DECLARE @p VARCHAR(MAX) ;
SET @p = '' ;
SELECT @p = @p + T1.Customer + ', '
FROM TABLE_1 AS T1
WHERE T1.Code = @c AND T1.Org = @org AND T1.Deleted <> 0
GROUP BY T1.Customer
RETURN SUBSTRING(@p, 1, LEN(@p) - 1)
END

我认为这不是获得结果的最佳方式,尤其是在我有一张大桌子的情况下。 有没有更好的解决方案?

编辑: 表 DDL 示例

CREATE TABLE [dbo].[TABLE_1](
[Org] [nchar](10) NULL,
[Customer] [nchar](100) NULL,
[Code] [nchar](10) NULL,
[Ordered] [decimal](18,1) NULL,
[Deleted] [decimal](18,1) NULL,
[Confirmed] [decimal](18,1) NULL) 
ON [PRIMARY]

【问题讨论】:

  • 你真的想在TSQL中连接字符串,没有表现层吗?
  • 我不确定我理解你的意思。我应该在查询中使用连接而不是函数吗?
  • 同一客户、组织和代码是否有可能超过一行?如果是这样,如果不针对不同的客户进行一些修复,大多数提供的答案(到目前为止)都将无法正常工作。
  • 根据我的测试,您的函数在速度上仍然胜过所提供的答案。
  • @mbigun,我已经扩展了我的答案以说明我的意思。 MSSQL 真的很讨厌字符串连接,在 MSSQL 中执行该操作是否必要?

标签: sql sql-server-2008 tsql stored-procedures


【解决方案1】:

无论您选择做什么,您都将面临 RBAR“问题”。 但是,您可能会发现使用 FOR XML PATH('') + OUTER APPLY 代替您的函数会更好。

如果你不知道这些,我会写一段代码来演示一下用法。 但是您能否先提供您的表格 DDL(+一些行)。

这里是:

SELECT
    T1.Org
    , T1.Code
    , ISNULL(STUFF(F.Customers, 1, 2, ''), '')  AS Customers
    , SUM(T1.Ordered) OVER (PARTITION BY T1.Customer, T1.Code) AS Ordered
    , SUM(T1.Confirmed) OVER (PARTITION BY T1.Customer, T1.Code) AS Confirmed
FROM TABLE_1 AS T1
OUTER APPLY (
    SELECT
        ', ' + T2.Customer
    FROM TABLE_1 AS T2
    WHERE T2.Code = T1.Code
    AND T2.Org = T1.Org
    AND T2.Deleted <> 0
    FOR XML PATH('')
) AS F(Customers)
WHERE T1.Deleted <> 0

【讨论】:

  • 谢谢!如果您提供一些 CTE 解决方案的示例,我将不胜感激。
  • 抱歉,CTE 只会使查询实际上运行得很糟糕,因为它会依赖 ROW_NUMBER 其中的 PARTITION BY 子句无法帮助 MSSQL 避免进行完全扫描。抱歉,我什至在我的版本之前谈到了 CTE。
  • 请注意,这不会给出正确的结果,但实际执行时间(即使已修复)比原始函数差 10 倍。
【解决方案2】:

SQLFiddle demo

SELECT
   Org,
   Code,
   STUFF(
      (SELECT ','+Customer
       FROM t WHERE Code=a.Code and Deleted<>0
       FOR XML PATH('')) , 1 , 1 , '' ),
   SUM(ordered),
   SUM(Confirmed) 

FROM 
   t A 
where Deleted<>0
group by ORG,code

【讨论】:

  • mbigun 不会在他的查询中对行进行分组,但您对他的假设可能是正确的。
【解决方案3】:

什么是最好的可能需要对您的特定数据进行一些测试,但首先让我们修复您的原始查询,以获得您在问题中所写的正确结果:

SELECT T1.Org,
       T1.Code,
       dbo.FUNC(T1.Code, T1.Org) AS Customers,
       SUM(Ordered) AS Ordered,
       SUM(Confirmed) AS Confirmed
FROM TABLE_1 AS T1 
WHERE T1.Deleted <> 0
GROUP BY T1.Org, T1.Code

【讨论】:

    【解决方案4】:

    您可以执行此操作,即使您的客户名称包含 XML 控制字符,它也应该有效。

    Fiddle Here

    SELECT
                t1.[Org],
                t1.[Code],
                STUFF(
                    (
                     SELECT
                                   ', ' + c.[Customer]
                         FROM
                                   [TABLE_1] c
                         WHERE
                                   c.[Deleted] <> 0
                             AND
                                   c.[Org] = t1.[Org]
                             AND
                                   c.[Code] = t1.[Code]
                         ORDER BY
                                   c.[Customer]
                         FOR XML PATH (''), TYPE
                     ).value('.', 'varchar(max)'),
                     1,
                     2,
                     '') [Customers],
                SUM(t1.[Ordered]),
                SUM(t1.[Confirmed])
        FROM
                [TABLE_1] t1
    
        WHERE
                t1.[Deleted] <> 0
        GROUP BY
                t1.[Org],
                t1.[Code];
    

    就性能而言,只执行两个查询并担心稍后显示为逗号分隔列表是有意义的。您可以获得相同的信息,但没有 MSSQL 无法实现的字符串聚合开销。

    Fiddle Here

    SELECT
                t1.[Org],
                t1.[Code],
                SUM(t1.[Ordered]),
                SUM(t1.[Confirmed])
        FROM
                [TABLE_1] t1
    
        WHERE
                t1.[Deleted] <> 0
        GROUP BY
                t1.[Org],
                t1.[Code];
    
    SELECT
                t1.[Org],
                t1.[Code],
                t1.[Customer]
        FROM
                [TABLE_1] t1
    
        WHERE
                t1.[Deleted] <> 0
        ORDER BY
                t1.[Org],
                t1.[Code],
                t1.[Customer];
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-09
      • 2020-02-10
      • 1970-01-01
      • 2018-08-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多