Tsql 满足SQL Server 2008第一天标准
我有一个数据表,将有数百万条记录,但我会在这里简化,看起来像这样Tsql 满足SQL Server 2008第一天标准,tsql,sql-server-2008,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
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的日期,其中所有前一天和当前日期的交易金额之和>=10,或者所有前一天和当前日期的销售金额之和>=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
有什么想法吗?每个id有多少条记录 伊齐克·本·甘(Itzik Ben Gan)比较了处理SQL Server 2008中运行总数的各种方法 他在测试中得出的结论是,直到每个分区有15条记录为止,这是最好的。在这一点上,方法变得更好了。三角形连接继续优于bog标准TSQL游标,直到分区大小达到500
显然,您的里程数可能会有所不同,但我认为这些都是有用的数字。我想您的意思是,您希望在ID中找到前一天的总里程数>=10或销售额>=25的第一天里程数。你称这一天为“激活日”。您的描述与此完全不同,因为它没有指定您只需要第一天,它要求计算所有前几天的总和,而您的示例结果显示截至当天的总和 我同意Martin的观点,运行总计是表现最好的,因为它可以在表格的一次扫描中产生结果 没有运行总计的结果必须计算前几天每天的总计,然后为每个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;
好的。振作起来。我使用了一点作弊的方法,使用了running-total-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
我应该解释几件事:我在表的末尾又创建了两列。您需要将其放入临时表或永久表中,以将总数推送到。在我把所有的总数都推到列中之后,只需选择就可以检索结果。关于这项技术有更多的信息。请注意,此解决方案速度快,但有点不安全。使用SSIS 2008的答案也是可以接受的!从您的描述中,我无法理解为什么第1行是ID 1的正确激活行,b ID 2如何满足您使用的前一个标准中的任何一个,这将倾向于表明当前行不包括在比较中,但我不确定这是否正确,因为第1行是ID 1的正确激活行,因为销售金额>=25。ID 2符合2010年4月4日的销售金额标准。ID 3符合2010年5月2日的trans_计数标准。我想它应该是所有前一行+当前行。我将更新问题。每个ID的平均记录为41,最大值为232,最小值为1,请尝试。我有大约60万条记录要测试。看起来我已经被保存了一份工作:-它确实很快,但它依赖于完全未记录的行为。另请参见此处的规则部分。+1,因为该技巧仍然很好地掌握,并在您的工具箱中。我们现在需要担心3000年虫子吗;但是你没有遵守规则部分,这就是我指出的原因!没有聚集索引,没有'OPTION MAXDOP 1`@Martin-谢谢你把这篇文章放在这里,让其他人可以阅读规则@雷姆斯-如果我们能活到2999年,我们都会有工作保障:在我的600K记录测试中,这只花了13秒。它每周运行一次,所以性能没什么大不了的,13秒当然可以接受。如果你按ID对表进行聚类索引,我打赌它会运行得更快。当然,您不应该为每周只使用一次的标准对其进行集群。尽管如此,我猜想其他查询类似于在日期范围内查找订单ID,所以我会考虑和评估像这样的表的聚类。您当前的聚集索引键是什么?当前没有任何索引标记?因为我刚刚开始这个过程。我将研究您提到的聚集索引。sys.indexes:。因此,“索引”在词源上可能不准确,但在技术上是正确的。如果您的表目前是一个堆,那么像我建议的那样的聚集键可能是一个好主意,不仅对于这个查询。我建议您先看一下设计索引一章