参数月份选择:进行查询,显示上个月、12个月前和最近12个月的平均值

发布于 2024-11-28 04:18:58 字数 2278 浏览 1 评论 0原文

我想玩一下我的 Total_Sales 表。 这就是数据的样子(使用 SQL Server 2008 R2)

Name    Year  Month  Sales
------  ----  -----  -----
Alfred  2011  1      100
Alfred  2011  2      200
Alfred  2011  3      300
Alfred  2011  4      400
Alfred  2011  5      500
Alfred  2011  6      600
Alfred  2011  7      700
Alfred  2011  8      800
Alfred  2011  9      900
Alfred  2011  10     500
Alfred  2011  11     500
Alfred  2011  12     500

我想要创建的 SQL 查询应显示如下数据:

Name    Year  Month  Sales Prev_Month Month_Last_Year_Sales Last_12_Month_AVG
------  ----  -----  ----- ---------- --------------------- -----------------
Alfred  2011  1      100   NULL       (year 2010, month 1)  (2010_01 to 2011_01)/(12)
Alfred  2011  2      200   100        (year 2010, month 2)  (2010_02 to 2011_02)/(12)
Alfred  2011  3      300   200        (year 2010, month 3)  (2010_03 to 2011_03)/(12)
Alfred  2011  4      400   300        (year 2010, month 4)  (2010_04 to 2011_04)/(12)
Alfred  2011  5      500   400        (year 2010, month 5)  (2010_05 to 2011_05)/(12)
Alfred  2011  6      600   500        (year 2010, month 6)  (2010_06 to 2011_06)/(12)
Alfred  2011  7      700   600        (year 2010, month 7)  (2010_07 to 2011_07)/(12)
Alfred  2011  8      800   700        (year 2010, month 8)  (2010_08 to 2011_08)/(12)
Alfred  2011  9      900   800        (year 2010, month 9)  (2010_09 to 2011_09)/(12)
Alfred  2011  10     500   900        (year 2010, month 10) (2010_10 to 2011_10)/(12)
Alfred  2011  11     500   500        (year 2010, month 11) (2010_11 to 2011_11)/(12)
Alfred  2011  12     500   500        (year 2010, month 12) (2010_12 to 2011_12)/(12)

要复制上个月,我使用的是: 复制上个月值并插入新行

SELECT
TS.name,
TS.year,
TS.month,
TS.sales,
COALESCE(TS2.sales, 0) AS prior_month_sales
FROM
TotalSales TS
LEFT OUTER JOIN TotalSales TS2 ON
TS2.name = TS.name AND
(
    (TS2.year = TS.year AND TS2.month = TS.month - 1) OR
    (TS.month = 1 AND TS2.month = 12 AND TS2.year = TS.year - 1)
)

Prev_Month 中的 NULL 表示该月份的开始Total_Sales 位于 2011 年 1 月,因此此示例没有先前数据。

我计划使用一个参数,您可以在其中选择一个月。
感谢您的帮助!

I wanted to play around with my Total_Sales table.
This is how the data looks like (using SQL Server 2008 R2)

Name    Year  Month  Sales
------  ----  -----  -----
Alfred  2011  1      100
Alfred  2011  2      200
Alfred  2011  3      300
Alfred  2011  4      400
Alfred  2011  5      500
Alfred  2011  6      600
Alfred  2011  7      700
Alfred  2011  8      800
Alfred  2011  9      900
Alfred  2011  10     500
Alfred  2011  11     500
Alfred  2011  12     500

The SQL query I want to create should display the data like this:

Name    Year  Month  Sales Prev_Month Month_Last_Year_Sales Last_12_Month_AVG
------  ----  -----  ----- ---------- --------------------- -----------------
Alfred  2011  1      100   NULL       (year 2010, month 1)  (2010_01 to 2011_01)/(12)
Alfred  2011  2      200   100        (year 2010, month 2)  (2010_02 to 2011_02)/(12)
Alfred  2011  3      300   200        (year 2010, month 3)  (2010_03 to 2011_03)/(12)
Alfred  2011  4      400   300        (year 2010, month 4)  (2010_04 to 2011_04)/(12)
Alfred  2011  5      500   400        (year 2010, month 5)  (2010_05 to 2011_05)/(12)
Alfred  2011  6      600   500        (year 2010, month 6)  (2010_06 to 2011_06)/(12)
Alfred  2011  7      700   600        (year 2010, month 7)  (2010_07 to 2011_07)/(12)
Alfred  2011  8      800   700        (year 2010, month 8)  (2010_08 to 2011_08)/(12)
Alfred  2011  9      900   800        (year 2010, month 9)  (2010_09 to 2011_09)/(12)
Alfred  2011  10     500   900        (year 2010, month 10) (2010_10 to 2011_10)/(12)
Alfred  2011  11     500   500        (year 2010, month 11) (2010_11 to 2011_11)/(12)
Alfred  2011  12     500   500        (year 2010, month 12) (2010_12 to 2011_12)/(12)

