I have two tables in oracle sql, A and B that manage a warehouse.
CREATE TABLE A (
id NUMBER ,
Item_ID NUMBER,
Start_NUM NUMBER NOT NULL,
End_NUM NUMBER NOT NULL
);
CREATE TABLE B (
id NUMBER ,
Item_ID NUMBER,
Start_NUM NUMBER NOT NULL,
End_NUM NUMBER NOT NULL
);
INSERT INTO A
(ID, ITEM_ID, START_NUM, END_NUM)
VALUES(1, 337, 101, 400);
INSERT INTO A
(ID, ITEM_ID, START_NUM, END_NUM)
VALUES(2, 337, 500, 800);
INSERT INTO A
(ID, ITEM_ID, START_NUM, END_NUM)
VALUES(3, 337, 801, 1200);
INSERT INTO A
(ID, ITEM_ID, START_NUM, END_NUM)
VALUES(4, 337, 1500, 1600);
INSERT INTO A
(ID, ITEM_ID, START_NUM, END_NUM)
VALUES(5, 337, 15000, 16000);
INSERT INTO B
(ID, ITEM_ID, START_NUM, END_NUM)
VALUES(1, 337, 240, 300);
INSERT INTO B
(ID, ITEM_ID, START_NUM, END_NUM)
VALUES(2, 337, 301, 400);
INSERT INTO B
(ID, ITEM_ID, START_NUM, END_NUM)
VALUES(3, 337, 850, 1100);
Both of them have the same columns Item_id, Start_num and End_num. Table A has the additions
of the items to the warehouse and table B the consumptions. Start_num and End_num represent the range of items received/consumed respectively.
For example start_num : 101 and end_num: 400 for item_id 337 means 300 items of id 337 numbering 101,102,103,...,400.
I want to create a view that returns the availability ranges of each item_id based on addition and consumption. For example i received items with numbering 101,102,103,104,...,400 and i consumed the 240,241,...,300 and 301,302,..,400 so the remaining numbering is 101,102,103,...,239.
The result i am looking for looks like that.
ID | ITEMID | AVAIL_START_NUM | AVAIL_END_NUM |
---|---|---|---|
1 | 337 | 101 | 239 |
2 | 337 | 500 | 800 |
3 | 337 | 801 | 849 |
4 | 337 | 1,101 | 1,200 |
5 | 337 | 1,500 | 1,600 |
6 | 337 | 15,000 | 16,000 |
Thank you in advance.
You have to merge the consumptions, then find the substracted intervals from intersections with the additions (you may have 2 new intervals) and then add the original additions not having any intersection with the consumptions.
WITH data_add(id, item_id, start_num, end_num) AS (
SELECT 1, 337, 101, 400 FROM DUAL UNION ALL
SELECT 2, 337, 500, 800 FROM DUAL UNION ALL
SELECT 3, 337, 801, 1200 FROM DUAL UNION ALL
SELECT 4, 337, 1500, 1600 FROM DUAL UNION ALL
SELECT 5, 337, 15000, 16000 FROM DUAL -- UNION ALL
)
, data_cons(id, item_id, start_num, end_num) AS (
SELECT 1, 337, 240, 300 FROM DUAL UNION ALL
SELECT 2, 337, 301, 400 FROM DUAL UNION ALL
SELECT 3, 337, 850, 1100 FROM DUAL -- UNION ALL
)
, merged_cons(item_id, start_num, end_num) AS (
SELECT * FROM data_cons
MATCH_RECOGNIZE (
PARTITION BY item_id
ORDER BY start_num, end_num
MEASURES FIRST(start_num) AS start_num, LAST(end_num) AS end_num
PATTERN( merged* strt )
DEFINE
merged AS MAX(end_num) + 1 >= NEXT(start_num)
)
)
, intersections(id, item_id, start_before, end_before, start_after, end_after) AS (
SELECT a.id, a.item_id, /*a.start_num AS add_start, a.end_num AS add_end,
b.start_num AS cons_start, b.end_num AS cons_end, */
CASE WHEN a.start_num < b.start_num - 1 THEN a.start_num END AS start_before,
CASE WHEN a.start_num < b.start_num - 1 THEN b.start_num - 1 END AS end_before,
CASE WHEN b.end_num + 1 < a.end_num THEN b.end_num + 1 END AS start_after,
CASE WHEN b.end_num + 1 < a.end_num THEN a.end_num END AS end_after
FROM data_add a
JOIN merged_cons b
ON a.item_id = b.item_id AND LEAST(a.end_num, b.end_num) >= GREATEST(a.start_num, b.start_num)
)
SELECT item_id, start_before as start_num, end_before as end_num
FROM intersections WHERE start_before IS NOT NULL
UNION ALL
SELECT item_id, start_after as start_num, end_after as end_num
FROM intersections WHERE start_after IS NOT NULL
UNION ALL
SELECT item_id, start_num, end_num
FROM data_add d
WHERE NOT EXISTS(SELECT 1 FROM intersections i WHERE i.id = d.id)
;
337 101 239
337 801 849
337 1101 1200
337 500 800
337 1500 1600
337 15000 16000