Выбор SQL из нескольких таблиц на основе даты и времени

Я работаю над сценарием для анализа некоторых данных, содержащихся в тысячах таблиц в базе данных SQL Server 2008.

Для простоты таблицы можно разбить на группы по 4-8 полусвязанных таблиц. Под полусвязанными я подразумеваю, что они представляют собой коллекции данных для одного и того же элемента, но не имеют фактического отношения SQL. Каждая таблица состоит из отметки даты и времени (тип данных datetime2), значения (может быть bit, int или float в зависимости от конкретного элемента) и некоторых других столбцов, которые в настоящее время не представляют интереса. Отметка даты и времени устанавливается каждые 15 минут (в четверть часа) в течение нескольких секунд; однако не все данные записываются точно в одно и то же время ...

Например:

ТАБЛИЦА 1:

TIMESTAMP                 VALUE
2014-11-27 07:15:00.390      1
2014-11-27 07:30:00.390      0
2014-11-27 07:45:00.373      0
2014-11-27 08:00:00.327      0

ТАБЛИЦА 2:

TIMESTAMP                 VALUE
2014-11-19 08:00:07.880      0
2014-11-19 08:15:06.867      0.0979999974370003
2014-11-19 08:30:08.593      0.0979999974370003
2014-11-19 08:45:07.397      0.0979999974370003

ТАБЛИЦА 3

TIMESTAMP                 VALUE
2014-11-27 07:15:00.390        0
2014-11-27 07:30:00.390        0
2014-11-27 07:45:00.373        1
2014-11-27 08:00:00.327        1

Как видите, не все таблицы начинаются с одного и того же квартального TIMESTAMP. По сути, мне нужен запрос, который будет возвращать ЗНАЧЕНИЕ для каждой из 3 таблиц для каждого 15-минутного интервала, начиная с самого раннего TIMESTAMP из 3 таблиц. Для приведенного примера я бы хотел начать в 2014-11-27 07:15 (не заботьтесь о секундах ... таким образом, необходимо разрешить метку времени быть + - 1 минуту или около того). Возврат NULL для значения, когда нет записи для конкретного TIMESTAMP, нормально. Итак, запрос для приведенного мной примера вернет что-то вроде:

TIMESTAMP                 VALUE1   VALUE2             VALUE3
2014-11-27 07:15           1    NULL                  0
2014-11-27 07:30           0    NULL                  0
2014-11-27 07:45           0    NULL                  1
2014-11-27 08:00           0    NULL                  1
...
2014-11-19 08:00           0         0                        1
2014-11-19 08:15           0         0.0979999974370003       0
2014-11-19 08:30           0         0.0979999974370003       0
2014-11-19 08:45           0         0.0979999974370003       0

Я надеюсь это имеет смысл. Любая помощь / указатели / рекомендации будут оценены.


person mkolker    schedule 19.12.2014    source источник


Ответы (3)


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

UPDATE TABLENAME
  SET TIMESTAMP = dateadd(minute,datediff(minute,0,TIMESTAMP),0)

или в новом столбце

ALTER TABLE TABLENAME ADD COLUMN NORMTIME DATETIME;

UPDATE TABLENAME
  SET NORMTIME = dateadd(minute,datediff(minute,0,TIMESTAMP),0)

Подробнее о датах напольных покрытий см. В этом сообщении: Установите дату на SQL-сервере


Следующим шагом является создание таблицы со всеми метками времени (нормализованными), которые вы ожидаете увидеть - то есть каждые 15 - по одной на строку. Давайте назовем эту таблицу TIME_PERIOD и столбец EVENT_TIME для моих примеров (называйте как хотите).

Есть много способов сделать такую ​​таблицу рекурсивной CTE, ROW_NUMBER (), даже грубой силой. Я оставляю это на ваше усмотрение.


Теперь проблема заключается в простом выборе с левыми соединениями и фильтром для допустимых значений, например:

SELECT TP.EVENT_TIME, a.VALUE as VALUE1, b.VALUE as VALUE2, c.VALUE as VALUE3
FROM  TIME_PERIOD TP
LEFT JOIN TABLE1 a ON a.[TIMESTAMP] = TP.EVENT_TIME
LEFT JOIN TABLE2 b ON b.[TIMESTAMP] = TP.EVENT_TIME
LEFT JOIN TABLE3 c ON c.[TIMESTAMP] = TP.EVENT_TIME
WHERE COALESCE(a.[TIMESTAMP], b.[TIMESTAMP], c.[TIMESTAMP]) is not null
ORDER  BY TP.EVENT_TIME DESC 

Если они разных типов, where может стать немного сложнее, поэтому вы всегда можете использовать это (что не так хорошо, как coalesce, но всегда будет работать):

WHERE a.[TIMESTAMP] IS NOT NULL OR
      b.[TIMESTAMP] IS NOT NULL OR
      c.[TIMESTAMP] IS NOT NULL
person Hogan    schedule 19.12.2014

Используйте 1_

SELECT COALESCE(a.[TIMESTAMP], b.[TIMESTAMP], c.[TIMESTAMP]) [TIMESTAMP],
       Isnull(Max(a.VALUE), 0)                               VALUE1,
       Max(b.VALUE)                                          VALUE2,
       Isnull(Max(c.VALUE), 0)                               VALUE3
FROM   TABLE1 a
       FULL OUTER JOIN TABLE2 b
                    ON CONVERT(SMALLDATETIME, a.[TIMESTAMP]) = CONVERT(SMALLDATETIME, b.[TIMESTAMP])
       FULL OUTER JOIN TABLE3 c
                    ON CONVERT(SMALLDATETIME, a.[TIMESTAMP]) = CONVERT(SMALLDATETIME, c.[TIMESTAMP])
GROUP  BY COALESCE(a.[TIMESTAMP], b.[TIMESTAMP], c.[TIMESTAMP])
ORDER  BY [TIMESTAMP] DESC 
person Pரதீப்    schedule 19.12.2014
comment
Временные метки имеют разные значения - они не будут объединяться простым = - person Hogan; 19.12.2014
comment
@Hogan - Да, ты прав, изначально я не понял, что ты говоришь. Обновил ответ - person Pரதீப்; 19.12.2014
comment
Да, исправлено, мне больше нравится мой ответ (думаю, будет быстрее), но это сработает. - person Hogan; 19.12.2014
comment
Спасибо! Я играл с полным внешним соединением, но все время натыкался на проблемы с соответствием дат. Спасибо вам за вашу помощь - person mkolker; 22.12.2014
comment
@mkolker - Если какой-либо из ответов удовлетворил вашу потребность, отметьте его как ответ, который поможет другим пользователям решить аналогичную проблему в будущем. - person Pரதீப்; 22.12.2014

Вот обновленная версия ответа NoDisplayName, которая делает то, что вы хотите. Он работает для SQL 2012, но вы можете заменить функцию DATETIMEFROMPARTS рядом других функций, чтобы получить тот же результат.

;WITH 
NewT1 as (
SELECT DATETimeFROMPARTS( DATEPART(year,Timestamp) , DATEPART(month,timestamp) , datepart(day,timestamp),datepart(hour,timestamp), datepart(minute,timestamp),0,0 ) as TimeStamp, Value
FROM Table1),
NewT2 as (
SELECT DATETimeFROMPARTS( DATEPART(year,Timestamp) , DATEPART(month,timestamp) , datepart(day,timestamp),datepart(hour,timestamp), datepart(minute,timestamp),0,0 ) as TimeStamp, Value
FROM Table2),
NewT3 as (
SELECT DATETimeFROMPARTS( DATEPART(year,Timestamp) , DATEPART(month,timestamp) , datepart(day,timestamp),datepart(hour,timestamp), datepart(minute,timestamp),0,0 ) as TimeStamp, Value
FROM Table3)
SELECT COALESCE(a.[TIMESTAMP], b.[TIMESTAMP], c.[TIMESTAMP]) [TIMESTAMPs],
       Isnull(Max(a.VALUE), 0)                            VALUE1,
       Isnull(Max(b.VALUE), 0)                                       VALUE2,
       Isnull(Max(c.VALUE), 0)                                       VALUE3
FROM   NewT1 a
       FULL OUTER JOIN NewT2 b
                    ON a.[TIMESTAMP] = b.[TIMESTAMP]
       FULL OUTER JOIN TABLE3 c
                    ON a.[TIMESTAMP] = b.[TIMESTAMP]
GROUP  BY COALESCE(a.[TIMESTAMP], b.[TIMESTAMP], c.[TIMESTAMP])
ORDER  BY [TIMESTAMPs] 
person LeeG    schedule 19.12.2014