join tables on different amount of columns - sql

I wanna replace values in the column Attribute in T1 with the values from T2. If sub_id is NULL in T2, then replace f values in T1 with t for all the rows with ID in T2, otherwise do it only for the rows with sub_id.
Below are the dummy tables and the result I get.
WITH T1 AS ( SELECT 1 AS id, 2 as sub_id, '2017-08-20' AS date, 'f' AS attribute UNION ALL
SELECT 1, 3 as sub_id, '2017-08-19', 'f' UNION ALL
SELECT 1, 4 as sub_id,'2017-08-18', 'f' UNION ALL
SELECT 2, 5 as sub_id,'2017-08-20', 'f' UNION ALL
SELECT 2, 6 as sub_id,'2017-08-19', 'f'
),
T2 AS (
SELECT 1 AS id, null as sub_id, '2017-08-19' AS date, '2017-08-25' AS date_end, 't' AS attribute UNION ALL
SELECT 2, 5 as sub_id,'2017-08-19', '2017-08-25' AS date_end, 't' UNION ALL
SELECT 2, 6 as sub_id,'2017-08-19', '2017-08-25' AS date_end, 't'
)
SELECT
a.id,
a.date,
a.sub_id,
COALESCE(b.attribute, a.attribute) as attribute
FROM T1 AS a
LEFT JOIN T2 AS b
ON a.id = b.id AND a.date >= b.date AND a.date <= b.date_end
AND a.sub_id = case when b.sub_id is not null then a.sub_id end
In the above result I wanna have the attribute column with t values in all the eligible rows.
I achieved what I want with another subquery:
WITH T1 AS ( SELECT 1 AS id, 2 as sub_id, '2017-08-20' AS date, 'f' AS attribute UNION ALL
SELECT 1, 3 as sub_id, '2017-08-19', 'f' UNION ALL
SELECT 1, 4 as sub_id,'2017-08-18', 'f' UNION ALL
SELECT 2, 5 as sub_id,'2017-08-20', 'f' UNION ALL
SELECT 2, 6 as sub_id,'2017-08-19', 'f'
),
T2 AS (
SELECT 1 AS id, null as sub_id, '2017-08-19' AS date, '2017-08-25' AS date_end, 't' AS attribute UNION ALL
SELECT 2, 5 as sub_id,'2017-08-19', '2017-08-25' AS date_end, 't' UNION ALL
SELECT 2, 6 as sub_id,'2017-08-19', '2017-08-25' AS date_end, 't'
) ,
adv_level AS ( SELECT
a.id,
a.date,
a.sub_id,
COALESCE(b.attribute, a.attribute) as attribute
FROM T1 AS a
LEFT JOIN T2 AS b
ON a.id = b.id AND a.date >= b.date AND a.date <= b.date_end AND a.sub_id = case when b.sub_id is not null then a.sub_id end)
SELECT
a.id,
a.date,
a.sub_id,
COALESCE(b.attribute, a.attribute) as attribute
FROM adv_level AS a
LEFT JOIN T2 AS b
ON a.id = b.id AND a.date >= b.date AND a.date <= b.date_end
but is there a way to do it just once?

Related

How to combine multiple tables with the basic ID of one table

