Change string to date and joining on date range in Oracle SQL - sql

I have below two tables:
~ What I am looking to do: I want to append the Price from Table 2 (t2) to Table 1 (t1), by joining on Quantity and YYYY_MM. Each t2.price was active in a certain time range (t2.Price_Active_Date_From and t2.Price_Active_Date_To), and the t1.Order_YYYYMM should fall within this range. Year 9999 indicates that the price has no end date yet, and is therefore still active. I also want to find which price was active 1 year prior to the order date
So the result should look like:
What I have tried below so far, which works to get the Price_Active_At_Order, but I am not sure how to get Price_Active_PY when my date values are strings:
select distinct
t1.Product_NR,
t1.Customer,
t1.Quantity,
t2.Price as Price_Active_At_Order,
t1.Order_YYYYMM as Order_Date
from Table_1 t1
join Table_2 t2 on t1.Product_NR = t2.Product_NR
and t1.Quantity = t2.Quantity
and t1.Order_YYYYMM between t2.Price_Active_Date_From and t2.Price_Active_Date_To

Two things to point out:
Line 6 : te.Order_YYYYMM as Order_Date change to t1.Order_YYYYMM as Order_Date
Line 13,14,15 : where clause is incomplete and unnecessary, remove it
Your code should work fine with the changes above.
select distinct
t1.Product_NR,
t1.Customer,
t1.Quantity,
t2.Price as Price_Active_At_Order,
t1.Order_YYYYMM as Order_Date
from Table_1 t1
join Table_2 t2 on t1.Product_NR = t2.Product_NR
and t1.Quantity = t2.Quantity
and t1.Order_YYYYMM between t2.Price_Active_Date_From and t2.Price_Active_Date_To

If you want to find a single row in table2 that can supply the complete quantity corresponding to a table1 row at the minimum price then, from Oracle 12, you can use a LATERAL join:
SELECT *
FROM table1 t1
LEFT OUTER JOIN LATERAL (
SELECT price,
price_active_date_from,
price_active_date_to
FROM table2 t2
WHERE t1.product_nr = t2.product_nr
AND t1.quantity <= t2.quantity
AND t1.order_yyyymm BETWEEN t2.price_active_date_from
AND t2.price_active_date_to
ORDER BY t2.price ASC
FETCH FIRST ROW ONLY
) t2
ON (1 = 1)
Which, for the sample data:
CREATE TABLE table1 (product_nr, customer, quantity, order_yyyymm) AS
SELECT 10023, 'X', 20, 202104 FROM DUAL UNION ALL
SELECT 10023, 'Y', 10, 202203 FROM DUAL UNION ALL
SELECT 10334, 'X', 1, 202204 FROM DUAL;
CREATE TABLE table2 (product_nr, quantity, price, price_active_date_from, price_active_date_to) AS
SELECT 10023, 1, 100, 202101, 999912 FROM DUAL UNION ALL
SELECT 10023, 10, 80, 201605, 202012 FROM DUAL UNION ALL
SELECT 10023, 10, 120, 202101, 202205 FROM DUAL UNION ALL
SELECT 10023, 20, 250, 202101, 999912 FROM DUAL UNION ALL
SELECT 10023, 100, 400, 202101, 202111 FROM DUAL UNION ALL
SELECT 10334, 1, 25, 202101, 202111 FROM DUAL UNION ALL
SELECT 10334, 1, 30, 202112, 202205 FROM DUAL;
Outputs:
PRODUCT_NR
CUSTOMER
QUANTITY
ORDER_YYYYMM
PRICE
PRICE_ACTIVE_DATE_FROM
PRICE_ACTIVE_DATE_TO
10023
X
20
202104
250
202101
999912
10023
Y
10
202203
120
202101
202205
10334
X
1
202204
30
202112
202205
If you want to find the row in table2 that can supply the quantity from the cumulative quantities between that row and the preceding rows within the date range ordered by ascending price then you can use:
SELECT *
FROM table1 t1
LEFT OUTER JOIN LATERAL (
SELECT price,
price_active_date_from,
price_active_date_to
FROM (
SELECT price,
price_active_date_from,
price_active_date_to,
SUM(quantity) OVER (ORDER BY price ASC) AS quantity
FROM table2 t2
WHERE t1.product_nr = t2.product_nr
AND t1.order_yyyymm BETWEEN t2.price_active_date_from
AND t2.price_active_date_to
) t2
WHERE t1.quantity <= t2.quantity
ORDER BY t2.price ASC
FETCH FIRST ROW ONLY
) t2
ON (1 = 1)
Which, for the sample data, has the same output.
db<>fiddle here

