Reputation: 695
(I have rewritten my question because it's more complex) I'm racking my brain on how to do this. Consider this example:
+---------+---------+----------+-----------+--------+-------------+
| IDE_REF | ID_REF2 | CURRENCY | INDICATOR | AMOUNT | TYPE_AMOUNT |
+---------+---------+----------+-----------+--------+-------------+
| 157 | 129 | USD | 2 | 30 | 1 |
| 157 | 129 | USD | 2 | 30 | 3 |
| 157 | 129 | USD | 2 | 30 | 8 |
| 166 | 129 | USD | 2 | 50 | 1 |
| 166 | 129 | USD | 2 | 50 | 3 |
| 166 | 129 | USD | 2 | 50 | 8 |
| 167 | 129 | USD | 2 | 80 | 1 |
| 167 | 129 | USD | 2 | 80 | 3 |
| 167 | 129 | USD | 2 | 80 | 8 |
| NULL | 129 | NULL | 2 | 20 | 1 |
| NULL | 129 | NULL | 2 | 20 | 3 |
| NULL | 129 | NULL | 2 | 20 | 8 |
| 158 | 1234 | USD | 2 | 10 | 1 |
| 158 | 1234 | USD | 2 | 10 | 3 |
| 158 | 1234 | USD | 2 | 10 | 8 |
| NULL | 1234 | NULL | 1 | 125 | 1 |
| NULL | 1234 | NULL | 1 | 125 | 3 |
| NULL | 1234 | NULL | 1 | 125 | 8 |
+---------+---------+----------+-----------+--------+-------------+
Explanation: If IDE_REF IS NULL, it should find the first matching ID_REF2 where IDE_REF IS NOT NULL and either add/subtract AMOUNT depending on the INDICATOR. So it's possible to have multiple results which are fine.
This is fine (AMOUNT of 20 has been added to 80 = 100, IDE_REF: 167, ID_REF2: 129):
+---------+---------+----------+-----------+--------+-------------+
| IDE_REF | ID_REF2 | CURRENCY | INDICATOR | AMOUNT | TYPE_AMOUNT |
+---------+---------+----------+-----------+--------+-------------+
| 157 | 129 | USD | 2 | 30 | 1 |
| 157 | 129 | USD | 2 | 30 | 3 |
| 157 | 129 | USD | 2 | 30 | 8 |
| 166 | 129 | USD | 2 | 50 | 1 |
| 166 | 129 | USD | 2 | 50 | 3 |
| 166 | 129 | USD | 2 | 50 | 8 |
| 167 | 129 | USD | 2 | 100 | 1 |
| 167 | 129 | USD | 2 | 100 | 3 |
| 167 | 129 | USD | 2 | 100 | 8 |
| 158 | 1234 | USD | 1 | 115 | 1 |
| 158 | 1234 | USD | 1 | 115 | 3 |
| 158 | 1234 | USD | 1 | 115 | 8 |
+---------+---------+----------+-----------+--------+-------------+
This is fine too (AMOUNT of 20 has been added to 50 = 70, IDE_REF: 166, ID_REF2: 129):
+---------+---------+----------+-----------+--------+-------------+
| IDE_REF | ID_REF2 | CURRENCY | INDICATOR | AMOUNT | TYPE_AMOUNT |
+---------+---------+----------+-----------+--------+-------------+
| 157 | 129 | USD | 2 | 30 | 1 |
| 157 | 129 | USD | 2 | 30 | 3 |
| 157 | 129 | USD | 2 | 30 | 8 |
| 166 | 129 | USD | 2 | 70 | 1 |
| 166 | 129 | USD | 2 | 70 | 3 |
| 166 | 129 | USD | 2 | 70 | 8 |
| 167 | 129 | USD | 2 | 80 | 1 |
| 167 | 129 | USD | 2 | 80 | 3 |
| 167 | 129 | USD | 2 | 80 | 8 |
| 158 | 1234 | USD | 1 | 115 | 1 |
| 158 | 1234 | USD | 1 | 115 | 3 |
| 158 | 1234 | USD | 1 | 115 | 8 |
+---------+---------+----------+-----------+--------+-------------+
Upvotes: 1
Views: 165
Reputation: 695
Based on Gordon's idea 'line up the rows and then some conditional logic' I managed to write up a complete query that solves my problem.
CREATE TABLE TableTest (
IDE_REF int
, ID_REF2 int
, CURRENCY varchar(10)
, INDICATOR int
, AMOUNT int
, TYPE_AMOUNT int
)
INSERT INTO TableTest
VALUES (157,129,'USD',2,30,1),
(157,129,'USD',2,30,3),
(157,129,'USD',2,30,8),
(166,129,'USD',2,50,1),
(166,129,'USD',2,50,3),
(166,129,'USD',2,50,8),
(167,129,'USD',2,80,1),
(167,129,'USD',2,80,3),
(167,129,'USD',2,80,8),
(NULL,129,'NULL',2,20,1),
(NULL,129,'NULL',2,20,3),
(NULL,129,'NULL',2,20,8),
(158,1234,'USD',2,10,1),
(158,1234,'USD',2,10,3),
(158,1234,'USD',2,10,8),
(NULL,1234,'NULL',1,125,1),
(NULL,1234,'NULL',1,125,3),
(NULL,1234,'NULL',1,125,8)
Query:
;WITH CTE AS (
SELECT
DENSE_RANK() OVER (ORDER BY ID_REF2, IDE_REF DESC, INDICATOR ASC) AS rn
, IDE_REF
, ID_REF2
, CURRENCY
, INDICATOR
, AMOUNT
, TYPE_AMOUNT
FROM TableTest
)
SELECT
IDE_REF
, ID_REF2
, CURRENCY
, INDICATOR
, CASE
WHEN INDICATOR = 2 AND INDICATORB = 1 THEN AMOUNTB - AMOUNT
WHEN INDICATOR = 1 AND INDICATORB = 2 THEN AMOUNT - AMOUNTB
WHEN INDICATOR = INDICATORB THEN AMOUNT + AMOUNTB
ELSE AMOUNT
END AS AMOUNT
, TYPE_AMOUNT
FROM
(
SELECT ta.*, tb.INDICATORB, tb.AMOUNTB
FROM CTE ta
LEFT JOIN (
SELECT ID_REF2 AS ID_REF2B, INDICATOR AS INDICATORB, AMOUNT AS AMOUNTB
FROM CTE
WHERE IDE_REF IS NULL
GROUP BY ID_REF2, INDICATOR, AMOUNT
) tb
ON ta.ID_REF2 = tb.ID_REF2B
AND ta.ID_REF2 IS NOT NULL
AND ta.rn IN (
SELECT MIN(rn)
FROM CTE
WHERE IDE_REF IS NOT NULL
GROUP BY ID_REF2
)
) x
WHERE x.IDE_REF IS NOT NULL
Upvotes: 0
Reputation: 1270513
This answers the original version of the question.
You can use a join
to line up the rows and then some conditional logic:
select t2.ide_ref, t2.id_ref2, t2.currency,
coalesce(t1.indicator, t2.indicator),
(case when t1.indicator = 1 then t1.amount - t2.amount
else t2.amount
end) as amount,
t1.type_amount
from t t2 left join
t t1
on t1.ide_ref = t2.ide_ref and
t1.type_amount = t1.type_amount and
t1.indicator = 1
where t2.indicator = 2;
Upvotes: 2