MySQL根据另一个表中的日期计算一个表中的行数

时间:2013-09-11 15:26:32

标签: mysql sql

我正在尝试计算表events中的行,其中列EventDate中的日期出现在另一个表customers中给出的两个日期之间。

客户

ID  EventFrom   EventTo
--  ----------  -----------
1   2011-01-01  2012-01-01
2   2012-12-10  2013-12-10
3   2010-05-01  2011-05-01
4   2011-01-01  2012-01-01
5   2012-07-30  2013-07-30
6   2011-06-21  2012-06-21
7   2011-06-22  2012-06-22
8   2010-02-19  2011-02-19

活动

ID  EventDate
--  ----------
2   1999-01-01
2   2012-12-12
2   2012-12-13
3   1900-01-12
4   2011-02-10
4   2011-02-11
4   2011-02-12

RESULT

ID  EventFrom   EventTo      Events
--  ----------  -----------  ------
1   2011-01-01  2012-01-01   0
2   2012-12-10  2013-12-10   2
3   2010-05-01  2011-05-01   0
4   2011-01-01  2012-01-01   3
5   2012-07-30  2013-07-30   0
6   2011-06-21  2012-06-21   0
7   2011-06-22  2012-06-22   0
8   2010-02-19  2011-02-19   0

ID {2}在events中出现两次,但第一个日期不在EventToEventFrom之间,因此不应计算在内。 ID 4在events中出现三次,并且都在正确的范围内。

我可以做到,但我最终得到了一个非常慢的嵌套连接。

SELECT customers.ID
, customers.EventFrom
, customers.EventTo
, IFNULL(e.Events, 0) AS 'Events'
FROM customers
LEFT JOIN (
    SELECT events.ID, COUNT(events.ID) AS 'Events'
    FROM events
    INNER JOIN customers ON customers.ID = events.ID
        AND events.EventDate BETWEEN customers.EventFrom AND customers.EventTo
    GROUP BY events.ID
) e ON e.ID = customers.ID

我已将EventDate设置为events中的索引。我尝试将EventFromEventTo设置为索引,但它没有产生很大的不同。此查询是较大查询的一部分,因此我为主要部分设置了索引。

我也试过这个:

SELECT customers.ID
, customers.EventFrom
, customers.EventTo
, SUM(IF(events.EventDate BETWEEN customers.EventFrom AND customers.EventTo), 1, 0) AS 'Events'
FROM customers
LEFT JOIN events ON events.ID = customers.ID

这也非常慢。 customers有大约150万行,但查询似乎仍然花费了不合理的长时间。有没有更好的结构方式?

3 个答案:

答案 0 :(得分:5)

SQL Fiddle

MySQL 5.5.32架构设置

CREATE TABLE CUSTOMERS
    (`ID` varchar(2), `EventFrom` varchar(10), `EventTo` varchar(11))
;

INSERT INTO CUSTOMERS
    (`ID`, `EventFrom`, `EventTo`)
VALUES
    ('1', '2011-01-01', '2012-01-01'),
    ('2', '2012-12-10', '2013-12-10'),
    ('3', '2010-05-01', '2011-05-01'),
    ('4', '2011-01-01', '2012-01-01'),
    ('5', '2012-07-30', '2013-07-30'),
    ('6', '2011-06-21', '2012-06-21'),
    ('7', '2011-06-22', '2012-06-22'),
    ('8', '2010-02-19', '2011-02-19')
;

CREATE TABLE EVENTS
    (`ID` int, `EventDate` datetime)
;

INSERT INTO EVENTS
    (`ID`, `EventDate`)
VALUES
    (2, '1999-01-01 00:00:00'),
    (2, '2012-12-12 00:00:00'),
    (2, '2012-12-13 00:00:00'),
    (3, '1900-01-12 00:00:00'),
    (4, '2011-02-10 00:00:00'),
    (4, '2011-02-11 00:00:00'),
    (4, '2011-02-12 00:00:00')
;

查询1

SELECT c.Id, c.EventFrom, c.EventTo, COUNT(e.ID)
FROM CUSTOMERS c
LEFT JOIN EVENTS e ON e.ID = c.ID AND 
                      e.EventDate BETWEEN c.EventFrom AND c.EventTo
GROUP BY c.Id, c.EventFrom, c.EventTo

<强> Results

| ID |  EVENTFROM |    EVENTTO | COUNT(E.ID) |
|----|------------|------------|-------------|
|  1 | 2011-01-01 | 2012-01-01 |           0 |
|  2 | 2012-12-10 | 2013-12-10 |           2 |
|  3 | 2010-05-01 | 2011-05-01 |           0 |
|  4 | 2011-01-01 | 2012-01-01 |           3 |
|  5 | 2012-07-30 | 2013-07-30 |           0 |
|  6 | 2011-06-21 | 2012-06-21 |           0 |
|  7 | 2011-06-22 | 2012-06-22 |           0 |
|  8 | 2010-02-19 | 2011-02-19 |           0 |

答案 1 :(得分:1)

用户left join。将日期条件放在on子句中。然后使用count(e.ID)计算表中的匹配项(计算非NULL值):

SELECT c.ID, c.EventFrom, c.EventTo,
       COUNT(e.ID) as "Events"
FROM customers c LEFT JOIN
     events e
     ON e.ID = c.ID and
        e.EventDate BETWEEN c.EventFrom AND c.EventTo
GROUP BY c.ID, c.EventFrom, c.EventTo;

答案 2 :(得分:1)

我宁愿做

select 
  c.Id, 
  c.EventFrom,
  c.EventTo
  COUNT(e.ID)
FROM customers c
LEFT JOIN events e on e.ID = c.ID and e.EvenDate BETWEEN c.EventFrom and c.EventTo
GROUP BY c.Id, c.EventFrom, c.EventTo