Related

How to use multiple max Function in SQL select with left join

Suppose I have the following three tables:
Table1:
ID
Value_1
11
abc
22
def
33
xyz
Table2:
ID
Date_1
11
12-Mar-22
11
01-Jan-23
22
19-Dec-22
22
07-Feb-23
33
07-Mar-22
Table3:
ID
Length_1
11
574
11
1029
22
9220
33
1093
33
876
Now, I need an SQL query that would select each ID with Max Lenth_1 and Max Date_1.
Desired output:
ID
Value_1
Date_1
Length_1
11
abc
01-Jan-23
1029
22
def
07-Feb-23
9220
33
xyz
07-Mar-22
1093
I have used max() fuction to achieve this with left join between 2 tables together, however struggling when I have to use Max () twice with 3 tables. I am relatively new to SQL.
SQL Select Max(Date) out of rows with Duplicate Id
I tried this for two tables
Aggregate before you join the tables:
SELECT t1.id,
t1.value_1,
t2.date_1,
t3.length_1
FROM table1 t1
INNER JOIN (
SELECT id,
MAX(date_1) AS date_1
FROM table2
GROUP BY id
) t2
ON (t1.id = t2.id)
INNER JOIN (
SELECT id,
MAX(length_1) AS length_1
FROM table3
GROUP BY id
) t3
ON (t1.id = t3.id)
Which, for the sample data:
CREATE TABLE Table1 (ID, Value_1) AS
SELECT 11, 'abc' FROM DUAL UNION ALL
SELECT 22, 'def' FROM DUAL UNION ALL
SELECT 33, 'xyz' FROM DUAL;
CREATE TABLE Table2 (ID, Date_1) AS
SELECT 11, DATE '2022-03-12' FROM DUAL UNION ALL
SELECT 11, DATE '2023-01-01' FROM DUAL UNION ALL
SELECT 22, DATE '2022-12-19' FROM DUAL UNION ALL
SELECT 22, DATE '2023-02-07' FROM DUAL UNION ALL
SELECT 33, DATE '2022-03-07' FROM DUAL;
CREATE TABLE Table3 (ID, Length_1) AS
SELECT 11, 574 FROM DUAL UNION ALL
SELECT 11, 1029 FROM DUAL UNION ALL
SELECT 22, 9220 FROM DUAL UNION ALL
SELECT 33, 1093 FROM DUAL UNION ALL
SELECT 33, 876 FROM DUAL;
Outputs:
ID
VALUE_1
DATE_1
LENGTH_1
11
abc
2023-01-01 00:00:00
1029
22
def
2023-02-07 00:00:00
9220
33
xyz
2022-03-07 00:00:00
1093
fiddle
One option is to use subqueries selecting max values from tables 2 and 3:
Select t1.ID, t1.VALUE_1,
(Select Max(DATE_1) From Table2 Where ID = t1.ID) "DATE_1",
(Select Max(LENGTH_1) From Table3 Where ID = t1.ID) "LENGTH_1"
From Table1 t1
Order By t1.ID
... another one is to use analytic function with distinct keyword but it could be performance costly with big datasets:
Select DISTINCT
t1.ID, t1.VALUE_1, Max(t2.DATE_1) OVER(Partition By t1.ID) "DATE_1", Max(t3.LENGTH_1) OVER(Partition By t1.ID) "LENGTH_1"
From Table1 t1
Inner Join Table2 t2 ON(t2.ID = t1.ID)
Inner Join Table3 t3 ON(t3.ID = t1.ID)
Order By t1.ID
... both with your sample data:
WITH
Table1 (ID, VALUE_1) AS
(
Select 11, 'abc' From Dual Union All
Select 22, 'def' From Dual Union All
Select 33, 'xyz' From Dual
),
Table2 (ID, DATE_1) AS
(
Select 11, To_Date('2022-03-12', 'yyyy-mm-dd') From Dual Union All
Select 11, To_Date('2023-01-01', 'yyyy-mm-dd') From Dual Union All
Select 22, To_Date('2022-12-19', 'yyyy-mm-dd') From Dual Union All
Select 22, To_Date('2023-02-07', 'yyyy-mm-dd') From Dual Union All
Select 33, To_Date('2022-03-07', 'yyyy-mm-dd') From Dual
),
Table3 (ID, LENGTH_1) AS
(
Select 11, 574 From Dual Union All
Select 11, 1029 From Dual Union All
Select 22, 9220 From Dual Union All
Select 33, 1093 From Dual Union All
Select 33, 876 From Dual
)
results as:
ID VALUE_1 DATE_1 LENGTH_1
---------- ------- --------- ----------
11 abc 01-JAN-23 1029
22 def 07-FEB-23 9220
33 xyz 07-MAR-22 1093
Select t1.id, t1.value1, max(t2.date_1) date_1, max(t3.length_1)t3
from table_1 t1
left join table_2 t2 on t1.id=t2.id
left join table_3 t3 on t1.id=t3.id
group by t1.id, t1.value1
order by 1

SQL Oracle Add 'case when' if value in Join not available

I have below two tables
~ What I am looking to do: I want to append the Price from Table 2 to Table 1. If the exact Quantity in Table 1 is not there in Table 2, take the closest max Quantity from Table 2 [i.e. 12 Quantity is not there in Table 2, the closest max is 20, so I want the price of that].
BUT in some cases there are no max Quantity values, which leads to some entries to fall out of my output. In this case, I am thinking to take the closest min Quantity from Table 2, to avoid some values to fall out of my output
So the result should look like:
I have tried below query but this does not account for taking the closest min Quantity when the closest max does not exist. Product_NR '20765' therefore falls out of my output. I'm thinking of 'case when' but not sure how to incorporate it below.
select
Product_NR,
Customer,
Quantity,
min(price)
from
( select distinct
t1.Product_NR,
t1.Customer,
t1.Quantity,
t2.price,
min(t2.quantity) over (partition by t1.product_NR) as Quantity_min
from Table_1 t1
left join Table_2 t2 on t1.Product_NR = t2.Product_NR
and t1.Quantity <= t2.Quantity
)
where t2.Quantity = Quantity_min
group by
Product_NR,
Customer,
Quantity
From Oracle 12, you can use a LATERAL join and order the correlated sub-query to get the greater quantity rows before the lower quantities and then FETCH FIRST ROW ONLY:
SELECT *
FROM table1 t1
LEFT OUTER JOIN LATERAL(
SELECT price
FROM table2 t2
WHERE t1.product_nr = t2.product_nr
ORDER BY
CASE WHEN t1.quantity <= t2.quantity THEN t2.quantity END
ASC NULLS LAST,
t2.quantity DESC
FETCH FIRST ROW ONLY
)
ON (1 = 1);
Which, for the sample data:
CREATE TABLE table1 (product_nr, customer, quantity) AS
SELECT 10023, 'X', 12 FROM DUAL UNION ALL
SELECT 10023, 'Y', 10 FROM DUAL UNION ALL
SELECT 10334, 'X', 1 FROM DUAL UNION ALL
SELECT 20765, 'Z', 5 FROM DUAL;
CREATE TABLE table2 (product_nr, quantity, price) AS
SELECT 10023, 1, 100 FROM DUAL UNION ALL
SELECT 10023, 10, 120 FROM DUAL UNION ALL
SELECT 10023, 20, 250 FROM DUAL UNION ALL
SELECT 10023, 100, 400 FROM DUAL UNION ALL
SELECT 10334, 0, 250 FROM DUAL UNION ALL
SELECT 10334, 200, 600 FROM DUAL UNION ALL
SELECT 20765, 1, 40 FROM DUAL UNION ALL
SELECT 20765, 2, 50 FROM DUAL;
Outputs:
PRODUCT_NR
CUSTOMER
QUANTITY
PRICE
10023
X
12
250
10023
Y
10
120
10334
X
1
600
20765
Z
5
50
db<>fiddle here