I want to combine 5 multiple tables, that have the same reference ID as the basic table containing all IDs. The "joined" tables are not containing a value for every reference, but sometimes they have multiple values for one reference. The output should be a sum of each value of the ID.
Example:
Basic Table:
Reference
Basic.Value
1
a
2
b
3
c
4
d
5
e
6
f
7
g
8
h
Table 1:
Reference
T1.Value
1
i
2
j
2
x
3
k
4
l
Table 2
Reference
T2.Value
1
m
5
n
7
o
7
y
8
p
Table 3
Reference
T3.Value
2
q
4
r
6
s
8
t
8
z
Result that should be the output:
Reference
Basic.Value
SUM(T1.Value)
SUM(T2.Value)
SUM(T3.Value)
1
a
i
m
2
b
(j+x)
q
3
c
k
4
d
l
r
5
e
n
6
f
s
7
g
(o+y)
8
h
p
(t+z)
I tried the following code:
SELECT
T0."STATUS",
T0."DocNum" AS "ProjectNumber",
T0."NAME", T0."CARDNAME" AS "Client",
T0."FINISHED" AS "Project Finished",
T1."PoPhAmt" AS "Project Value",
T1."PhBudget" AS "Budget",
(T1."PoPhAmt"-T1."PhBudget") AS "Planned Gross Profit",
T1."TotalAP" AS "Ordered",
SUM(T2."PaidSys") AS "Paid Downpayments(Client)",
COUNT(T2."PaidSys"),
SUM(T3."PaidSys") AS "Paid Invoices(Client)",
COUNT(T3."PaidSys"),
SUM(T4."PaidSys") AS "Creditnotes(Client)",
COUNT(T4."PaidSys")
FROM
(OPMG T0 INNER JOIN PMG8 T1 ON T0."AbsEntry" = T1."AbsEntry")
LEFT JOIN ODPI T2 ON T0."FIPROJECT" = T2."Project"
LEFT JOIN OINV T3 ON T0."FIPROJECT" = T3."Project"
LEFT JOIN ORIN T4 ON T0."FIPROJECT" = T4."Project"
WHERE
T0."FINISHED" < '100' AND T0."STATUS" <> 'N' AND T0."STATUS" <> 'P'
GROUP BY
T0."STATUS",
T0."DocNum",
T0."NAME",
T0."CARDNAME",
T0."FINISHED" ,
T1."PoPhAmt",
T1."PhBudget",
T1."TotalAP"
ORDER BY
T0."DocNum"
Aggregate before joining:
select *
from basic_table t left join
(select t1.project, count(*) as cnt1, sum(value) as value1
from t1
group by t1.project
) t1
on t.FIPROJECT = T1.Project left join
(select t2.project, count(*) as cnt2, sum(value) as value2
from t2
group by t2.project
) t2
on t.FIPROJECT = T2.Project left join
(select t3.project, count(*) as cnt3, sum(value) as value3
from t3
group by t3.project
) t3
on t.FIPROJECT = T3.Project;
SELECT T.ID,T.VALUE,T1.VALUE T_1_VALUE,T2.VALUE T_2_VALUE,T3.VALUE T_3_VALUE
FROM BASIC_TABLE T
LEFT JOIN TABLE_1 T1 ON T.ID=T1.ID
LEFT JOIN TABLE_2 T2 ON T.ID=T2.ID
LEFT JOIN TABLE_3 T3 ON T.ID=T3.ID
WITH BASIC_TABLE(REFERENCE,BASIC_VALUE) AS
(
SELECT 1, 'a' UNION ALL
SELECT 2 , 'b' UNION ALL
SELECT 3 , 'c' UNION ALL
SELECT 4 , 'd' UNION ALL
SELECT 5 , 'e' UNION ALL
SELECT 6 , 'f' UNION ALL
SELECT 7 , 'g' UNION ALL
SELECT 8 , 'h'
),
TABLE_1(REFERENCE,T1_VALUE) AS
(
SELECT 1, 'i' UNION ALL
SELECT 2, 'j' UNION ALL
SELECT 2, 'x' UNION ALL
SELECT 3, 'k' UNION ALL
SELECT 4, 'l'
),
TABLE_2(REFERENCE,T2_VALUE)AS
(
SELECT 1, 'm' UNION ALL
SELECT 5, 'n' UNION ALL
SELECT 7, 'o' UNION ALL
SELECT 7, 'y' UNION ALL
SELECT 8 , 'p'
),
TABLE_3(REFERENCE,T3_VALUE)AS
(
SELECT 2, 'q' UNION ALL
SELECT 4, 'r' UNION ALL
SELECT 6, 's' UNION ALL
SELECT 8, 't' UNION ALL
SELECT 8, 'z'
)
SELECT B.REFERENCE,B.BASIC_VALUE,ISNULL(T1.R,'')AS SUM_T_1_VALUE,ISNULL(T2.R,'')AS SUM_T_2_VALUE,
ISNULL(T3.R,'')AS SUM_T_3_VALUE
FROM BASIC_TABLE AS B
LEFT JOIN
(
SELECT T.REFERENCE,STRING_AGG(T.T1_VALUE,'+')R
FROM TABLE_1 AS T
GROUP BY T.REFERENCE
)T1 ON B.REFERENCE=T1.REFERENCE
LEFT JOIN
(
SELECT T.REFERENCE,STRING_AGG(T.T2_VALUE,'+')R
FROM TABLE_2 AS T
GROUP BY T.REFERENCE
)T2 ON B.REFERENCE=T2.REFERENCE
LEFT JOIN
(
SELECT T.REFERENCE,STRING_AGG(T.T3_VALUE,'+')R
FROM TABLE_3 AS T
GROUP BY T.REFERENCE
)T3 ON B.REFERENCE=T3.REFERENCE
Not sure about SAP HANA, but in MS SQL Server 2017 this code produces more or less the required output
use following query STRING_AGG function with group by Basic_Table.Reference
SELECT
T.Reference,
MAX(T.Basic_Value) AS Basic_Value,
SUM(T1.T1_Value) AS T1_Value,
SUM(T2.T2_Value) AS T2_Value,
SUM(T3.T3_Value) AS T3_Value
FROM BASIC_TABLE T
LEFT JOIN TABLE_1 T1 ON T.Reference=T1.Reference
LEFT JOIN TABLE_2 T2 ON T.Reference=T2.Reference
LEFT JOIN TABLE_3 T3 ON T.Reference=T3.Reference
GROUP BY T.Reference