To copy the prior month I am using this: Copy prior month value and insert into new row

SELECT
TS.name,
TS.year,
TS.month,
TS.sales,
COALESCE(TS2.sales, 0) AS prior_month_sales
FROM
TotalSales TS
LEFT OUTER JOIN TotalSales TS2 ON
TS2.name = TS.name AND
(
    (TS2.year = TS.year AND TS2.month = TS.month - 1) OR
    (TS.month = 1 AND TS2.month = 12 AND TS2.year = TS.year - 1)
)

The NULL in Prev_Month is to show that the start of the Total_Sales was in year 2011 month 1, so no prior data for this example.

I am planning to use a parameter, where you select a month.

Thanks for any help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

油焖大侠 2024-12-05 04:18:58
SELECT
  [this_month].*,
  [last_month].Sales        AS [prev_month_sales],
  [last_year].Sales         AS [month_last_year_sales],
  [yearly].AverageSales     AS [last_12_month_average]
FROM
  Total_Sales     AS [this_month]
LEFT JOIN
  Total_Sales     AS [last_month]
    ON  [last_month].Name = [this_month].Name
    AND (
         ([last_month].Year = [this_month].Year     AND [last_month].Month = [this_month].Month - 1)
      OR ([last_month].Year = [this_month].Year - 1 AND [last_month].Month = 12 AND [this_month].Month = 1)
    )
LEFT JOIN
  TotalSales     AS [last_year]
    ON  [last_year].Name  = [this_month].Name
    AND [last_year].Year  = [this_month].Year - 1
    AND [last_year].Month = [this_month].Month
CROSS APPLY
(
  SELECT
    AVG(Sales) AS AverageSales
  FROM
    Total_Sales
  WHERE
    Name = [this_month].Name
    AND (
            (Year = [this_month].Year     AND Month <= [this_month].Month)
         OR (Year = [this_month].Year - 1 AND Month >  [this_month].Month)
    )
)
  AS [yearly]

平均值不是除以 12 的值,因为上一年并不总是有 12 个月的数据。但 AVG() 函数会为您处理这个问题。

另外,我强烈建议不要使用 YEAR 和 MONTH 字段。相反,我建议使用 DATETIME 字段来表示“月份开始”并使用 SQL Server 的日期函数...

Last Month : MonthStart = DATEADD(MONTH, -1, ThisMonth)
A Year Ago : MonthStart = DATEADD(YEAR,  -1, ThisMonth)
Last Year  : MonthStart > DATEADD(YEAR,  -1, ThisMonth) AND MonthStart <= ThisMonth
SELECT
  [this_month].*,
  [last_month].Sales        AS [prev_month_sales],
  [last_year].Sales         AS [month_last_year_sales],
  [yearly].AverageSales     AS [last_12_month_average]
FROM
  Total_Sales     AS [this_month]
LEFT JOIN
  Total_Sales     AS [last_month]
    ON  [last_month].Name = [this_month].Name
    AND (
         ([last_month].Year = [this_month].Year     AND [last_month].Month = [this_month].Month - 1)
      OR ([last_month].Year = [this_month].Year - 1 AND [last_month].Month = 12 AND [this_month].Month = 1)
    )
LEFT JOIN
  TotalSales     AS [last_year]
    ON  [last_year].Name  = [this_month].Name
    AND [last_year].Year  = [this_month].Year - 1
    AND [last_year].Month = [this_month].Month
CROSS APPLY
(
  SELECT
    AVG(Sales) AS AverageSales
  FROM
    Total_Sales
  WHERE
    Name = [this_month].Name
    AND (
            (Year = [this_month].Year     AND Month <= [this_month].Month)
         OR (Year = [this_month].Year - 1 AND Month >  [this_month].Month)
    )
)
  AS [yearly]

The Average isn't the value divided by 12, as there are not always 12 months worth of data in the preceding year. But the AVG() function takes care of that for you.

Also, I'd highly reccomend against using YEAR and MONTH fields. Instead I would recommend using a DATETIME field to represent the "Month Start" and using SQL Server's Date functions...

Last Month : MonthStart = DATEADD(MONTH, -1, ThisMonth)
A Year Ago : MonthStart = DATEADD(YEAR,  -1, ThisMonth)
Last Year  : MonthStart > DATEADD(YEAR,  -1, ThisMonth) AND MonthStart <= ThisMonth
成熟稳重的好男人 2024-12-05 04:18:58

我不知道另一个答案是否更快......

WITH sales AS (
  SELECT
    ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Year, Month) AS month_id,
    *
  FROM
    yearly_sales
)
SELECT
  Name       = [this_month].Name,
  Year       = MAX([this_month].Year),
  Month      = MAX([this_month].Month),
  Sales      = MAX([this_month].Sales),
  Last_Month = MAX(CASE WHEN [13_months].month_id = [this_month].month_id - 1  THEN [13_months].Sales END),
  Last_Year  = MAX(CASE WHEN [13_months].month_id = [this_month].month_id - 12 THEN [13_months].Sales END),
  Yearly_AVG = AVG(CASE WHEN [13_months].month_id > [this_month].month_id - 12 THEN [13_months].Sales END)
