简体   繁体   中英

SQL query subtract specific rows based on criteria

(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.

  • If INDICATOR = 1, AMOUNT should be subtracted from AMOUNT of INDICATOR 2
  • If INDICATOR = 2, AMOUNT should be added to AMOUNT of INDICATOR 2.

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 |
+---------+---------+----------+-----------+--------+-------------+

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;

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.

Fiddle link

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM