【问题标题】:Need report with the number of columns depending on date range parameter需要根据日期范围参数包含列数的报告
【发布时间】:2015-07-02 01:19:23
【问题描述】:

我是 SQL 的新手。我目前正在开发一个 Oracle 数据库,并创建了一个根据日期范围参数提取数据的报告。

代码如下:

SELECT 
  DISTINCT C.CUSTOMER_CODE
, MS.SALESMAN_NAME
, SUM(C.REVENUE_AMT) Rev_Amt


FROM 
    C_REVENUE_ANALYSIS C
  , M_CUSTOMER_H MC
  , M_SALESMAN MS


WHERE C.COMPANY_CODE = 'W1'
  AND C.CUSTOMER_CODE = MC.CUSTOMER_CODE
  AND MC.SALESMAN_CODE = MS.SALESMAN_CODE
  AND trunc(C.REVENUE_DATE) between to_date(<STARTDATE>,'YYYYMMDD') and to_date(<ENDDATE>,'YYYYMMDD')
  AND MS.COMPANY_CODE = '00'

GROUP BY C.CUSTOMER_CODE, MS.SALESMAN_NAME

ORDER BY C.CUSTOMER_CODE, MS.SALESMAN_NAME

1 月 1 日至 4 月 30 日期间生成的报告为:

+-----------+--------------+--------------+
|Customer   |Salesman Name |Revenue Amount|
+-----------+--------------+--------------+
|Customer 1 |Salesman 1    |       5000.00|
+-----------+--------------+--------------+
|Customer 2 |Salesman 1    |       8000.00|
+-----------+--------------+--------------+
|Customer 3 |Salesman 2    |        300.00|
+-----------+--------------+--------------+
|Customer 4 |Salesman 3    |        600.00|
+-----------+--------------+--------------+
|Customer 5 |Salesman 3    |       5000.00|
+-----------+--------------+--------------+
|Customer 6 |Salesman 3    |       8000.00|
+-----------+--------------+--------------+
|Customer 7 |Salesman 4    |       9000.00|
+-----------+--------------+--------------+
|Customer 8 |Salesman 5    |       2000.00|
+-----------+--------------+--------------+
|Customer 9 |Salesman 6    |       1000.00|
+-----------+--------------+--------------+
|Customer10 |Salesman 6    |       5000.00|
+-----------+--------------+--------------+
|Customer11 |Salesman 7    |       6000.00|
+-----------+--------------+--------------+
|Customer12 |Salesman 8    |       8000.00|
+-----------+--------------+--------------+

现在我需要你的帮助。我需要显示从 1 月到 4 月每个月每个销售员的收入明细。

所以我希望结果如下所示:

+-----------+--------------+-----------+-----------+-----------+-----------+-------------+
|Customer   |Salesman Name |Rev for Jan|Rev for Feb|Rev for Mar|Rev for Apr|Total Rev Amt|
+-----------+--------------+-----------+-----------+-----------+-----------+-------------+
|Customer 1 |Salesman 1    |    1000.00|    1000.00|    1000.00|    2000.00|      5000.00|
+-----------+--------------+-----------+-----------+-----------+-----------+-------------+
|Customer 2 |Salesman 1    |    2000.00|    2000.00|    2000.00|    2000.00|      8000.00|
+-----------+--------------+-----------+-----------+-----------+-----------+-------------+
|Customer 3 |Salesman 2    |     100.00|       0.00|     100.00|     100.00|       300.00|
+-----------+--------------+-----------+-----------+-----------+-----------+-------------+
|Customer 4 |Salesman 3    |     100.00|     200.00|     100.00|     200.00|       600.00|
+-----------+--------------+-----------+-----------+-----------+-----------+-------------+
|Customer 5 |Salesman 3    |    1000.00|    2000.00|    1000.00|    1000.00|      5000.00|
+-----------+--------------+-----------+-----------+-----------+-----------+-------------+
|Customer 6 |Salesman 3    |    1000.00|    2000.00|    1000.00|    4000.00|      8000.00|
+-----------+--------------+-----------+-----------+-----------+-----------+-------------+
|Customer 7 |Salesman 4    |    2000.00|    2000.00|    3000.00|    2000.00|      9000.00|
+-----------+--------------+-----------+-----------+-----------+-----------+-------------+
|Customer 8 |Salesman 5    |     500.00|     400.00|     500.00|     600.00|      2000.00|
+-----------+--------------+-----------+-----------+-----------+-----------+-------------+
|Customer 9 |Salesman 6    |     200.00|     200.00|     200.00|     400.00|      1000.00|
+-----------+--------------+-----------+-----------+-----------+-----------+-------------+
|Customer10 |Salesman 6    |    1000.00|    1000.00|    2000.00|    1000.00|      5000.00|
+-----------+--------------+-----------+-----------+-----------+-----------+-------------+
|Customer11 |Salesman 7    |    2000.00|    2000.00|    1000.00|    1000.00|      6000.00|
+-----------+--------------+-----------+-----------+-----------+-----------+-------------+
|Customer12 |Salesman 8    |    2000.00|    2000.00|    2000.00|    2000.00|      8000.00|
+-----------+--------------+-----------+-----------+-----------+-----------+-------------+