SQL query to get both common and and non common data from 2 tables

Hi im looking for a query which will give me both common and non-common data in one query.
Table 2
ID
Assay
1
124
Result
required_missing
required_present
125
124
Based on req_ind column from table 1 , if req_ind is 1 and the same assay is present in table 2 i want to list it as above.
required missing column can have multiple column.
With the data given this gives requested result:
WITH table1 as (
select 1 as ID, 123 as Assay, 0 as req_ind from dual
union all
select 2,124,1 from dual
union all
select 3,125,1 from dual
),
table2 as (
select 1 as ID, 124 as Assay from dual
),
required_missing as (
select
row_number() over (order by table1.Assay) as R,
table1.Assay as required_missing
from table1
left join table2 on table2.Assay = table1.Assay
where table1.req_ind=1 and table2.id is null
),
requires_present as (
select
row_number() over (order by table1.Assay) as R,
table1.Assay as required_present
from table1
left join table2 on table2.Assay = table1.Assay
where table1.req_ind=1 and table2.id is not null
),
results as (
select row_number() over (order by (id)) as r
from table1
)
select rm.required_missing, rp.required_present
from results
left join required_missing rm on rm.R = results.R
left join requires_present rp on rp.R = results.R
where rm.R is not null or rp.R is not null;
output:
REQUIRED_MISSING
REQUIRED_PRESENT
125
124
If you want to have a comma separated list for missing and for present then you can use:
SELECT LISTAGG(CASE WHEN t2.assay IS NULL THEN t1.assay END, ',')
WITHIN GROUP (ORDER BY t1.assay) AS required_missing,
LISTAGG(t2.assay, ',')
WITHIN GROUP (ORDER BY t1.assay) AS required_present
FROM table1 t1
LEFT OUTER JOIN table2 t2
ON (t1.assay = t2.assay)
WHERE t1.req_ind = 1
Which, for the sample data:
CREATE TABLE table1 (id, assay, req_ind) AS
SELECT 1, 123, 0 FROM DUAL UNION ALL
SELECT 2, 124, 1 FROM DUAL UNION ALL
SELECT 3, 125, 1 FROM DUAL UNION ALL
SELECT 4, 126, 1 FROM DUAL UNION ALL
SELECT 5, 127, 1 FROM DUAL;
CREATE TABLE table2 (id, assay) AS
SELECT 1, 124 FROM DUAL UNION ALL
SELECT 2, 127 FROM DUAL;
Outputs:
REQUIRED_MISSING
REQUIRED_PRESENT
125,126
124,127
If you want the output in multiple rows then:
SELECT required_missing,
required_present
FROM (
SELECT NVL2(t2.assay, 'P', 'M') AS status,
ROW_NUMBER() OVER (
PARTITION BY NVL2(t2.assay, 'P', 'M')
ORDER BY t1.assay
) AS rn,
t1.assay
FROM table1 t1
LEFT OUTER JOIN table2 t2
ON (t1.assay = t2.assay)
WHERE t1.req_ind = 1
)
PIVOT (
MAX(assay)
FOR status IN (
'M' AS required_missing,
'P' AS required_present
)
)
Which outputs:
REQUIRED_MISSING
REQUIRED_PRESENT
125
124
126
127
db<>fiddle here

SQL Left Outer Join not returning all rows from left table (no where clause filter)

