计算预定义数量的值的平均值和标准偏差,用零替换缺失的行
Calculate average and standard deviation for pre defined number of values substituting missing rows with zeros
我有一个简单的 table,其中包含产品记录及其一年中每天的总销售额(只有 3 列 - 产品、日期、销售额)。因此,例如,如果产品 A 每天都售出,它将有 365
条记录。同样,如果产品 B 仅售出 50 天,table 将只有 50
行表示该产品 - 每一天销售一列。
我需要计算全年的每日平均销售额和标准差,这意味着,对于产品 B,我需要额外的 365-50=315
个销售额为零的条目才能计算每日平均销售额当年的平均值和标准偏差正确。
有没有办法在 SQL 中高效、动态地执行此操作?
谢谢
找到一年的第一天和最后一天,然后使用 datediff()
找到那一年的天数。
之后不要在销售中使用 AVG
,而是 SUM(Sales) / days_in_year
select *,
days_in_year = datediff(day, first_of_year, last_of_year) + 1
from (values (2019), (2020)) v(year)
cross apply
(
select first_of_year = dateadd(year, year - 1900, 0),
last_of_year = dateadd(year, year - 1900 + 1, -1)
) d
有不同的方式来看待它 - 不要试图添加额外的空行,只需除以一年中的天数。虽然一年的天数不是固定的(闰年有 366 天),但可以很容易地计算出来,因为一年的第一天总是 1 月 1 日,最后一天总是 12 月 31 日:
SELECT YEAR(date),
product,
SUM(sales) / DATEPART(dy, DATEFROMPARTS(YEAR(date)), 12, 31))
FROM sales_table
GROUP BY YEAR(date), product
我们可以生成 366 行并将销售数据加入其中:
WITH rg(rn) AS (
SELECT 1 AS rn
UNION ALL
SELECT a.rn + 1 AS rn
FROM rg a
WHERE a.rn <= 366
)
SELECT
*
FROM
rg
LEFT JOIN (
SELECT YEAR(saledate) as yr, DATEPART(dayofyear, saledate) as doy, count(*) as numsales
FROM sales
GROUP BY YEAR(saledate), DATEPART(dayofyear, saledate)
) s ON rg.rn = s.doy
OPTION (MAXRECURSION 370);
您可以将空值(当天没有销售数据)替换为例如AVG(COALESCE(numsales, 0))
。您可能还需要一个 WHERE 子句来消除非闰年的第 366 天(例如 MODULO 年份为 4,如果为 0,则只执行 366 行)。
如果只做一年,可以在sales子查询中使用where子句只给出相关记录;最有效的方法是使用 WHERE salesdate >= DATEFROMPARTS(YEAR(GetDate()), 1, 1) AND salesdate < DATEFROMPARTS(YEAR(GetDate()) + 1, 1, 1)
之类的范围,而不是在每个销售日期调用一个函数来从中提取年份以与常量进行比较。如果只有一年
,您还可以从 select/group 中删除 YEAR(salesdate)
如果你做了很多年,你可以让 rg 生成更多行,或者(也许更简单)将它交叉连接到一个年份列表,这样你就可以得到 366 行乘以例如VALUES (2015),(2016),(2017),(2018),(2019),(2020)
(并从加入的销售部分中得出年份)
我有一个简单的 table,其中包含产品记录及其一年中每天的总销售额(只有 3 列 - 产品、日期、销售额)。因此,例如,如果产品 A 每天都售出,它将有 365
条记录。同样,如果产品 B 仅售出 50 天,table 将只有 50
行表示该产品 - 每一天销售一列。
我需要计算全年的每日平均销售额和标准差,这意味着,对于产品 B,我需要额外的 365-50=315
个销售额为零的条目才能计算每日平均销售额当年的平均值和标准偏差正确。
有没有办法在 SQL 中高效、动态地执行此操作?
谢谢
找到一年的第一天和最后一天,然后使用 datediff()
找到那一年的天数。
之后不要在销售中使用 AVG
,而是 SUM(Sales) / days_in_year
select *,
days_in_year = datediff(day, first_of_year, last_of_year) + 1
from (values (2019), (2020)) v(year)
cross apply
(
select first_of_year = dateadd(year, year - 1900, 0),
last_of_year = dateadd(year, year - 1900 + 1, -1)
) d
有不同的方式来看待它 - 不要试图添加额外的空行,只需除以一年中的天数。虽然一年的天数不是固定的(闰年有 366 天),但可以很容易地计算出来,因为一年的第一天总是 1 月 1 日,最后一天总是 12 月 31 日:
SELECT YEAR(date),
product,
SUM(sales) / DATEPART(dy, DATEFROMPARTS(YEAR(date)), 12, 31))
FROM sales_table
GROUP BY YEAR(date), product
我们可以生成 366 行并将销售数据加入其中:
WITH rg(rn) AS (
SELECT 1 AS rn
UNION ALL
SELECT a.rn + 1 AS rn
FROM rg a
WHERE a.rn <= 366
)
SELECT
*
FROM
rg
LEFT JOIN (
SELECT YEAR(saledate) as yr, DATEPART(dayofyear, saledate) as doy, count(*) as numsales
FROM sales
GROUP BY YEAR(saledate), DATEPART(dayofyear, saledate)
) s ON rg.rn = s.doy
OPTION (MAXRECURSION 370);
您可以将空值(当天没有销售数据)替换为例如AVG(COALESCE(numsales, 0))
。您可能还需要一个 WHERE 子句来消除非闰年的第 366 天(例如 MODULO 年份为 4,如果为 0,则只执行 366 行)。
如果只做一年,可以在sales子查询中使用where子句只给出相关记录;最有效的方法是使用 WHERE salesdate >= DATEFROMPARTS(YEAR(GetDate()), 1, 1) AND salesdate < DATEFROMPARTS(YEAR(GetDate()) + 1, 1, 1)
之类的范围,而不是在每个销售日期调用一个函数来从中提取年份以与常量进行比较。如果只有一年
如果你做了很多年,你可以让 rg 生成更多行,或者(也许更简单)将它交叉连接到一个年份列表,这样你就可以得到 366 行乘以例如VALUES (2015),(2016),(2017),(2018),(2019),(2020)
(并从加入的销售部分中得出年份)