У меня есть таблица (назовем ее audit
), которая выглядит так:
+--------------------------------------------------------------------------+
| id | recordId | status | mdate | type | relatedId |
+--------------------------------------------------------------------------+
| 1 | 3006 | A | 2013-04-03 23:59:01.275 | type1 | 1 |
| 2 | 3025 | B | 2013-04-04 00:00:02.134 | type1 | 1 |
| 3 | 4578 | A | 2013-04-04 00:04:30.033 | type2 | 1 |
| 4 | 7940 | C | 2013-04-04 00:04:32.683 | type1 | <NULL> |
| 5 | 3006 | D | 2013-04-04 00:04:32.683 | type1 | <NULL> |
| 6 | 4822 | E | 2013-04-04 00:04:32.683 | type2 | <NULL> |
| 7 | 3006 | A | 2013-04-04 00:06:54.033 | type1 | 2 |
| 8 | 3025 | C | 2013-04-04 00:06:54.033 | type1 | 2 |
... и так далее для миллионов строк. И еще одна таблица, которую мы назовем related
:
+-------------+
| id | source |
+-------------+
| 1 | src_X |
| 2 | src_Y |
| 3 | src_Z |
| 4 | src_X |
| 5 | src_X |
... и так далее для сотен тысяч строк.
В обеих таблицах больше столбцов, но это все, что нам нужно для описания проблемы. Столбец relatedId
присоединяется к таблице related
. recordId
также соединяется с другой таблицей, и в audit
будет несколько записей с одним и тем же recordId
.
Я пытаюсь создать запрос, который выдаст следующий результат:
+-----------------+
| source | count |
+-----------------+
| src_X | 1643 |
| src_Y | 255 |
| NULL | 729 |
+-----------------+
Подсчет представляет собой количество записей в audit
, которые имеют данный type
(например, "type1"
) и находятся в пределах набора статусов (например, "A", "B", "C"
), которые затем оставляются внешними, присоединяются к related
и группируются по source
.
Загвоздка в том, что я хочу включать только записи из audit
, которые находятся в определенном диапазоне дат, и я также хочу объединить только от audit
до related
самой старой записи в этом диапазоне для каждого recordId
. Кроме того, я хочу игнорировать любые записи, соответствующие критериям type
и status
, но имеющие запись для того же recordId
, которая старше диапазона дат.
Итак, чтобы уточнить данные из приведенного выше примера: допустим, мне нужен тип type1
и значения статуса "A", "B", "C"
с диапазоном дат от 2013-04-04
до 2013-04-05
. Ряды 2 и 4 будут включены в подсчет. Строка 3 исключена, так как содержит неверный type
. Строка 5 исключена, так как статус неверен. Строка 6 исключена, поскольку и статус, и тип неверны. Строка 1 исключена, поскольку она находится за пределами диапазона дат. Строка 7 также исключается, так как есть еще одна строка (строка 1), которая соответствует критериям статуса и типа с тем же recordId
, который старше начала диапазона дат. Строка 8 исключается, так как и строка 8, и строка 2 имеют одинаковые recordId
и соответствуют критериям, но мы учитываем только самую старую запись из двух в пределах диапазона.
Другими словами, я хочу подсчитывать только первый раз, когда запись для данного RecordId появляется в таблице и находится в пределах целевого диапазона дат.
Мы придумали следующее:
WITH data (recordId, id) AS (
SELECT a.recordId, MIN(a.id)
FROM audit a
WHERE a.status in ('A','B','C')
AND type = 'type1'
GROUP BY a.recordId
)
SELECT r.source, COUNT(*)
FROM data d
JOIN audit a ON d.id = a.id
LEFT JOIN related r ON a.relatedId = r.id
WHERE a.mdate >= '2013-04-04 00:00:00.000'
and a.mdate < '2013-04-05 00:00:00.000'
GROUP BY r.source
Это будет выполняться на MSSQL Server 2008 и в настоящее время основано на том факте, что идентификаторы таблиц аудита генерируются автоматически. Поскольку идентификаторы генерируются в момент вставки записи, а mdate также является отметкой времени вставки, а записи никогда не обновляются после вставки, я думаю, что это нормально. Похоже, что запрос дает правильный результат на ограниченном наборе тестовых данных, но я надеялся на второе мнение.
- Этот запрос выглядит нормально?
- Можно ли улучшить его работу?
AND a.mdate < '2013-04-05 00:00:00.000'
к вычисляемой таблице поможет ограничить количество возвращаемых записей. - person dave.c   schedule 26.06.2013