I have a left outer join that doesn't return all rows from the "left" table. I have no where clause, so no filtering should be applied after the join.
I am expecting:
Product 1
AT
100
Product 2
AT
25
Product 4
AT
57
Product 1
GR
45
Product 2
GR
22
Product 3
GR
5
Product 4
GR
4
Product 3
null
null
But I'm missing the last row. Any light you could shed into this is very appreciated.
To reproduce it:
-- Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
drop table t1;
drop table t2;
create table t1
(ov_product varchar2(18 byte)
,product varchar2(18 byte)
)
/
create table t2
(reporting_month number
,product varchar2(18 byte)
,sender varchar2(2 byte)
,items number
)
/
insert into t1
(
select 'Product 1' ov_product, 'P1' product from dual
union
select 'Product 2' ov_product, 'P2' product from dual
union
select 'Product 3' ov_product, 'P3' product from dual
union
select 'Product 4' ov_product, 'P4' product from dual
);
insert into t2
(
select 202108, 'P1', 'AT', 100 from dual
union
select 202108, 'P2', 'AT', 25 from dual
union
-- no P3 for AT
select 202108, 'P4', 'AT', 57 from dual
union
select 202108, 'P1', 'GR', 45 from dual
union
select 202108, 'P2', 'GR', 22 from dual
union
select 202108, 'P3', 'GR', 5 from dual
union
select 202108, 'P4', 'GR', 4 from dual
)
;
commit;
select t1.ov_product
,t2.sender
,t2.items
from t1
left outer join t2
on t1.product = t2.product
order by 2, 1
;
Your outer join works fine.
You probably mean partitioned outer join.
See the additional query_partition_clause in the join
PARTITION BY (sender) only this join will fill the gaps in sender as you expects.
select t1.ov_product
,t2.sender
,t2.items
from t1
left outer join t2
PARTITION BY (sender)
on t1.product = t2.product
order by 2, 1
OV_PRODUCT SE ITEMS
------------------ -- ----------
Product 1 AT 100
Product 2 AT 25
Product 3 AT
Product 4 AT 57
Product 1 GR 45
Product 2 GR 22
Product 3 GR 5
Product 4 GR 4
Since there is a line in t2 with product 3 there is no a null-value record to be produced with the left-join.
In order to get such a line you need either to join a table of senders if you have one. Or, if the table does not exist, you may use a CTE like this
with senders(sender) as (select distinct sender from t2)
with senders(sender) as (select distinct sender from t2)
select t1.ov_product
,t2.sender
,t2.items
from t1
cross join senders
left outer join t2
on t1.product = t2.product
and t2.sender = senders.sender
order by 2, 1;

SQL - Finding differences in row order of two tables

I have two tables of ID's and dates and I want to order both tables by date and see those ids that are not in the same order
e.g.
table_1
id | date
------------
A 01/01/09
B 02/01/09
C 03/01/09
table_2
id | date
------------
A 01/01/09
B 03/01/09
C 02/01/09
and get the results
B
C
Now admittedly I could just dump the results of an order by query and diff them, but I was wondering if there is an SQL-y way of getting the same results.
Edit to clarify, the dates are not necessarily the same between tables, it's just there to determine an order
Thanks
if the dates are different in TABLE_1 and TABLE_2, you will have to join both tables on their rank. For exemple:
SQL> WITH table_1 AS (
2 SELECT 'A' ID, DATE '2009-01-01' dt FROM dual UNION ALL
3 SELECT 'B', DATE '2009-01-02' FROM dual UNION ALL
4 SELECT 'C', DATE '2009-01-03' FROM dual
5 ), table_2 AS (
6 SELECT 'A' ID, DATE '2009-01-01' dt FROM dual UNION ALL
7 SELECT 'C', DATE '2009-01-02' FROM dual UNION ALL
8 SELECT 'B', DATE '2009-01-03' FROM dual
9 )
10 SELECT t1.ID
11 FROM (SELECT ID, row_number() over(ORDER BY dt) rn FROM table_1) t1
12 WHERE (ID, rn) NOT IN (SELECT ID,
13 row_number() over(ORDER BY dt) rn
14 FROM table_2);
ID
--
B
C
Is it not just the case of joining on the date and comparing the IDs are the same. This assumes that table_1 is the master sequence.
SELECT table_1.id
FROM
table_1
INNER JOIN table_2
on table_1.[date] = table_2.[date]
WHERE table_1.id <> table_2.id
ORDER BY table_1.id
ehm select id from table_1, table_2 where table_1.id = table_2.id and table_1.date <> table_2.date ?