问题
Suppose I have the following table in my SQL Server 2012 database:
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
...
And I want to get the average values for each sector for a specific FkId
, BUT BASED UPON THE TOTAL NUMBER OF DATES AVAILABLE IN TOTAL FOR THAT FkId. Meaning that if I wanted to get the average for FkId
= 1 for the dates, say, 2018-01-01
and 2018-01-10
my result set would be:
Sector AvgVal
---------------------------------
A (1+2+3+4) / 4 = 2.5
B (1+4) / 4 = 1.25
C (1+3+4) / 4 = 2
In other words, not dividing by the number of dates available for that sector, but divided by the total number of dates in the table for that date-range for that FkId
.
I figured I can do this with CTEs in the following way:
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
But I'm really hoping there is a better way. Any thoughts?
回答1:
Try this:
select dateCol,
fkid,
sector,
sum(value) over (partition by fkid, sector) /
(select count(distinct dateCol) from @MyTable where fkid = t.fkid)
from @MyTable t
or
select fkid,
sector,
sum(value) /
(select count(distinct dateCol) from @MyTable where fkid = t.fkid)
from @MyTable t
group by fkid, sector
回答2:
select *,
avg_val = AVG([Value]) over
(
partition by Sector, FkId
order by DateCol
range between unbounded preceding and unbounded following
)
from @MyTable;
回答3:
Is that what you need:
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;
Results:
+---------------------+------+--------+-------+--------+
| 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 |
+---------------------+------+--------+-------+--------+
来源:https://stackoverflow.com/questions/52044769/sql-server-aggregate-by-number-of-records-returned-for-all-groups