SQL Server - получить первое и последнее (по дате и времени) значение DataPoints - PullRequest
0 голосов
/ 13 мая 2018

У меня есть две таблицы в базе данных SQL Server 2016:

Метры:

    MeterId int [PK]
    Name varchar

Точки данных:

    DataPointId int [PK]
    DateTime datetimeoffset
    Value decimal
    MeterId int [FK -> Meters.MeterId]

Что мне нужно сделать, это прочитать первый и последний (самый старый и последний) DateTime и значение datapoint для каждого метра - таблица результатов должна выглядеть так:

Meter Name | First DateTime | First Value | Last DateTime | Last Value
-----------+----------------+-------------+---------------+-----------
Meter 1    | 2017-04-30...  | 233.55      |  2017-08-30...| 83.5 
Meter 2    | 2017-01-10...  | 11.12       |  2017-01-01...| 55.5

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

select
    Meters.Name as [Meter Name],
    min(DataPoints.DateTime) as [First DateTime],
    max(DataPoints.DateTime) as [Last DateTime]
from Meters
left join DataPoints on DataPoints.MeterId = Meters.MeterId
group by Meters.Name

Как изменить этот запрос, чтобы поля первого / последнего значения также были включены в результат

Ответы [ 2 ]

0 голосов
/ 13 мая 2018

Хорошо, я сделал это следующим образом, присоединив DataPoints дважды, как это:

SELECT Meters.Name, 
       t1.Value, 
       t1.DateTime, 
       t2.Value, 
       t2.DateTime
FROM 
    (SELECT DataPoints.MeterId, 
            MIN(DataPoints.DateTime) as FirstDateTime, 
            MAX(DataPoints.DateTime) as LastDateTime
     FROM DataPoints
     GROUP BY DataPoints.MeterId) as SubQuery
INNER JOIN DataPoints t1
    ON t1.DateTime = SubQuery.FirstDateTime AND t1.MeterId = SubQuery.MeterId 
INNER JOIN DataPoints t2
    ON t2.DateTime = SubQuery.LastDateTime AND t2.MeterId = SubQuery.MeterId
right join Meters on Meters.MeterId = DataPoints.MeterId

Это обеспечивает лучшую производительность.Таблица DataPoints имеет индексы, созданные на DataPointId и DateTime.

0 голосов
/ 13 мая 2018

Я думаю, это то, что вы хотите:

select distinct m.Name as MeterName,
       min(dp.DateTime) over (partition by m.name) as FirstDateTime,
       max(dp.DateTime) over (partition by m.name)  as LastDateTime,
       first_value(value) over (partition by m_name order by dp.datetime) as first_value,
       first_value(value) over (partition by m_name order by dp.datetime desc) as last_value
from dbo.Meters m left join
     dbo.DataPoints dp
     on dp.MeterId = m.MeterId;

К сожалению, first_value()last_value()) не являются функциями агрегирования. Это один из редких случаев, когда использование select distinct для агрегирования имеет смысл.

Для производительности вам нужны индексы на DataPonts(MeterId, datetime, value). Я бы также переписал условия как:

select distinct m.Name as MeterName,
       min(dp.DateTime) over (partition by dp.MeterId) as FirstDateTime,
       max(dp.DateTime) over (partition by dp.MeterId)  as LastDateTime,
       first_value(value) over (partition by dp.MeterId order by dp.datetime) as first_value,
       first_value(value) over (partition by dp.MeterId order by dp.datetime desc) as last_value
from dbo.Meters m left join
     dbo.DataPoints dp
     on dp.MeterId = m.MeterId;

Я почти уверен, что MeterId и MeterName являются синонимами. Это позволит коду использовать индекс.

Другой метод, который может работать с тем же индексом:

select m.*, firstdp.*, lastdp.*
from dbo.Meters m outer apply
     (select top (1) dp.datetime, dp.value
      from dbo.DataPoints dp
      where dp.MeterId = m.MeterId
      order by datetime asc
     ) firstdp outer apply
     (select top (1) dp.datetime, dp.value
      from dbo.DataPoints dp
      where dp.MeterId = m.MeterId
      order by datetime desc
     ) lastdp;

С индексом я бы не удивился, если бы он был довольно быстрым.

...