Макс в срок с дубликатами даты - PullRequest
1 голос
/ 28 октября 2010

У меня есть такая таблица

DateTime start_time not null,
DateTime end_time not null,
Status_Id int not null,
Entry_Id int not null

Я хочу получить счетчик каждого состояния в течение периода времени, когда для данного entry_id действителен только последний запущенный.

Что я сейчас использую, так это (с динамическими датами):

with c (Status_Id, Entry_Id, Start_Date) AS (
  select Status_Id, Entry_Id, Start_Date from tbl where
  (End_Date BETWEEN '19000101' AND '21000101')
  AND ((Start_Date BETWEEN '19000101' AND '21000101')
  OR End_Date <= '21000101'))
select Status_Id, count(*) as cnt from 
 (select Entry_Id, max(start_date) as start_date from c
  group by Entry_Id) d inner join
c on c.Entry_Id = d.Entry_Id
and c.start_date = d.start_date
GROUP BY Status_Id WITH ROLLUP

Проблема в том, что он считается неправильным, когда есть некоторые entry_id, которые имеют несколько записей одинаковой start_date (Меня не особо волнует, какой статус выбран в этом случае, просто выбран только 1)

Некоторые данные испытаний:

status_id   Entry_id    Start_date
496 45173   2010-09-29 18:04:33.000
490 45173   2010-09-29 18:48:20.100
495 45173   2010-09-29 19:25:29.300
489 45174   2010-09-29 18:43:01.500
493 45175   2010-09-29 18:48:00.500
493 45175   2010-09-29 21:16:02.700
489 45175   2010-09-30 17:52:12.100
493 45176   2010-09-29 17:55:21.300
492 45176   2010-09-29 18:20:52.200 <------ This is the one that gives the problems
493 45176   2010-09-29 18:20:52.200 <------ This is the one that gives the problems

Результат должен быть

495 1
489 2
492 1 (or 493 1)

Ответы [ 3 ]

2 голосов
/ 28 октября 2010

Если я правильно понял, вы хотите посчитать отдельную запись для определенного статуса в вашем периоде времени ... если это так, вы должны использовать предложение DISTINCT в вашем count(), переходя от count (*) кcount (отличный Entry_id)

with c (Status_Id, Entry_Id, Start_Date) AS (
  select Status_Id, Entry_Id, Start_Date from tbl where
  (End_Date BETWEEN '19000101' AND '21000101')
  AND ((Start_Date BETWEEN '19000101' AND '21000101')
  OR End_Date <= '21000101'))
select Status_Id, count(distinct Entry_Id) as cnt from 
 (select Entry_Id, max(start_date) as start_date from c
  group by Entry_Id) d inner join
c on c.Entry_Id = d.Entry_Id
and c.start_date = d.start_date
GROUP BY Status_Id WITH ROLLUP

EDIT

Пока вы не заботитесь о том, какой статус возвращается для данной записи, я думаю, вы можете изменить внутреннийзапросить возвращение первого статуса и присоединиться к нему тоже

with c (Status_Id, Entry_Id, Start_Date) AS (
  select Status_Id, Entry_Id, Start_Date from tbl where
  (End_Date BETWEEN '19000101' AND '21000101')
  AND ((Start_Date BETWEEN '19000101' AND '21000101')
  OR End_Date <= '21000101'))
select c.Status_Id, count(c.Entry_Id) as cnt from 
 (select Entry_Id, Start_Date, (select top 1 Status_id from c where Entry_Id = CC.Entry_Id and Start_Date = CC.Start_Date) as Status_Id
  from (select Entry_Id, max(start_date) as start_date from c
  group by Entry_Id) as CC) d inner join
c on c.Entry_Id = d.Entry_Id
and c.start_date = d.start_date
and c.status_id = d.status_id
GROUP BY c.Status_Id

Результат

Status_id Count
 489       2
 492       1
 495       1
1 голос
/ 29 октября 2010

Альтернативный ответ, основанный на прекрасных комментариях ОП.

WITH
   [sequenced_data]
AS
(
  SELECT
    *,
    ROW_NUMBER() OVER (PARTITION BY entry_id ORDER BY start_time DESC, status_id DESC) AS [sequence_id]
  FROM
    tbl
  WHERE
    start_time < '21:00' AND end_time > '19:00'
)
SELECT status_id, COUNT(*)
FROM [sequenced_data]
WHERE sequence_id = 1
GROUP BY status_id

Функция ROW_NUMBER () необходима только в том случае, если нет ни одного поля, которое может однозначно идентифицировать отдельные записи. Альтернативные запросы могут быть написаны там, где в данных есть уникальный столбец идентификаторов. SQL Server, однако, чрезвычайно эффективен для оптимизации запросов ROW_NUMBER (), таких как выше, и он должен (при условии соответствующих индексов) быть эффективными.

EDIT

Кто-то только что сказал мне, что людям не нравится длинный код, они предпочитают компактный код. Таким образом, версия CTE была заменена встроенной версией (CTE действительно просто помогли разбить запрос по понятным причинам и при необходимости находится в истории редактирования) ...

EDIT

ROW_NUMBER () не может являться частью предложения WHERE, как обнаружил OP. Запрос обновляется путем возврата одного CTE.

0 голосов
/ 29 октября 2010

Я сам нашел решение:

with c (Status_Id, Entry_Id, Start_Date) AS (
  select Status_Id, Entry_Id, Start_Date from tbl where
  (End_Date BETWEEN '19000101' AND '21000101')
  AND ((Start_Date BETWEEN '19000101' AND '21000101')
  OR End_Date <= '21000101'))
select Status_Id, count(*) as cnt from 
(select max(Status_Id) as Status_Id, c.Entry_Id from --<--- ADDED
 (select Entry_Id, max(start_date) as start_date from c
  group by Entry_Id) d inner join
c on c.Entry_Id = d.Entry_Id
and c.start_date = d.start_date
group by c.Entry_Id) y  --<--- ADDED
GROUP BY Status_Id WITH ROLLUP
...