2013-09-11 5 views
1

Я пытаюсь подсчитать строки в таблице events, где дата в столбце EventDate происходит между двумя датами, указанными в другой таблице customers.MySQL подсчитывает строки в одной таблице на основе дат в другой таблице

КЛИЕНТЫ

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 

РЕЗУЛЬТАТ

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, но первая дата не лежит между EventTo и EventFrom поэтому не должен» т. 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. Я попытался установить EventFrom и EventTo в качестве индексов, но это не сильно изменило ситуацию. И этот запрос является частью более крупного запроса, поэтому у меня были индексы, настроенные для основной части.

Я также попытался это:

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 имеет около 1,5 миллионов строк, но все же запрос, кажется, занимает неоправданно долгое время. Есть ли лучший способ структурирования этого?

+0

Идея: использовать внутреннее соединение вместо левого присоединиться к подсчету, а затем использовать другой запрос для добавления нулей в наборе результатов, если вы действительно нуждаетесь в них (возможно, нет?). И вы, похоже, не считаете события в любом месте вашего запроса, вопреки своим требованиям в первом предложении. –

+0

Является ли COUNT (events.ID) не считая событий? Мне не обязательно нужны нули, просто это подмножество большего запроса, и я хочу, чтобы все строки в «клиентах» возвращались для других целей. – MadScone

+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 | 
+0

Спасибо, это намного лучше, чем я делал. – MadScone

1

Пользователь left join. Поместите условие даты в пункт on. Затем посчитайте матчи в таблице с помощью count(e.ID) (который подсчитывает непустые значения):

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; 
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 
Смежные вопросы