2013-04-03 2 views
1

У меня есть таблица MySQL, где есть много строк для каждого человека, и я хочу написать запрос, который объединяет строки со специальным ограничением. (один на человека)Как написать запрос, который объединяет одну строку с последней датой среди нескольких строк?

Например, скажем, таблица состоит из следующих данных.

name date     reason 
--------------------------------------- 
John 2013-04-01 14:00:00  Vacation 
John 2013-03-31 18:00:00  Sick 
Ted 2012-05-06 20:00:00  Sick 
Ted 2012-02-20 01:00:00  Vacation 
John 2011-12-21 00:00:00  Sick 
Bob 2011-04-02 20:00:00  Sick 

Я хочу видеть распределение столбца «причина». Если я просто напишу запрос, например, ниже

select reason, count(*) as count from table group by reason 

затем я смогу увидеть множество причин для этой таблицы в целом.

reason  count 
------------------ 
Sick   4 
Vacation  2 

Однако меня интересует только одна причина от каждого человека. Причина, которая должна быть засчитана, должна быть из строки с последней датой из записей человека. Например, самая последняя причина Джона - Vacation, а последняя причина Теда - Sick. И последняя причина Боба (и единственная причина) - Sick.

Ожидаемый результат для этого запроса должен быть следующим. (Сумма графа будет 3, потому что есть только 3 человека)

reason  count 
----------------- 
Sick  2 
Vacation 1 

Можно ли написать запрос такой, что однократное последняя причина будет учитываться, когда я хочу, чтобы увидеть распределение (счет) причин?

Вот некоторые факты о таблице.

  • Таблица содержит десятки миллионов строк
  • Для большинства раз, каждый человек имеет одну причину.
  • Некоторые люди имеют несколько причин, но 99,99% людей имеют менее 5 причин.
  • Существует около 30 различных причин, в то время как есть миллионы различных имен.
  • Таблица разделена на основе диапазона дат.
+0

Я вижу различные SQL-решения, предлагаемые людьми. Спасибо за ответы. Но я не уверен, какой из них будет идеальным, поскольку таблица имеет миллионы строк. Будет ли использовать запрос «объяснять», чтобы рассказать мне лучший запрос? Или кто-нибудь знает, какой из ответов является лучшим, просто глядя на него? – user482594

+0

Запрос - все очень похожее и даст вам столько же времени. Попробуйте, план объяснений никогда не скажет вам, достаточно ли это! –

ответ

1
SELECT T.REASON, COUNT(*) 
FROM 
(
SELECT PERSON, MAX(DATE) AS MAX_DATE 
FROM TABLE-NAME 
GROUP BY PERSON 
) A, TABLE-NAME T 
WHERE T.PERSON = A.PERSON AND T.DATE = A.MAX_DATE 
GROUP BY T.REASON 
+0

Это должно быть 'A.MAX_DATE' в предложении' WHERE'. и его количество считается как для отдыха, так и для здоровья. –

+0

Ваш запрос дает значение «Больные 1 и Отпуск 2». Требуется 'Sick 2 Vacation 1' –

+0

@AjoKoshy Выполнено с предложением where. Я не думаю, что у вас будет два для vactaion, поскольку у одного только Джона есть отпуск, Джон. 2013-04-01 14:00:00 Отпуск – Santhosh

0

Попробуйте

select reason, count(*) from 
(select reason from table where date in 
    (select max(date) from table group by name)) t 
group by reason 
+0

Его подсчет числа как 2 как для отдыха, так и для больных –

+0

№ Проверить http://www.sqlfiddle.com/#!2/36551/13 это –

+0

Ваш запрос дает значение как «Больной 1 и отпуск 2». Требуется «Sick 2 Vacation 1' –

0

решение вы ищете, кажется, решена этим запросом:

select 
    reason, 
    count(*) 
from (select * from tablename group by name) abc 
group by 
    reason 

Это довольно быстро и просто. Вы можете посмотреть SQL Fiddle

+0

. Каково поведение по умолчанию, когда столбцы, не входящие в предложение 'group by', имеют разные значения? Например, вы используете 'group by' для имени только тогда, когда у человека разные даты и разные причины. Знаете ли вы, является ли поведение последовательным? – user482594

+0

@ user482594 поведение по умолчанию здесь просто помогает вам фильтровать количество повторений. Имя «group by» позволяет вам получить самую последнюю причину для каждого пользователя. И затем вы можете фильтровать результаты снова в соответствии с требованиями. Поведение совместимо во всех отношениях –

0

В MySQL это не очень эффективно делать такой запрос, так как у вас нет доступа к таким инструментам, как partitionning query in SQL Server или Oracle.
Вы все еще можете эмулировать его, делая подзапрос и получить строки, основанные на состоянии вам нужно, здесь максимальная дата:

SELECT t.reason, COUNT(1) 
FROM 
(
    SELECT name, MAX(adate) AS maxDate 
    FROM @aTable 
    GROUP BY name 
) maxDateRows 
    INNER JOIN @aTable t ON maxDateRows.name = t.name 
         AND maxDateRows.maxDate = t.adate 
GROUP BY t.reason 

You can see a sample here.
Тест этот запрос на ваши образцы, но я боюсь, что это будет медленно, как черт.

Для вашей информации, вы можете сделать то же самое в более элегантной и гораздо быстрее, как в SQL Server:

SELECT reason, COUNT(1) 
FROM 
(
    SELECT name 
      , reason 
      , RANK() OVER(PARTITION BY name ORDER BY adate DESC) as Rank 
    FROM @aTable 
    ) AS rankTable 
WHERE Rank = 1 
GROUP BY reason 

The sample is here

Если вы действительно прилипла к MySql, и первый запрос слишком медленный, тогда вы можете разделить проблему.

Сделайте первый запрос создания таблицы:

CREATE TABLE maxDateRows AS 
SELECT name, MAX(adate) AS maxDate 
FROM @aTable 
GROUP BY name 

Затем создать индекс как имя и MaxDate.
Наконец, получить результаты:

SELECT t.reason, COUNT(1) 
FROM maxDateRows m 
    INNER JOIN @aTable t ON m.name = t.name 
         AND m.maxDate = t.adate 
GROUP BY t.reason 
+0

Да .. Я застрял в MySQL на данный момент. Для вашего решения с 'create table ...' и 'select', могут ли эти 2 запроса объединиться в один? Я не думаю, что видел это раньше. Поэтому я просто прошу вас об этом. – user482594

0

Извиняюсь, если этот ответ дублирует существующий. Возможно, я страдаю от какой-то формы афазии, но я не вижу ее ...

SELECT x.reason 
    , COUNT(*) 
    FROM absentism x 
    JOIN 
    (SELECT name,MAX(date) max_date FROM absentism GROUP BY name) y 
    ON y.name = x.name 
    AND y.max_date = x.date 
GROUP 
    BY reason; 
Смежные вопросы