MYSQL:关于连续日期的表更新

时间:2012-03-11 01:44:08

标签: mysql sql gaps-and-islands

晚上好,

我整天都在和这个人搏斗。

我正在尝试更新我的客户表格中的一列,该列会显示次数他们是订阅者。此表是从大数据转储创建的,每个客户都有单独的行,并且每个月他们都是订阅者(为每个月分配一个迭代编号)。名字是独一无二的。它看起来像这样,并显示(例如)Jane Doe是1000期的订户,但不是1002期。

Row_ID Customer_Name  Date_Code 
1      Jane Doe       1000      
2      Jane Doe       1001      
3      Jane Doe       1004      
4      Jane Doe       1005      
5      Ted Jones      1000      
6      Ted Jones      1001      
7      Ted Jones      1002      
etc...

在这种情况下,Jane Doe是1000-1001的订阅者,离开了我们的订阅,然后从1004-1005回来。我有一个主表,包括所有日期逻辑(开始日期,结束日期,日期代码等...)。看起来大致如下:

Start_Date   End_Date    Date_Code
1990-01-01   1990-03-31  1000
1990-04-01   1990-06-30  1001
1990-07-01   1990-09-30  1002
1990-10-01   1990-12-31  1003
etc...

我正试图找到一种方法来使输出像:

Customer_Name  Subscription_Count
Jane Doe       2
Ted Jones      1

有没有人遇到过这样的事情?对我来说(作为一个人)很明显,这些数字是(或不是)连续的,并且是(或不是)整个样本的表示,但我不确定如何使MYSQL理解它。我很欣赏任何想法。

*编辑 - 我尝试了加入和不存在的替代方案,并且都在10分钟后超时。我相信这是由于主表的大小(约100,000行)。你有什么建议吗?再次感谢所有评论。

**编辑#2 - 添加索引并稍微调整一下表后,两种解决方案都很有效。再次感谢您对这一点的支持。

2 个答案:

答案 0 :(得分:1)

查询可能如下所示:

SELECT customer_name, count(*) AS subscriptions
FROM   tbl AS t
WHERE NOT EXISTS (
    SELECT *
    FROM tbl AS t1
    WHERE t1.customer_name = t.customer_name
    AND t1.date_code = t.date_code + 1
    )
GROUP BY customer_name;

这里的技巧是排除客户的每个date_codes系列中的所有行,然后计数:只有每个块的最后一行没有后继(date_code + 1)。

我假设连续的date_codes形成一个订阅(根据我对问题的第一个评论)。因此,不需要Start_DateEnd_Date的其他信息。


性能

实际上,{p> LEFT JOIN / IS NULL应该比MySQL中的NOT EXISTS快一点(如@nnichols提供的那样)。
性能更重要的是索引。为了加快速度,您需要customer_namedate_code上的索引。像这样:

CREATE INDEX tbl_customer_name ON tbl(customer_name);
CREATE INDEX tbl_date_code ON tbl(date_code);

答案 1 :(得分:1)

我无法100%确定仍然如此,但LEFT JOIN / IS NULL通常比MySQL中的NOT EXISTS更快 -

SELECT t1.customer_name, COUNT(*) AS subscriptions
FROM   tbl t1
LEFT JOIN tbl t2
    ON t1.customer_name = t2.customer_name
    AND t1.date_code + 1 = t2.date_code
WHERE t2.customer_name IS NULL
GROUP BY t1.customer_name

UPDATE 在这两个字段中添加复合索引而不是两个单列索引会显着提升性能 -

CREATE UNIQUE INDEX `UQ_customer_date_code` ON tbl (customer_name, date_code);

我使用一个包含160万条记录的测试表(21个date_codes的100k客户)进行了一些测试。添加此索引后,查询时间减少了约80%。使用LEFT JOIN而不是NOT EXISTS只会将查询时间减少大约15%。