【问题标题】:Parameter month selection: Make a query that shows previous month, 12 months ago and last 12 months average参数月份选择:查询显示上个月、12个月前和最近12个月的平均值
【发布时间】:2011-08-03 10:17:25
【问题描述】:

我想玩弄我的 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)

要复制上个月我正在使用这个: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)
)

Prev_Month 中的 NULL 表示 Total_Sales 的开始时间是 2011 年的第 1 个月,因此此示例没有先前的数据。

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

【问题讨论】:

  • 您可以在 Reporting Services 而不是 SQL 中进行聚合。获取年份数据并使用报表功能
  • 我就是这么想的,但是为一个数据集创建查询不是更容易吗?我遇到的问题是 Last_12_Month_AVG。
  • 我通过报告服务完全做到了
  • AVG 代码如下: =(RunningValue(Clng(Iif(Fields!fiscalyear.Value=2011 ,Fields!valuesale.Value,0)),SUM,"table1") + SUM(Clng (Iif(Fields!fiscalyear.Value=2010,Fields!valuesale.Value,0)),"table1") - RunningValue(Clng(Iif(Fields!fiscalyear.Value=2010 ,Fields!valuesale.Value,0)), SUM,"table1"))/12

标签: sql reporting-services parameters subquery


【解决方案1】:
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

【讨论】:

    【解决方案2】:

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

    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
    

    【讨论】:

    • Last_12_Months_AVG 的问题。这会仅在有 12 个月可用的情况下显示值,而在月份不足的情况下显示空/零?
    • 形成后,每个销售值要么提供给 AVG 函数,要么替换为 NULL,然后提供给 AVG 函数。 (CASE 没有 ELSE,但默认情况下在隐式 ELSE 语句中返回 NULL。)然后 AVG() 函数忽略所有 NULL(因此 2,NULL 的平均值仍然为 2)。如果提供给 AVG() 的所有值均为 NULL,则结果为 NULL - 但目前代码包括“本月”作为平均值的一部分,因此它始终至少有一个实际值可供使用。
    • 我遇到了一个问题,也许您知道快速解决方法?如果员工没有预订上个月的销售额,则没有可显示此人的数据。有没有办法添加缺少员工的行,其中“销售”设置为 0,并且仍然提取其他行的数据?前任。 2012 年 -- 第 1 个月 -- 姓名 Alfred -- 销售额 0 -- 前 500 名等
    • 在当前 CTE 之前创建另一个 CTE。在那里 CROSS JOIN 所有代理与您感兴趣的所有月份,然后 LEFT JOIN 您填充的所有数据节点。然后在sales CTE 中使用that CTE 代替yearly_sales。或者,修复客户端中的输出。在我看来,这通常更可取。
    • @ Dems -- 好的,我一直在尝试这样做,但是我的查询给了我所有销售的 NULL 值。你能在接受的答案中举一个例子吗? :)
    【解决方案3】:

    来自 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

    一种方法是“修复”您的数据,确保其中始终包含值。我建议在填充数据的任何系统中都这样做。或者作为一个夜间批次检查没有输入他们的数据并为您保留 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.>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      • 2020-10-10
      • 2021-03-25
      • 1970-01-01
      相关资源
      最近更新 更多