2014-07-28 2 views
1

У меня есть этот запрос, который вычисляет СРЕДНЕМ для измерений днем ​​ теперь я хотел бы иметь для каждой строки в СРЕДНЕМ со всех ближайших 5 дней с этогоПолучить среднее в течение следующих 5 строк

ох .. . Я использую Sql Server

Вот что я имею

SELECT Cast(Cast (Datepart(year, Dateadd(minute, (a.quarternumber * 15), 
      '2000-01-01')) 
        AS 
      VARCHAR(4)) 
      + '-' 
      + Cast (Datepart(month, Dateadd(minute, (a.quarternumber * 15), 
        '2000-01-01')) 
      AS VARCHAR(4)) 
      + '-' 
      + Cast (Datepart(day, Dateadd(minute, (a.quarternumber * 15), 
      '2000-01-01') 
        )AS 
      VARCHAR(4)) 
      + ' ' AS DATETIME) AS [TimeStamp], 
     --AVG(a.value) over(order by a.value) as exper, 
     Round(Avg(a.value), 2) AS Value 
FROM measurements.archive a 
     INNER JOIN measurements.points p 
       ON a.pointid = p.id 
     INNER JOIN fifthcore..cm_lod_devices ld 
       ON ld.uuid = p.logicaldeviceuuid 
WHERE ld.id IN (SELECT value 
       FROM @LodDeviceIds) 
     AND p.name = @Name 
     AND a.quarternumber BETWEEN @ChartBeginNumber AND @ChartEndNumber 
GROUP BY Datepart(year, Dateadd(minute, (a.quarternumber * 15), '2000-01-01') 
), 
      Datepart(month, Dateadd(minute, (a.quarternumber * 15), '2000-01-01' 
         )), 
      Datepart(day, Dateadd(minute, (a.quarternumber * 15), '2000-01-01')) 
ORDER BY Datepart(day, Dateadd(minute, (a.quarternumber * 15), '2000-01-01')) 

Я WHAS глядя в нечто подобное, но просто не могу заставить его работать

AVG(y) OVER(ORDER BY x 
3 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) 


Table        :extra column that would avg the result of next five days 
2014-07-01 00:00:00.000 16.780000 --> 15.8 
2014-07-02 00:00:00.000 15.940000 --> 16 
2014-07-03 00:00:00.000 16.790000 
2014-07-04 00:00:00.000 16.790000 
2014-07-05 00:00:00.000 16.040000 
2014-07-06 00:00:00.000 16.500000 
2014-07-07 00:00:00.000 16.790000 
2014-07-08 00:00:00.000 16.790000 
2014-07-09 00:00:00.000 16.790000 
+2

Какие СУБД вы используете? –

+0

ah, im using SQL Server –

+0

Я отметил ваш вопрос соответствующим образом. Когда вы задаете будущие вопросы, добавьте тег для своих РСУБД в дополнение к тегу SQL (так как ответы часто зависят от поставщика). –

ответ

2

Я думаю, ваша проблема в том, что вы используете AVG() С предложением OVER неправильно.

Вы используете GROUP BY - и поэтому AVG() в том же запросе будет применяться к группе, а не ко всему набору данных, поэтому вы не можете использовать стандартную оконную версию SQL SERVER 2012 без предварительного подзапроса или CTE.

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

WITH cte AS (
    SELECT Cast(Cast (Datepart(year, Dateadd(minute, (a.quarternumber * 15), 
       '2000-01-01')) 
         AS 
       VARCHAR(4)) 
       + '-' 
       + Cast (Datepart(month, Dateadd(minute, (a.quarternumber * 15), 
         '2000-01-01')) 
       AS VARCHAR(4)) 
       + '-' 
       + Cast (Datepart(day, Dateadd(minute, (a.quarternumber * 15), 
       '2000-01-01') 
         )AS 
       VARCHAR(4)) 
       + ' ' AS DATETIME) AS [TimeStamp], 
      Round(Avg(a.value), 2) AS Value 
      SUM (a.value) AS ValueSum, -- New item - required for calculating windowed average from group 
      COUNT(a.value) AS ValueCount -- New item - required for calculating windowed average from group 
    FROM measurements.archive a 
      INNER JOIN measurements.points p 
        ON a.pointid = p.id 
      INNER JOIN fifthcore..cm_lod_devices ld 
        ON ld.uuid = p.logicaldeviceuuid 
    WHERE ld.id IN (SELECT value 
        FROM @LodDeviceIds) 
      AND p.name = @Name 
      AND a.quarternumber BETWEEN @ChartBeginNumber AND @ChartEndNumber 
    GROUP BY Datepart(year, Dateadd(minute, (a.quarternumber * 15), '2000-01-01') 
    ), 
       Datepart(month, Dateadd(minute, (a.quarternumber * 15), '2000-01-01' 
          )), 
       Datepart(day, Dateadd(minute, (a.quarternumber * 15), '2000-01-01')) 
    ORDER BY Datepart(day, Dateadd(minute, (a.quarternumber * 15), '2000-01-01')) 
) 
SELECT 
    [TimeStamp], 
    Value, 
    SUM(ValueSum) OVER (
     ORDER BY [TimeStamp] ROWS BETWEEN CURRENT ROW AND 5 FOLLOWING 
    )/
    SUM(ValueCount) OVER (
     ORDER BY [TimeStamp] ROWS BETWEEN CURRENT ROW AND 5 FOLLOWING 
    ) AS ValueOverNext5 
    -- Average = SUM(x)/COUNT(x) = SUM(SUM(group))/SUM(COUNT(group)) 