Left join only on first row

I have the following sample query:
WITH a As
(
SELECT '2020-04-01' as date,'test123' as id,'abc' as foo,10 as purchases Union all
SELECT '2020-04-01', 'test123', 'abc', 0 Union all
SELECT '2020-04-01', 'test123', 'abc', 0
),
b as
(
SELECT '2020-04-01' as date,'test123' as id,'abc' as foo,50 as budget
)
select
a.date,a.id,a.foo,a.purchases,budget
from a
LEFT JOIN b
ON
concat(a.date,a.id)=concat(b.date,b.id)
and I'd like the following output
Row date,id,foo,purchases,budget
1 2020-04-01,test123,abc,10,50
2 2020-04-01,test123,abc,0,null
3 2020-04-01,test123,abc,0,null
I read many questions on the similar topic but I wasn't able to make it work.
You can use row_number():
select a.date, a.id, a.foo, a.purchases,
(case when a.seqnum = 1 then b.budget end) as budget
from (seleect a.*, row_number() over (partition by date, id order by purchases desc) as seqnum
from a
) a
b
using (date, id);
In the b inner query add this to the columns being selected:
ROW_NUMBER() OVER(PARTITION BY Date ORDER BY Purchases DESC) AS SequenceNumber
Then in your LEFT JOIN add "AND b.SequenceNumber = 1"
If you really are hard-coding everything and not selecting from an actual table, it would be:
WITH a As
(
SELECT '2020-04-01' as date,'test123' as id,'abc' as foo,10 as purchases Union all
SELECT '2020-04-01', 'test123', 'abc', 0 Union all
SELECT '2020-04-01', 'test123', 'abc', 0
),
b as
(
SELECT '2020-04-01' as date,'test123' as id,'abc' as foo,50 as budget
)
select
a.date,a.id,a.foo,a.purchases,budget
from a
LEFT JOIN b
ON a.date = b.date
AND a.id = b.id
AND a.purchases > 0
Otherwise, if you have a table in the inner "b" query. Then it would be something like this:
WITH a As
(
SELECT '2020-04-01' as date,'test123' as id,'abc' as foo,10 as purchases Union all
SELECT '2020-04-01', 'test123', 'abc', 0 Union all
SELECT '2020-04-01', 'test123', 'abc', 0
),
b as
(
SELECT '2020-04-01' as date,'test123' as id,'abc' as foo,50 as budget,
ROW_NUMBER() OVER(PARTITION BY date ORDER BY Id) SequenceNumber
)
select
a.date,a.id,a.foo,a.purchases,budget
from a
LEFT JOIN b
ON a.date = b.date
AND a.id = b.id
AND b.SequenceNumber = 1

retrieve row from multiple row of table in oracle

