【问题标题】:ORACLE merge two SELECTs with UNION adding upORACLE 合并两个 SELECT 和 UNION 相加
【发布时间】:2014-07-28 04:14:23
【问题描述】:

我有这个 SQL:

SELECT 
c.customer_code,                           
SUM(units) AS tot_units,
SUM(total_amount) AS tot_money,
null as units_to_date,
null as amount_to_date,
FROM customers c
join transactions t on t.customer_code = c.customer_code
WHERE customer_active='S'
GROUP BY c.customer_code
UNION
SELECT 
c.customer_code,               
null AS tot_units,
null AS tot_money,
SUM(units) as units_to_date,
SUM(total_amount) as amount_to_date, 
FROM customers c
join transactions t on t.customer_code = c.customer_code
WHERE customer_active='S' and t.transaction_date >= (trunc(current_date) - (60 * INTERVAL '1' DAY))
GROUP BY c.customer_code;

结果是:

CUSTOMER_CODE, TOT_UNITS, TOT_AMOUNT, TO_DATE_UNITS, TO_DATE_AMOUNT
0000001        450        300         null           null          
0000001        null       null        30             15        

我需要的结果是:

CUSTOMER_CODE, TOT_UNITS, TOT_AMOUNT, TO_DATE_UNITS, TO_DATE_AMOUNT
0000001        450        300         30             15        

我尝试过使用 UNION,但它不起作用。

【问题讨论】:

  • 但是你为什么大喊大叫?

标签: sql oracle select union


【解决方案1】:

另一个简单的解决方案;

WITH CUSTOMER AS (
  SELECT C.CUSTOMER_CODE,
         SUM(UNITS) AS TOT_UNITS,
         SUM(TOTAL_AMOUNT) AS TOT_MONEY,
         NULL AS UNITS_TO_DATE,
         NULL AS AMOUNT_TO_DATE,
    FROM CUSTOMERS C JOIN TRANSACTIONS T ON T.CUSTOMER_CODE = C.CUSTOMER_CODE
   WHERE CUSTOMER_ACTIVE = 'S'
GROUP BY C.CUSTOMER_CODE
UNION
  SELECT C.CUSTOMER_CODE,
         NULL AS TOT_UNITS,
         NULL AS TOT_MONEY,
         SUM(UNITS) AS UNITS_TO_DATE,
         SUM(TOTAL_AMOUNT) AS AMOUNT_TO_DATE,
    FROM CUSTOMERS C JOIN TRANSACTIONS T ON T.CUSTOMER_CODE = C.CUSTOMER_CODE
   WHERE     CUSTOMER_ACTIVE = 'S'
         AND T.TRANSACTION_DATE >= (TRUNC(CURRENT_DATE) - (60 * INTERVAL '1' DAY))
GROUP BY C.CUSTOMER_CODE)
    SELECT CUSTOMER_CODE,
           MAX(TOT_UNITS) AS TOT_UNITS,
           MAX(TOT_MONEY) AS TOT_MONEY,
           MAX(UNITS_TO_DATE) AS UNITS_TO_DATE,
           MAX(AMOUNT_TO_DATE) AS AMOUNT_TO_DATE
      FROM CUSTOMER
  GROUP BY CUSTOMER_CODE

【讨论】:

    【解决方案2】:

    扩展@VJHil 的答案, 可以摆脱union。诀窍是使用case 过滤掉您想要的日期范围之外的所有内容:

    SELECT   c.customer_code,
             SUM (units) AS tot_units,
             SUM (total_amount) AS tot_money,
             SUM (
                CASE
                   WHEN t.transaction_date >=
                           (TRUNC (CURRENT_DATE) - (60 * INTERVAL '1' DAY)) THEN
                      units
                   ELSE
                      NULL
                END)
                AS units_to_date,
             SUM (
                CASE
                   WHEN t.transaction_date >=
                           (TRUNC (CURRENT_DATE) - (60 * INTERVAL '1' DAY)) THEN
                      total_amount
                   ELSE
                      NULL
                END)
                AS amount_to_date
    FROM     customers c JOIN transactions t ON t.customer_code = c.customer_code
    WHERE    customer_active = 'S'
    GROUP BY c.customer_code
    

    这应该比任何两次访问数据的解决方案都要好。

    【讨论】:

    • 我同意您的方法,并且您“忠实”他关于求和 units_to_date 和 ammount_to_date 的原始条件。当然,对 NULLS 求和将导致 NULLS。因此我认为这在技术上是正确的,但我认为最终用户会想要零来代替 NULLS。
    • @Patrick:在这种情况下,我倾向于使用null 而不是零,因为它适用于sumcount,所以它更实用一些。如果该列为空时需要非空结果,则可以使用nvlcoalesce 进行转换。
    【解决方案3】:

    我认为你没有任何理由使用联合。试试这个,我刚刚删除了空值。

    SELECT 
    c.customer_code,               
    SUM(units) AS tot_units,
    SUM(total_amount) AS tot_money,
    SUM(units) as units_to_date,
    SUM(total_amount) as amount_to_date, 
    MIN(transaction_date) AS first_transaction_date
    FROM customers c
    join transactions t on t.customer_code = c.customer_code
    WHERE customer_active='S' and t.transaction_date >= (trunc(current_date) - (60 * INTERVAL '1' DAY))
    GROUP BY c.customer_code;
    

    【讨论】:

    • 你好。谢谢,但在 total_amount 中,无论购买日期如何,我都需要该客户购买的 total_amount,而在 amount_to_date 中,我只需要一段时间内购买的金额。
    【解决方案4】:

    可以拆分为两个视图并加入它们。 然后使用最适合的NVL()MAX()

    WITH V1 AS (
    SELECT 
    c.customer_code,                           
    SUM(units) AS tot_units,
    SUM(total_amount) AS tot_money,
    null as units_to_date,
    null as amount_to_date,
    FROM customers c
    join transactions t on t.customer_code = c.customer_code
    WHERE customer_active='S'
    GROUP BY c.customer_code
    ),
    V2 AS (
    SELECT 
    c.customer_code,               
    null AS tot_units,
    null AS tot_money,
    SUM(units) as units_to_date,
    SUM(total_amount) as amount_to_date, 
    FROM customers c
    join transactions t on t.customer_code = c.customer_code
    WHERE customer_active='S' and t.transaction_date >= (trunc(current_date) - (60 * INTERVAL '1' DAY))
    GROUP BY c.customer_code)
    SELECT 
     V1.CUSTOMER_CODE, NVL(V1.TOT_UNITS,V2. TOT_UNITS), NVL(V1.TOT_AMOUNT,V2. TOT_AMOUNT), NVL(V1.TO_DATE_UNITS,V2. TO_DATE_UNITS) TO_DATE_AMOUNT
    FROM V1, V2
    WHERE V1.CUSTOMER_CODE = V2.CUSTOMER_CODE
    

    【讨论】:

    • 进一步测试这个解决方案我发现当 V2 没有记录时(因为在时间间隔内没有购买)我没有得到任何记录。我将尝试使用外部连接。
    • 好的,你也可以考虑艾伦的回答,因为这听起来更好!除非你的桌子不是很大。
    猜你喜欢
    • 1970-01-01
    • 2019-03-09
    • 2023-03-24
    • 1970-01-01
    • 2011-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多