0

I have a table t with

ORD_DATE ORD_ID ORD_REF ORD_TYPE1 ORD_TYPE2 PRODNUM PRODQUAL PRICE
2020-09-01 101 101 ORDER ORDER 456 F 555
2020-09-02 102 101 CONF ORDER 456 F 555
2020-11-30 103 102 ORDER ORDER 123 K 444
2020-12-01 104 102 CONF ORDER 123 K 444
2020-12-01 105 103 ORDER ORDER 123 K 444
2020-12-01 106 104 ORDER ORDER 123 K 333
2020-12-02 107 104 CONF ORDER 123 K 333
2020-12-08 108 104 CONF RETURN 123 K -333
2020-12-01 109 105 ORDER ORDER 123 F 222
2020-12-02 110 105 CONF ORDER 123 F 222

and a table s with:

ORD_DATE PROD_NUMBER PROD_QUAL
2020-12-01-00.00.00.000000 123 K
2020-12-01-00.00.00.000000 123 L
  • In table t are all sales per day.
  • A sale has 2 stages: first the order is generated when the customer buys something ("ORDER"/"ORDER"). Then it gets confirmed which is at the next day or within the next days normally ("CONF"/"ORDER"). If a customer sends the product back it's a return ("CONF"/"RETURN").

In table s are the products that are "second hand".

  • if a product is in that table it means all sales from table t with
ORDER_TYPE_1 = "ORDER"
AND ORDER_TYPE_2 = "ORDER"
AND t.ORD_DATE >= s.ORD_DATE
AND t.PROD_NUMBER = s.PROD_NUMBER
AND t.PROD_QUAL = s.PROD_QUAL

count as "second hand".

I need the sum of all "second hand" sales that are confirmed from the year 2021 and month 12. But only rows with CONF/ORDER or CONF/RETURN should be in the calculation. I have CAL_YEAR and CAL_MONTH in table t for that (omitted for less clutter).

From table t only ORDER_REF 105 matches that and the sum would be 0 because only these 2 rows matter:

| 2020-12-02 | 107    | 104     | CONF      | ORDER     | 123     | K        | 333
| 2020-12-08 | 108    | 104     | CONF      | RETURN    | 123     | K        | -333

My code so far:

SELECT SUM(PRICE)
FROM t
--
LEFT JOIN s
ON t.PRODNUM = s.PRODNUM
AND t.PRODQUAL = s.PRODQUAL
AND (SELECT ORD_DATE FROM t WHERE ORDER_TYPE_1 = 'ORDER' AND ORDER_TYPE_2 = 'ORDER') >= s.ORD_DATE
--
WHERE CAL_YEAR = 2021
AND CAL_MONTH = 12
AND ORDER_TYPE_1 = 'CONF'
AND ORDER_TYPE_2 IN ('ORDER', 'RETURN')
--
GROUP BY PRICE
;

SQL-Error: "single-row subquery returns more than one row

My problem is limiting the LEFT JOIN to ORDER/ORDER (so that ORDER_REF 105 is in) but only use CONF/ORDER and CONF/RETURN for the sum (so that ORDER_REF 102 is out).

Anyone can help?

Vega
  • 2,661
  • 5
  • 24
  • 49
  • It's not clear that you need the subquery in the join condition... can't you just say `t.order_type_1 = 'ORDER'...`? But I think my bigger question is, why do you want to join to `s` at all if `t` has all the information you need? – EdmCoff Dec 22 '21 at 15:28
  • t has no information when a product starts getting sold as "second hand". That information is only in table s. – Vega Dec 22 '21 at 15:32
  • Ah, my mistake. Thanks for clarifying. (When I read "if a product is in that table it means all sales from table t with" I thought it meant "s is ... all sales from table t with".) It's still not clear to me why you want a subquery in the join condition, though, instead of referencing the `t` you are joining. – EdmCoff Dec 22 '21 at 15:40
  • 1
    Please [edit] the question to include a [MRE] with the expected output for your sample data. – MT0 Dec 22 '21 at 15:55
  • Join the subquery instead, – jarlh Dec 22 '21 at 16:12
  • I couldn't follow all your business rules, but looks like an [`outer apply`](https://oracle-base.com/articles/12c/lateral-inline-views-cross-apply-and-outer-apply-joins-12cr1) might help. – William Robertson Dec 22 '21 at 18:22

1 Answers1

0

The simplest way I can think of would be to do a self-join, where you join a second copy of table t aliased t2 to use for the CONF/ORDER and CONF/RETURN rows, while you use t for the ORDER/ORDER rows.

SELECT SUM(t2.PRICE)
FROM t
--
INNER JOIN t t2
ON t2.ORD_REF = t.ORD_REF
AND t2.ORDER_TYPE_1 = 'CONF'
AND t2.ORDER_TYPE_2 IN ('ORDER', 'RETURN')
-- 
LEFT JOIN s
ON t.PRODNUM = s.PRODNUM
AND t.PRODQUAL = s.PRODQUAL
AND t.ORD_DATE >= s.ORD_DATE
--
WHERE t.CAL_YEAR = 2021
AND t.CAL_MONTH = 12
AND t.ORDER_TYPE_1 = 'ORDER'
AND t.ORDER_TYPE_2 = 'ORDER'
;

If you need it to be more efficient, you could use analytic/window functions to pull the summed price from the CONF rows into the ORDER/ORDER row as a new column. This way it will only query table t once instead of twice.

SELECT SUM(t2.order_price_sum)
FROM (select t.*, 
            sum(case when ORDER_TYPE_1 = 'CONF' 
                          AND ORDER_TYPE_2 IN ('ORDER', 'RETURN') 
                     then t.price 
                     else 0 end) over (partition by ord_ref) as order_price_sum
            from t) t2
--
LEFT JOIN s
ON t2.PRODNUM = s.PRODNUM
AND t2.PRODQUAL = s.PRODQUAL
AND t2.ord_date >= s.ORD_DATE
--
WHERE CAL_YEAR = 2021
AND CAL_MONTH = 12
AND ORDER_TYPE_1 = 'ORDER'
AND ORDER_TYPE_2 = 'ORDER'
;
kfinity
  • 8,581
  • 1
  • 13
  • 20