【问题标题】:Optimising T-SQL reporting performance优化 T-SQL 报告性能
【发布时间】:2019-08-12 03:03:20
【问题描述】:

我有下面的表格,我需要根据 PerCode 值成对删除两个日期之间的相反行, 实际上,我们删除了日期范围内具有相同 PerCode 并且具有相等和相反值的行。

问题是开始日期和结束日期是用户在报告时作为参数提供的,但是如果我尝试在运行时删除它们,查询会花费太多时间。

例子:

开始日期 = 01/01/2018 结束日期 = 2018 年 12 月 31 日

我应该删除第 3 行和第 4 行。 你知道如何在优化性能的同时做到这一点(表有 2 亿行)

+----+------------+---------+---------+-----------+
| Id |    Date    | PerCode |  Value  | IsDeleted |
+----+------------+---------+---------+-----------+
|  1 | 01/10/2017 | C1      |    10   |           |
|  2 | 01/01/2018 | C1      |   -10   |           |
|  3 | 15/02/2018 | C2      |    20   |    1      |
|  4 | 10/03/2018 | C2      |   -20   |    1      |
|  5 | 01/12/2018 | C3      |    15   |           |
|  6 | 01/02/2019 | C3      |   -15   |           |
+----+------------+---------+---------------------+

【问题讨论】:

  • 我不明白您为什么根据问题中的信息删除第 3 行和第 4 行。我可以看到第 3 行和第 4 行的值的总和为 -20 + 20 = 0。但该日期范围内还有其他行。第 2 行的值为 -10,第 5 行的值为 +15。其他行都在日期范围之外。日期范围内所有行的 SUM 实际上是 +5,所以我预计不会删除任何行。没有什么可以解释为什么第 3 行和第 4 行的处理方式不同。我可以看到它们具有相等和相反的值,但这没有作为标准的一部分提及。
  • @RichardHansell,谢谢你的回复我添加了 PerCode 列以便更清楚,行也应该根据 PerCode 值成对删除,所以我们删除日期范围内具有相同 PerCode 的行并且具有相等和相反的值
  • 好的,这对你的问题有很大的影响!我的意思是从根本上改变了你的要求。这背后可能有很多问题,例如每个“percode”总是正好有两行吗?如果“percode”有三行,+10、-10、-10,你会随机删除+10和一-10,还是保留所有三行?当您说删除时,您的意思是“设置 IsDeleted 标志”?
  • 是的,每个“percode”可以有超过 2 行,如果是这样,我会随机删除 +10 和一个 -10,正如你已经说过的。除此之外,删除意味着设置一个 IsDeleted 标志。我已经完成了完成这项工作的查询(查询更新了表上的 isDeleted 列),但我有义务在运行时(报告时间)应用查询,因为我不知道之前的开始日期和结束日期,并且这需要太多时间。
  • 你能提供你已经使用的查询(那个不是高性能的)吗?

标签: tsql sql-server-2012 query-performance


【解决方案1】:

我对此进行了快速处理,使用表变量让我可以使用您的测试数据组合查询。但是,当使用超过 200 万行时,这可能效果不佳?

DECLARE @table TABLE (id INT, [date] DATE, percode CHAR(2), [value] INT, isdeleted BIT);
INSERT INTO @table
SELECT 1, '20171001', 'C1', 10, NULL
UNION ALL
SELECT 2, '20180101', 'C1', -10, NULL
UNION ALL
SELECT 3, '20180215', 'C2', 20, NULL
UNION ALL
SELECT 4, '20180310', 'C2', -20, NULL
UNION ALL
SELECT 5, '20181201', 'C3', 15, NULL
UNION ALL
SELECT 6, '20190201', 'C3', -15, NULL;

DECLARE @date_from DATE = '20180101';
DECLARE @date_to DATE = '20181231';

WITH ordered AS (
    SELECT
        id, 
        percode, 
        [value],
        ROW_NUMBER() OVER (PARTITION BY percode, [value] ORDER BY [value]) AS order_id
    FROM
        @table
    WHERE
        [date] BETWEEN @date_from AND @date_to
        AND ISNULL(isdeleted, 0) != 1),
matches AS (
    SELECT 
        m1.id AS match_1_id,
        m2.id AS match_2_id 
    FROM 
        ordered m1
        INNER JOIN ordered m2 ON m1.percode = m2.percode AND m1.[value] = m2.[value] * -1 AND m1.order_id = m2.order_id)
UPDATE
    t
SET
    isdeleted = 1
FROM
    @table t
    INNER JOIN matches m ON m.match_1_id = t.id OR m.match_2_id = t.id;
SELECT * FROM @table;

结果:

id  date        percode value   isdeleted
1   2017-10-01  C1      10      NULL
2   2018-01-01  C1      -10     NULL
3   2018-02-15  C2      20      1
4   2018-03-10  C2      -20     1
5   2018-12-01  C3      15      NULL
6   2019-02-01  C3      -15     NULL

它是如何工作的?好吧,我将任务分解为几个步骤:

  • 列出指定日期期间内尚未删除的所有行;
  • 为每一行数据分配一个运行计数,按 percode 和值分组。所以第一个 C1 10 将是编号 #1,然后第二个 C1 10 将是编号 #2,依此类推;
  • 要查找匹配项,只需查找具有相同 percode、与另一个值组相同且相反的值以及相同的运行计数的任何值;
  • 如果有匹配项,请将 isdeleted 标志设置为 1。

【讨论】:

    【解决方案2】:

    这是我的代码,但这不是实时超过 2 亿行。 而在现实生活中,Percode 是 5 列(date、varchar(13)、varchar(2)、varchar(1) 和 varchar(50))的串联,而 Value 是 4 个数字列。

    我正在寻找其他想法。

    --DECLARE @table TABLE (id INT, [date] DATE, percode CHAR(2), [value] INT, isdeleted BIT);
    Select * INTO #MasterTable FROM
    (
    SELECT 1 id, '20171001' [date], 'C1' percode, 10 [value], NULL isdeleted
    UNION ALL
    SELECT 2, '20180101', 'C1', -10, NULL
    UNION ALL
    SELECT 3, '20180215', 'C2', 20, NULL
    UNION ALL
    SELECT 4, '20180310', 'C2', -20, NULL
    UNION ALL
    SELECT 5, '20181201', 'C3', 15, NULL
    UNION ALL
    SELECT 6, '20190201', 'C3', -15, NULL
    ) T ;
    
    DECLARE @date_from DATE = '20180101';
    DECLARE @date_to DATE = '20181231';
    
    select F.id
    Into #TmpTable
    from 
    (
    select Id, PerCode, Value
    ,ROW_NUMBER() over (partition by PerCode, Value order by (select 0)) Rn2
    from
    #MasterTable ) F 
    inner join (
    select 
    PerCode
    , Rn1
    from (
    select  
    PerCode
     ,Value
    
    ,ROW_NUMBER() over (partition by PerCode, Value order by (select 0)) Rn1
    FROM #MasterTable
    where
    [date] BETWEEN @date_from AND @date_to
    ) A
    group by PerCode , Rn1
    having sum(Value) = 0  and count(*)>1
    
    ) B on                F.PerCode = B.PerCode
                      and F.Rn2 = B.Rn1
    
    
    update  R
    set IsDeleted = 1
    from #MasterTable R
    inner join #TmpTable P
    on R.id = P.id
    
    select * from #MasterTable
    
    drop table #MasterTable ;
    drop table #TmpTable;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-29
      相关资源
      最近更新 更多