SQL Server 2008首日标准得到满足

时间:2010-08-18 14:23:22

标签: tsql sql-server-2008

我有一个数据表(将有数百万条记录,但我会在这里简化),看起来像这样。

ID   APPROVAL_DT       DAY_DT        TRANS_COUNT     SALE_AMOUNT
1    2010-04-22        2010-04-27    2               260
1    2010-04-22        2010-04-28    1               40
2    2010-03-28        2010-04-02    1               5
2    2010-03-28        2010-04-03    5               10
2    2010-03-28        2010-04-04    1               20
3    2010-04-25        2010-05-01    6               10
3    2010-04-25        2010-05-02    4               10
4    2010-06-01        2010-06-07    1               5

我需要找出每个ID的DAY_DT,其中所有之前和当前DAY_DT TRANS_COUNTs的总和> = 10或所有之前和当前DAY_DT的总和SALE_AMOUNTs> = 25

因此,应用于上表的查询结果将是

ID   APPROVAL_DT  ACTIVATED_DT
1    2010-04-22   2010-04-27
2    2010-03-28   2010-04-04
3    2010-04-25   2010-05-02
4    2010-06-01   NULL

有什么想法吗?

3 个答案:

答案 0 :(得分:1)

您拥有每个ID有多少条记录?

Itzik Ben Gan对SQL Server 2008中解决运行总计的各种方法进行了比较。

他在测试中得出的结论是,每个分区最多有15个记录triangular Join最好。那时SQL CLR方法变得更好了。三角形连接继续优于沼泽标准TSQL光标,直到分区大小达到500。

显然你的里程可能会有所不同,但我认为这些数字很有用。

答案 1 :(得分:1)

我认为你的意思是你要在ID中找到 first day_dt,其中前一天的总和是trans_count> = 10或者sales_amount> = 25。你这叫发现一天'activated_dt'。您的描述与此完全不同,因为它没有指定您只想要第一个日,并且它要求所有之前天的总和,而您的示例结果显示总和最多当天。

我同意马丁的观点,一个总计将是表现最好的,因为它可以在一次扫描表中产生结果。

没有运行总计的结果必须计算每天day_dt的前几天总数,然后为每个ID选择第一个:

with cte1 as (
select
  t.id,
  t.approval_dt,
  t.day_dt as activated_dt
from Table t
cross apply (
  select sum(trans_count) as sum_tc,
     sum(sale_amount) as sum_sa,
     max(day_dt) as max_day_dt
  from table c
  where c.id = t.id
  and c.day_dt <= t.day_dt) as p
where p.sum_tc >= 10
or p.sum_sa >=25)
, cte2 as (
  select id
   , approval_dt
   , activated_dt
   , row_number() over (partition by id order by activated_dt) as rn
  from cte1)
select *
from cte2
where rn = 1;

答案 2 :(得分:1)

好的。振作起来。我通过使用运行的总UPDATE方法使用了一点作弊方法。有一些怪癖,所以厌倦了把它放在生产代码中。其中最大的是如何遍历表。应该在APPROVAL_DAY列上放置聚簇索引,以确保不拆分日期。无论如何,这里也是。

CREATE TABLE #test
(
    ID   int,
    APPROVAL_DT   date,    
    DAY_DT        date,
    TRANS_COUNT     int,
    SALE_AMOUNT int,
    DailyTransCount int,
    DailySalesTotal int
)

INSERT INTO #test
SELECT 1,'2010-04-22','2010-04-27',2,260,0,0 UNION ALL
SELECT 1,'2010-04-22','2010-04-28', 1,40, 0,0 UNION ALL
SELECT 2,'2010-03-28','2010-04-02', 1,5, 0,0 UNION ALL
SELECT 2,'2010-03-28','2010-04-03', 5,10, 0,0 UNION ALL
SELECT 2,'2010-03-28','2010-04-04', 1,20, 0,0 UNION ALL
SELECT 3,'2010-04-25','2010-05-01', 6,10, 0,0 UNION ALL
SELECT 3,'2010-04-25','2010-05-02', 4,10, 0,0 UNION ALL
SELECT 4,'2010-06-01','2010-06-07', 1,5, 0,0

DECLARE @PreviousDay date; SET @PreviousDay = '29991231'
DECLARE @DailyTransCount int; SET @DailyTransCount = 0
DECLARE @DailySalesTotal int; SET @DailySalesTotal = 0
DECLARE @Group int; SET @Group = 0

UPDATE #test
    SET DailyTransCount = 0,
        DailySalesTotal = 0

UPDATE #test
    SET @DailyTransCount = DailyTransCount = CASE WHEN APPROVAL_DT = @PreviousDay THEN @DailyTransCount + Trans_Count ELSE Trans_Count END,
        @DailySalesTotal = DailySalesTotal = CASE WHEN APPROVAL_DT = @PreviousDay THEN @DailySalesTotal + SALE_AMOUNT ELSE SALE_AMOUNT END,
        @PreviousDay = APPROVAL_DT

SELECT Y.ID, X.APPROVAL_DT, X.DAY_DT FROM 
    (SELECT DISTINCT(ID) FROM #test T) Y
    LEFT JOIN ( SELECT ID, APPROVAL_DT, MIN(DAY_DT) AS DAY_DT FROM #test
                WHERE DailyTransCount >= 10 OR DailySalesTotal >= 25
                GROUP BY ID, APPROVAL_DT ) X ON X.ID = Y.ID

我应该解释一些事情:我在表的末尾创建了两个列。您需要将其放入临时表(或永久表)以将总计推入。在我将所有总计推入列后,它只是一个选择来检索结果。有关此技术的更多信息here。请注意,此解决方案很快,但对它有点不安全。