I want to retrieve data from three table
for example
Table_1 : NAME_A
PD_ID
A
B
C
Table_2 : NAME_B Primary_key PD_ID,EV_N
PD_ID EV_N EV_DEC
A 1 one
A 2 Two
B 1 one
B 2 Two
B 3 THREE
C 1 one
C 2 Two
Table_3 : NAME_C Primary key PD_ID
PD_ID, FFT_NAME, FFT_DESC
A XY XY_DESC
B ZY ZY_DESC
B XY XY_DESC
C ZY ZY_DESC
C XY XY_DESC
C PY PY_DESC
Required Output
PD_ID EV_N EV_DEC FFT_NAME FFT_DESC
A 1 ONE XY XY_DESC
A 2 TWO
B 1 ONE ZY ZY_DESC
B 2 TWO XY XY_DESC
B 3 THREE
C 1 ONE ZY ZY_DESC
C 2 Two XY XY_DESC
PY PY_DESC
The idea is to range records in both tables and then use this range numbers in a full outer join:
with
t1 as (
select 'A' pd_id from dual union all
select 'B' pd_id from dual union all
select 'C' pd_id from dual
),
t2 as (
select 'A' pd_id, 1 EV_N, 'one' EV_DEC from dual union all
select 'A' pd_id, 2 EV_N, 'two' EV_DEC from dual union all
select 'B' pd_id, 1 EV_N, 'one' EV_DEC from dual union all
select 'B' pd_id, 2 EV_N, 'two' EV_DEC from dual union all
select 'B' pd_id, 3 EV_N, 'three' EV_DEC from dual union all
select 'C' pd_id, 1 EV_N, 'one' EV_DEC from dual union all
select 'C' pd_id, 2 EV_N, 'two' EV_DEC from dual
),
t3 as (
select 'A' pd_id, 'XY' FFT_NAME, 'XY_DESC' FFT_DESC from dual union all
select 'B' pd_id, 'ZY' FFT_NAME, 'ZY_DESC' FFT_DESC from dual union all
select 'B' pd_id, 'XY' FFT_NAME, 'XY_DESC' FFT_DESC from dual union all
select 'C' pd_id, 'ZY' FFT_NAME, 'ZY_DESC' FFT_DESC from dual union all
select 'C' pd_id, 'XY' FFT_NAME, 'XY_DESC' FFT_DESC from dual union all
select 'C' pd_id, 'PY' FFT_NAME, 'PY_DESC' FFT_DESC from dual
)
select coalesce(t22.pd_id,t33.pd_id) pd_id,
t22.ev_dec,
t33.FFT_NAME,
t33.FFT_DESC
from (
select pd_id, ev_n, ev_dec, row_number() over (partition by pd_id order by ev_n, ev_dec) rn
from t2
) t22
full join (
select pd_id, FFT_NAME, FFT_DESC, row_number() over (partition by pd_id order by FFT_NAME, FFT_DESC) rn
from t3
) t33
on t22.pd_id = t33.pd_id
and t22.rn = t33.rn
This won't produce the exact output you specify but it will produce a consistent and predictable output:
select t1.PD_ID
, t2.EV_N
, t2.EV_DEC
, t2.FFT_NAME
, t2.FFT_DESC
from name_a t1
cross join ( select coalesce(b.p_id, c.p_id) as p_id
, b.ev_n
, upper(b.ev_dec) as ev_dec
, c.fft_name
, c.fft_desc
from ( select * from name_b ) b
full outer join
( select c.*
, row_number() over (partition by c.p_id
order by c.fft_name) as rn
from name_c c) c
on c.p_id = b._pid
and c.rn = b.ev_n) t2
where t1.p_id = t2.p_id
order by t1.p_id
, t2.ev_n nulls last

Assign values from another table based on time range