FROM
  Sales        AS [this_month]
INNER JOIN
  Sales        AS [13_months]
    ON  [13_months].Name      = [this_month].Name
    AND [13_months].month_id <= [this_month].month_id
    AND [13_months].month_id >= [this_month].month_id - 12
GROUP BY
  [this_month].Name

Another answer that I have no idea is faster or not...

WITH sales AS (
  SELECT
    ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Year, Month) AS month_id,
    *
  FROM
    yearly_sales
)
SELECT
  Name       = [this_month].Name,
  Year       = MAX([this_month].Year),
  Month      = MAX([this_month].Month),
  Sales      = MAX([this_month].Sales),
  Last_Month = MAX(CASE WHEN [13_months].month_id = [this_month].month_id - 1  THEN [13_months].Sales END),
  Last_Year  = MAX(CASE WHEN [13_months].month_id = [this_month].month_id - 12 THEN [13_months].Sales END),
  Yearly_AVG = AVG(CASE WHEN [13_months].month_id > [this_month].month_id - 12 THEN [13_months].Sales END)
FROM
  Sales        AS [this_month]
INNER JOIN
  Sales        AS [13_months]
    ON  [13_months].Name      = [this_month].Name
    AND [13_months].month_id <= [this_month].month_id
    AND [13_months].month_id >= [this_month].month_id - 12
GROUP BY
  [this_month].Name
雄赳赳气昂昂 2024-12-05 04:18:58

来自 AceAlfred -
我遇到了一个问题,也许你知道快速解决方法?如果员工未登记上个月的销售额,则不会显示此人的数据。有没有办法添加缺少员工的行,其中“销售额”设置为 0,并且仍然提取其他行的数据?前任。 2012 年 -- 第 1 个月 -- 姓名 Alfred -- 销售额 0 -- Prev 500

一种方法是“修复”您的数据,确保其中始终具有值。我建议在填充数据的任何系统中执行此操作。或者作为每晚的批处理,检查未输入数据的人员并为您粘贴 0(如果/当真实数据到达时进行更新)。但如果你不能...

CREATE TABLE agent (id INT, name NVARCHAR(128), start_date DATETIME, leave_date DATETIME);
-- populate with your agents

CREATE TABLE calendar (year DATETIME, month DATETIME, day DATETIME);
-- populate with all dates you want to report on

CREATE TABLE sales (agent_id INT, month_start DATETIME, total INT);
-- populate with your data


WITH new_raw_data AS
(
  SELECT
    agent.id                  AS [agent_id],
    calendar.month            AS [month_start],
    COALESCE(sales.total, 0)  AS [total]
  FROM
    agent
  INNER JOIN
    calendar
      ON  calendar.month_start >= COALESCE(DATEADD(month, -1, agent.start_date), '2000 Jan 01')
      AND calendar.month_start <= COALESCE(agent.leave_date, '2079 Dec 31')
  LEFT JOIN
    sales
      ON  sales.agent_id    = agent.id
      AND sales.month_start = calendar.month_start
  WHERE
    calendar.month_start = calendar.day   -- Only use records for the start of each month
)
,
<your other queries, using the nicely cleaned data, go here.>

From AceAlfred -
One problem I have run into, maybe you know a quick fix? When a employee has not booked his sales for a previous month there is no data to display for this individual. Is there a way to add a row with the missing employee, where the "sales" is set to 0 and still pull the data for the other rows? Ex. Year 2012 -- Month 1 -- Name Alfred -- Sales 0 -- Prev 500

One approach is to "fix" your data, ensuring it always has values in it. I'd recommend doing that in whatever system is populating your data. Or as a nightly batch that checks for people that didn't enter their data and sticks in 0's for you (To be updated if/when the real data arrives). But if you can't...

CREATE TABLE agent (id INT, name NVARCHAR(128), start_date DATETIME, leave_date DATETIME);
-- populate with your agents

CREATE TABLE calendar (year DATETIME, month DATETIME, day DATETIME);
-- populate with all dates you want to report on

CREATE TABLE sales (agent_id INT, month_start DATETIME, total INT);
-- populate with your data


WITH new_raw_data AS
(
  SELECT
    agent.id                  AS [agent_id],
    calendar.month            AS [month_start],
    COALESCE(sales.total, 0)  AS [total]
  FROM
    agent
  INNER JOIN
    calendar
      ON  calendar.month_start >= COALESCE(DATEADD(month, -1, agent.start_date), '2000 Jan 01')
      AND calendar.month_start <= COALESCE(agent.leave_date, '2079 Dec 31')
  LEFT JOIN
    sales
      ON  sales.agent_id    = agent.id
      AND sales.month_start = calendar.month_start
  WHERE
    calendar.month_start = calendar.day   -- Only use records for the start of each month
)
,
<your other queries, using the nicely cleaned data, go here.>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文