FROM cte 

Как вы можете видеть, что я поставил запрос внутри общего табличного выражения, и добавил 2 новые колонки для SUM и COUNT. Затем я применил оконный AVG OVER к этим элементам (выполнив SUM (total) OVER (...)/SUM (count) OVER (...) - прямой AVG из уже усредненных значений из CTE будет неправильным)

Надеюсь, это имеет смысл и работает!

1

Это если вы SQL Server 2008 Первая часть просто делает тестовые данные:

DECLARE @MyTable TABLE 
(
    MyDate DATETIME, 
    Value DECIMAL(19,6) 
) 

INSERT INTO @MyTable 
VALUES 
('2014-07-01 00:00:00.000',16.780000), 
('2014-07-02 00:00:00.000',15.940000), 
('2014-07-03 00:00:00.000',16.790000), 
('2014-07-04 00:00:00.000',16.790000), 
('2014-07-05 00:00:00.000',16.040000), 
('2014-07-06 00:00:00.000',16.500000), 
('2014-07-07 00:00:00.000',16.790000), 
('2014-07-08 00:00:00.000',16.790000), 
('2014-07-09 00:00:00.000',16.790000); 

Теперь я создаю КТР заказ мои данные и дать ему RowNumber.

WITH SortedData AS 
(
    SELECT *, 
    ROW_NUMBER() OVER (ORDER BY MyDate) RN 
    FROM @MyTable 
) 

Теперь, используя мой CTE, я получаю следующие 4 строки в avg.

SELECT 
CAST(t1.MyDate AS DATE) AS MyDate, 
t1.Value, 
t1.RN, 
d.FCNT, 
d.FSUM, 
d.FAVG 
FROM SortedData T1 
OUTER APPLY 
(
    SELECT 
     SUM(T2.VALUE) FSUM, 
     COUNT(1) FCNT, 
     AVG(T2.Value) FAVG 
    FROM SortedData T2 
    WHERE T2.RN >= T1.RN 
     AND T2.RN <= T1.RN + 4 
) d 
ORDER BY T1.RN 

Вот результат:

MyDate Value RN FCNT FSUM FAVG 
2014-07-01 16.780000 1 5 82.340000 16.468000 
2014-07-02 15.940000 2 5 82.060000 16.412000 
2014-07-03 16.790000 3 5 82.910000 16.582000 
2014-07-04 16.790000 4 5 82.910000 16.582000 
2014-07-05 16.040000 5 5 82.910000 16.582000 
2014-07-06 16.500000 6 4 66.870000 16.717500 
2014-07-07 16.790000 7 3 50.370000 16.790000 
2014-07-08 16.790000 8 2 33.580000 16.790000 
2014-07-09 16.790000 9 1 16.790000 16.790000 

Для SQL Server 2012 это может быть так просто, как это: Позволяет изменить вверх тестовые данные и положить еще несколько строк для каждой даты:

DECLARE @MyTable TABLE 
(
    MyDate DATETIME, 
    Value DECIMAL(19,6) 
) 

INSERT INTO @MyTable 
VALUES 
('2014-07-01 00:00:00.000',1.0), 
('2014-07-01 00:00:00.000',2.0), 
('2014-07-01 00:00:00.000',3.0), 
('2014-07-01 00:00:00.000',4.0), 
('2014-07-02 00:00:00.000',7.0), 
('2014-07-02 00:00:00.000',7.0), 
('2014-07-02 00:00:00.000',7.0), 
('2014-07-03 00:00:00.000',8.0), 
('2014-07-04 00:00:00.000',9.0), 
('2014-07-05 00:00:00.000',10.0), 
('2014-07-06 00:00:00.000',11.0), 
('2014-07-07 00:00:00.000',20.0), 
('2014-07-08 00:00:00.000',25.0), 
('2014-07-09 00:00:00.000',50.0); 

Теперь позволяет сделать нашу CTE группу и AVG данные:

WITH SortedData AS 
(
    SELECT 
     MyDate, 
     AVG(VALUE) DayAvg, 
     ROW_NUMBER() OVER (ORDER BY MyDate) RN, 
     AVG(AVG(VALUE)) OVER (ORDER BY MyDate ROWS between current row and 4 following) FDAVG 
    FROM @MyTable 
    GROUP BY MyDate 
) 

SELECT CAST(sd.MyDate AS DATE) AS MyDate, 
    sd.DayAvg, 
    sd.RN, 
    sd.FDAVG 
FROM SortedData sd 

Вот результат:

MyDate DayAvg RN FDAVG 
2014-07-01 2.500000 1 7.300000 
2014-07-02 7.000000 2 9.000000 
2014-07-03 8.000000 3 11.600000 
2014-07-04 9.000000 4 15.000000 
2014-07-05 10.000000 5 23.200000 
2014-07-06 11.000000 6 26.500000 
2014-07-07 20.000000 7 31.666666 
2014-07-08 25.000000 8 37.500000 
2014-07-09 50.000000 9 50.000000 
+0

Это отличный ответ, но можно ли пропустить строки вместо них? –

+0

да, например, если бы вы хотели следующие 3 строки ПОСЛЕ текущей строки, вы могли бы добавить AVG (AVG (VALUE)) OVER (ORDER BY MyDate ROWS между 1 последующим и 3 последующими), но последняя строка вернет значение null, t - любые строки после него в avg. –

Смежные вопросы