SQL Server - агрегирование по количеству записей, возвращаемых для всех групп - PullRequest
0 голосов
/ 27 августа 2018

Предположим, у меня есть следующая таблица в базе данных SQL Server 2012:

MyTable:

DateCol       FkId    Sector      Value
--------------------------------------------
2018-01-01     1        A            1
2018-01-02     1        A            2
2018-01-03     1        A            3
2018-01-04     1        A            4
2018-01-01     1        B            1
2018-01-04     1        B            4
2018-01-01     1        C            1
2018-01-03     1        C            3
2018-01-04     1        C            4
2018-01-01     2        A            1
...

И я хочу получить средние значения для каждого сектора для конкретного FkId, , НО НА ОСНОВЕ ОБЩЕГО ЧИСЛА ДАТ, ДОСТУПНЫХ ВСЕГО ДЛЯ ЭТОГО FkId . Это означает, что если бы я хотел получить среднее значение для FkId = 1 для дат, скажем, 2018-01-01 и 2018-01-10, мой набор результатов был бы:

Sector       AvgVal
---------------------------------
A            (1+2+3+4) / 4 = 2.5
B            (1+4) / 4 = 1.25
C            (1+3+4) / 4 = 2

Другими словами, не делить на число дат, доступных для этого сектора, а делить на общее число дат в таблице для этого диапазона дат для этого FkId.

Я подумал, что могу сделать это с CTE следующим образом:

DECLARE @FkId INT = 1,
        @StartDate DATE = '2018-01-01',
        @EndDate DATE = '2018-01-10'

DECLARE @MyTable TABLE
                 (
                     DateCol DATE,
                     FkId INT,
                     Sector VARCHAR(1),
                     Value FLOAT
                 );

INSERT INTO @MyTable (DateCol, FkId, Sector, Value)
VALUES
    ('2018-01-01', 1, 'A', 1),
    ('2018-01-02', 1, 'A', 2),
    ('2018-01-03', 1, 'A', 3),
    ('2018-01-04', 1, 'A', 4),

    ('2018-01-01', 1, 'B', 1),
    ('2018-01-04', 1, 'B', 4),

    ('2018-01-01', 1, 'C', 1),
    ('2018-01-03', 1, 'C', 3),
    ('2018-01-04', 1, 'C', 4),

    ('2018-01-01', 2, 'A', 1);

WITH NumDates AS
(
    SELECT
        Sector,
        COUNT(DateCol) AS cnt
    FROM
        @MyTable
    WHERE
        DateCol BETWEEN @StartDate AND @EndDate
        AND FkId = @FkId
    GROUP BY
        Sector
),
MaxNumDates AS
(
    SELECT
        MAX(cnt) AS MaxNum
    FROM
        NumDates
)
SELECT
    Sector,
    SUM(Value) / MaxNum AS AvgVal
FROM
    @MyTable
JOIN 
    MaxNumDates ON 1 = 1
WHERE
    DateCol BETWEEN @StartDate AND @EndDate
    AND FkId = @FkId
GROUP BY
    Sector, MaxNum

Но я действительно надеюсь, что есть лучший способ. Есть мысли?

Ответы [ 3 ]

0 голосов
/ 27 августа 2018

Это то, что вам нужно:

DECLARE
@MyTable TABLE
    (
         DateCol DATE
        ,FkId    INT
        ,Sector  VARCHAR(1)
        ,Value   FLOAT
    );

INSERT INTO @MyTable (DateCol, FkId, Sector, Value) VALUES
    ('2018-01-01', 1, 'A', 1),
    ('2018-01-02', 1, 'A', 2),
    ('2018-01-03', 1, 'A', 3),
    ('2018-01-04', 1, 'A', 4),
    ('2018-01-01', 1, 'B', 1),
    ('2018-01-04', 1, 'B', 4),
    ('2018-01-01', 1, 'C', 1),
    ('2018-01-03', 1, 'C', 3),
    ('2018-01-04', 1, 'C', 4),
    ('2018-01-01', 2, 'A', 1);

SELECT *, 
       SUM(Value) OVER (PARTITION BY Sector ORDER BY Sector ASC) / 
          (SELECT COUNT(DISTINCT DateCol) FROM @MyTable WHERE Fkid = Tbl.Fkid) AS Result
FROM @MyTable AS Tbl;

Результаты:

+---------------------+------+--------+-------+--------+
|        DateCol      | FkId | Sector | Value | Result |
+---------------------+------+--------+-------+--------+
| 01.01.2018 00:00:00 |    1 | A      |     1 |   2,75 |
| 02.01.2018 00:00:00 |    1 | A      |     2 |   2,75 |
| 03.01.2018 00:00:00 |    1 | A      |     3 |   2,75 |
| 04.01.2018 00:00:00 |    1 | A      |     4 |   2,75 |
| 01.01.2018 00:00:00 |    2 | A      |     1 |     11 |
| 01.01.2018 00:00:00 |    1 | B      |     1 |   1,25 |
| 04.01.2018 00:00:00 |    1 | B      |     4 |   1,25 |
| 01.01.2018 00:00:00 |    1 | C      |     1 |      2 |
| 03.01.2018 00:00:00 |    1 | C      |     3 |      2 |
| 04.01.2018 00:00:00 |    1 | C      |     4 |      2 |
+---------------------+------+--------+-------+--------+
0 голосов
/ 27 августа 2018
select *,
     avg_val = AVG([Value]) over
               (
                  partition by Sector, FkId
                  order by DateCol
                  range between unbounded preceding and unbounded following
               )
from @MyTable;
0 голосов
/ 27 августа 2018

Попробуйте это:

select dateCol,
       fkid, 
       sector,
       sum(value) over (partition by fkid, sector) /
       (select count(distinct dateCol) from @MyTable where fkid = t.fkid)
from @MyTable t

или

select fkid, 
       sector,
       sum(value) /
       (select count(distinct dateCol) from @MyTable where fkid = t.fkid)
from @MyTable t
group by fkid, sector
...