很遗憾,由于我是新手,我无权创建存储过程来定期调用这些数据。所以我可能不得不编写冗余代码。

但真正的问题是,能够分解每个销售员每个月的收入的代码是什么?

此外,列数因日期范围而异。例如;当1月到4月被选中时,我可以获得4列的收入加上1栏,以获得总收入。当上一年的10月到今年4月份被选中时,我可以获得7列的收入加上1列以获得总收入。

请问有人可以帮助渴望学习和证明自己的新手吗?非常感谢您的帮助。提前非常感谢。

编辑:

经过一番劝说,我的经理同意提交我的存储过程以供批准,如果获得批准,它将被创建。

如果我确实要创建一个存储过程,获得所需结果的代码是什么?

提前致谢。

【问题讨论】:

    标签: sql oracle calculated-columns


    【解决方案1】:

    可以使用如下所示的枢轴。通过使用 EXTRACT 函数将月份从收入日期中提取出来,然后您可以使用它来 PIVOT 并为每个月创建一个列。此外,通过使用带窗口的 SUM,您可以获得每个销售员/客户组合的总收入。

    需要注意的一点是,如果您的日期范围跨越多年,则每年的同一月份将汇总到同一列中。

    示例:如果您的日期范围是 2019 年 1 月 1 日至 2020 年 1 月 31 日,则 2019 年 1 月和 2020 年 1 月将相加。

    查询

    WITH
        d (customer,
           salesman,
           revenue_amount,
           revenue_date)
        AS
            (SELECT 'Customer 1', 'Salesman 1', 20.00, TO_DATE ('1-JAN-2020') FROM DUAL
             UNION ALL
             SELECT 'Customer 1', 'Salesman 1', 30.00, TO_DATE ('8-JAN-2020') FROM DUAL
             UNION ALL
             SELECT 'Customer 1', 'Salesman 1', 30.00, TO_DATE ('21-APR-2020') FROM DUAL
             UNION ALL
             SELECT 'Customer 1', 'Salesman 1', 57.00, TO_DATE ('12-JUN-2020') FROM DUAL
             UNION ALL
             SELECT 'Customer 1', 'Salesman 1', 76.00, TO_DATE ('1-OCT-2020') FROM DUAL
             UNION ALL
             SELECT 'Customer 1', 'Salesman 1', 57.00, TO_DATE ('17-MAR-2020') FROM DUAL
             UNION ALL
             SELECT 'Customer 2', 'Salesman 1', 12.00, TO_DATE ('3-MAY-2020') FROM DUAL
             UNION ALL
             SELECT 'Customer 3', 'Salesman 2', 300.00, TO_DATE ('5-APR-2020') FROM DUAL)
      SELECT customer,
             salesman,
             NVL (jan, 0)     AS rev_for_jan,
             NVL (feb, 0)     AS rev_for_feb,
             NVL (mar, 0)     AS rev_for_mar,
             NVL (apr, 0)     AS rev_for_apr,
             NVL (may, 0)     AS rev_for_may,
             NVL (jun, 0)     AS rev_for_jun,
             NVL (jul, 0)     AS rev_for_jul,
             NVL (aug, 0)     AS rev_for_aug,
             NVL (sep, 0)     AS rev_for_sep,
             NVL (oct, 0)     AS rev_for_oct,
             NVL (nov, 0)     AS rev_for_nov,
             NVL (dec, 0)     AS rev_for_dec,
             total_revenue
        FROM (SELECT d.customer,
                     d.salesman,
                     d.revenue_amount,
                     EXTRACT (MONTH FROM d.revenue_date)                                 AS revenue_month,
                     SUM (revenue_amount) OVER (PARTITION BY d.customer, d.salesman)     AS total_revenue
                FROM d
               WHERE revenue_date BETWEEN TO_DATE ('1-JAN-2020', 'DD-MON-YYYY')
                                      AND TO_DATE ('30-APR-2020', 'DD-MON-YYYY'))
             PIVOT (SUM (revenue_amount)
                   FOR revenue_month
                   IN (1 AS jan,
                      2 AS feb,
                      3 AS mar,
                      4 AS apr,
                      5 AS may,
                      6 AS jun,
                      7 AS jul,
                      8 AS aug,
                      9 AS sep,
                      10 AS oct,
                      11 AS nov,
                      12 AS dec))
    ORDER BY customer, salesman;
    

    结果

         CUSTOMER      SALESMAN    REV_FOR_JAN    REV_FOR_FEB    REV_FOR_MAR    REV_FOR_APR    REV_FOR_MAY    REV_FOR_JUN    REV_FOR_JUL    REV_FOR_AUG    REV_FOR_SEP    REV_FOR_OCT    REV_FOR_NOV    REV_FOR_DEC    TOTAL_REVENUE
    _____________ _____________ ______________ ______________ ______________ ______________ ______________ ______________ ______________ ______________ ______________ ______________ ______________ ______________ ________________
    Customer 1    Salesman 1                50              0             57             30              0              0              0              0              0              0              0              0              137
    Customer 3    Salesman 2                 0              0              0            300              0              0              0              0              0              0              0              0              300
    

    【讨论】:

      【解决方案2】:

      另一种解决方案:

      select a.cust, a.salesman, 
          (select sum(b.revenue) from rev b 
          where a.cust=b.cust and a.salesman=b.salesman and
          tr_date between to_date('1 jan 2014','YYYYMMDD') and to_date('31 jan 2014')) Jan_Rev,
          (select sum(c.revenue) from rev c
          where a.cust=c.cust and a.salesman=c.salesman and
          tr_date between to_date('1 feb 2014','YYYYMMDD') and to_date('28 feb 2014')) feb_Rev,
          (select sum(d.revenue) from rev d 
          where a.cust=d.cust and a.salesman=d.salesman and
          tr_date between to_date('1 mar 2014','YYYYMMDD') and to_date('31 mar 2014')) mar_Rev
          ... and so on for more months..
      from 
          cust_table a
      where
        .. add where clause if any ...
      

      【讨论】:

        【解决方案3】:

        case 语句可能在 sum 中起作用,类似于 Pivot 语句。

        SELECT 
          DISTINCT C.CUSTOMER_CODE
        , MS.SALESMAN_NAME
        , SUM(C.REVENUE_AMT) Rev_Amt
        ,CASE WHEN 
            trunc(C.REVENUE_DATE) between to_date('1 jan 2014','YYYYMMDD') and to_date('31 jan 2014','YYYYMMDD') 
            THEN SUM(C.REVENUE_AMT)
            ELSE 0
         END AS Revenue_Jan_2014
        
        FROM 
            C_REVENUE_ANALYSIS C
          , M_CUSTOMER_H MC
          , M_SALESMAN MS
        
        
        WHERE C.COMPANY_CODE = 'W1'
          AND C.CUSTOMER_CODE = MC.CUSTOMER_CODE
          AND MC.SALESMAN_CODE = MS.SALESMAN_CODE
          AND trunc(C.REVENUE_DATE) between to_date(<STARTDATE>,'YYYYMMDD') and to_date(<ENDDATE>,'YYYYMMDD')
          AND MS.COMPANY_CODE = '00'
        
        GROUP BY C.CUSTOMER_CODE, MS.SALESMAN_NAME
        
        ORDER BY C.CUSTOMER_CODE, MS.SALESMAN_NAME
        

        但是,就像 xionutz2k 的答案一样,这也需要知道具体的列。

        您可以开始使用 Dynamics SQL,但这会变得很糟糕。这是使用动态 SQL 的参考链接。

        Dynamic SQL to generate column names?

        【讨论】:

        • 谢谢理查德。我考虑在每一列的代码中使用 CASE。但是,如果我提前知道列数,那就太好了。动态 SQL 听起来不错,但我不需要访问权限来为此创建 SPROC 吗?
        • 您可能需要访问权限,因为在大多数情况下您需要将 SQL 生成为字符串,然后运行 ​​Execute 语句。我能想到的唯一其他选择是您以平面格式从 SQL 获取数据,然后使用您的报告工具“透视”数据。我知道使用 SSRS 很容易做到这一点。您使用的是什么报告工具?
        • 所以我能够说服经理将我的存储过程发送给批准,如果批准,它可以被创建并且我可以使用它。因此,只要获得批准,我现在就可以创建我可能需要的东西。我现在该怎么办?请指教。并提前再次感谢您。
        • 动态 SQL 意味着使用字符串变量(DECLARE sSQL as VARCHAR(MAX))并设置字符串,例如。 SET sSQL = 'SELECT ...Rest of statement here ."。然后使用执行命令运行该 SQL 以给出结果。通常复杂且混乱。如果您可以在报告工具中执行此操作,那就更好了。
        • 对不起,理查德。我应该在前面提到。对于报告,我可以使用 SSRS。我可以使用 Visual Studio 创建报告并将报告上传到我们的报告工作区。你有什么建议?
        【解决方案4】:

        看看pivot函数http://www.oracle.com/technetwork/articles/sql/11g-pivot-097235.html

        丑陋的部分是您必须提前知道旋转的列(在您的情况下是所选范围内的月份)。一个潜在的解决方案是使用 PLSQL 创建动态 SQL 语句,但您可能不需要适当的权限来执行它。您在帖子中提到您无法创建程序(我假设您没有 CREATE PROCEDURE 授权),但不确定这是否是您缺少的唯一授权。

        【讨论】:

        • 感谢您的回复。基本上,我没有任何 WRITE 访问权限。只读访问。如何在访问受限的情况下证明自己????
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-28
        • 2017-05-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多