Let's say I have T1 :
id date attribute
1 2017-04-20 t
1 2017-04-19 t
1 2017-04-18 t
2 2017-04-20 t
2 2017-04-19 f
And I also have T2 :
id date date_end attribute
1 2017-04-19 2017-04-25 f
What I want is to get a new table, which would contain data from T1 but in case there is matching ids in both table, replace the attribute value in T1 with the attribute value in T2 for the date range defined in T2.
The result would look like this:
id date attribute
1 2017-04-20 f
1 2017-04-19 f
1 2017-04-18 t
2 2017-04-20 t
2 2017-04-19 f
What I have come up with is the following:
SELECT t1.id, t1.date,
CASE WHEN max(T2.id) IS NULL THEN T1.attribute ELSE T2.attribute END
FROM T1
LEFT JOIN T2 using (id)
Can't figure out how to change the attributes for the particular range.
Any help is appreciated!
I think this is just LEFT JOIN with COALESCE():
SELECT t1.id, t1.date,
COALESCE(t2.attribute, t1.attribute) as attribute
FROM T1 LEFT JOIN
T2
ON t1.id = t2.id AND
t1.date >= t2.date and t1.date <= t2.date_end;
For BigQuery Standard SQL
#standardSQL
SELECT
a.id,
a.date,
CASE
WHEN a.date BETWEEN b.date AND b.date_end THEN b.attribute
ELSE a.attribute
END AS attribute
FROM T1 AS a
LEFT JOIN T2 AS b
ON a.id = b.id
You can play/test it with sample data from your question
#standardSQL
WITH T1 AS (
SELECT 1 AS id, '2017-04-20' AS date, 't' AS attribute UNION ALL
SELECT 1, '2017-04-19', 't' UNION ALL
SELECT 1, '2017-04-18', 't' UNION ALL
SELECT 2, '2017-04-20', 't' UNION ALL
SELECT 2, '2017-04-19', 'f'
),
T2 AS (
SELECT 1 AS id, '2017-04-19' AS date, '2017-04-25' AS date_end, 'f' AS attribute
)
SELECT
a.id,
a.date,
CASE
WHEN a.date BETWEEN b.date AND b.date_end THEN b.attribute
ELSE a.attribute
END AS attribute
FROM T1 AS a
LEFT JOIN T2 AS b
ON a.id = b.id
-- ORDER BY 1,2 DESC

join 2 tables with SQL

I have to join 2 tables with SQL in a special way:
TABLE1 has the fields GROUP and MEMBER, TABLE2 has the fields GROUP and MASTER.
I have to build a new TABLE3 with the fields GROUP and ID by copying TABLE1 to TABLE3 and search TABLE2 if there is a GROUP from TABLE1 and if, copy GROUP and MASTER to TABLE3.
Example:
table1:
group member
1 a
1 b
1 c
2 x
3 y
table2:
group master
3 n
3 z
1 k
9 v
2 m
7 o
8 p
Expected result, table3:
group id
1 a from table1
1 b from table1
1 c from table1
1 k from table2
2 x from table1
2 m from table2
3 y from table1
3 z from table2
3 n from table2
I hope everything's clear.
So what is the SQL query?
Thanks, Hein
The first part (copy members) should be easy:
INSERT INTO table3 (group, id) SELECT group, member FROM table1;
Then You just copy the masters, that are in groups, that are already present in table1:
INSERT INTO table3 (group, id) SELECT group, master FROM table2 WHERE group IN (SELECT DISTINCT group FROM table1);
Try this out. Of course you need to INSERT the whole selection to your new table named Table3.
WITH TABLE1(GRP,MMBR) AS
(SELECT 1, 'a' FROM DUAL UNION ALL
SELECT 1, 'b' FROM DUAL UNION ALL
SELECT 1, 'c' FROM DUAL UNION ALL
SELECT 2, 'x' FROM DUAL UNION ALL
SELECT 3, 'y' FROM DUAL),
TABLE2(GRP,MSTR) AS
(SELECT 3, 'n' FROM DUAL UNION ALL
SELECT 3, 'z' FROM DUAL UNION ALL
SELECT 1, 'k' FROM DUAL UNION ALL
SELECT 9, 'v' FROM DUAL UNION ALL
SELECT 2, 'm' FROM DUAL UNION ALL
SELECT 7, 'o' FROM DUAL UNION ALL
SELECT 8, 'p' FROM DUAL)
SELECT * FROM (
SELECT GRP, MMBR ID FROM TABLE1
UNION --UNION ALL if you need duplicates
SELECT GRP, MSTR ID FROM TABLE2
WHERE TABLE2.GRP IN (SELECT GRP FROM TABLE1)
)
ORDER BY GRP, ID
You can do it using UNION ALL and 2 simple SELECT in an INSERT as follows:
INSERT INTO table3(group,id)
SELECT group,id FROM table1
UNION ALL
SELECT group,id FROM table2
SELECT * FROM table3;
And if you don't want duplicate values,try this using UNION instead of UNION ALL:
INSERT INTO table3(group,id)
SELECT group,id FROM table1
UNION
SELECT group,id FROM table2
SELECT * FROM table3;