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

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

Related

Union v/s Inner join on 3 conditions in SQL

Will the following 2 SQL statements give same result?
SQL1-
A inner join B on (condition1)
union
A inner join B on (condition2)
union
A inner join B on (condition3)
SQL2-
A inner join B on (condition1) OR (condition2) OR (condition3)
At least it depends on A or B originally having doubles. For example
with A(c) as (
select 1 union all
select 1 union all
select 2
),
B(c) as (
select 1 union all
select 2 union all
select 3
)
select *
from A join B on A.c=B.c
union
select *
from A join B on A.c>B.c
returns 3 rows (distinct).
with A(c) as (
select 1 union all
select 1 union all
select 2
),
B(c) as (
select 1 union all
select 2 union all
select 3
)
select *
from A join B on A.c=B.c or A.c>B.c
returns 4 rows due to A having doubles.

PostgreSQL query: I want to find the count of each base in a DNA sequence

I have 5 random DNA sequence (20 length DNA base), I want to find the base count.
In the first section, I have prepared a DNA length function to generate a sequence of 5 x 20 DNA base lengths. But I would like to find the base count. How many "A" is in the sequence, how many "C" is in the sequence, how many "G" is in the sequence, and how many "T" is in the sequence.
prepare dna_length(int) as
with t1 as (select chr(65) as s union select chr(67) union select chr(71) union select chr(84) )
, t2 as ( select s, row_number() over() as rn from t1)
, t3 as ( select generate_series(1,$1) as i,round(random() * 4 + 0.5) as rn )
, t4 as ( select t2.s from t2 join t3 on (t2.rn=t3.rn))
select array_to_string(array(select s from t4),'') as dna;
with t1 as (
select 1 as rn, 'A' as s
union select 2, 'C'
union select 3, 'T'
union select 4, 'G'
), t2 as (
select generate_series(1, 5) as sample
), t3 as (
select t2.sample, generate_series(1,20) as i,
round(random() * 4 + 0.5) as rn
from t2
), t4 as (
select t3.sample, t3.i, t3.rn, t1.s
from t3
join t1 on t1.rn = t3.rn
)
select sample, string_agg(s, '' order by i)
from t4
group by sample
order by sample;
Right now it looks like this:
id DNA
1 ACTGCTGCAGTCGTACGTAC
2 TGCAGTCGTAGCTGACGTAG
3 GCAGTGACCAACGTGTGACA
4 TGACGTGTCGAGACGAAGAG
5 CGTGTGAGAGTCGTAGAGTG
The result should look like this:
id DNA A C G T
1 ACTGCTGCAGTCGTACGTAC 4 6 5 5
2 TGCAGTCGTAGCTGACGTAG 4 4 6 6
3 GCAGTGACCAACGTGTGACA 6 5 6 4
4 TGACGTGTCGAGACGAAGAG 4 3 8 3
5 CGTGTGAGAGTCGTAGAGTG 4 2 9 5
You can do conditional counts in the final query:
with ...
select
sample,
string_agg(s, '' order by i) dna,
count(*) filter(where s = 'A') a,
count(*) filter(where s = 'C') c,
count(*) filter(where s = 'G') g,
count(*) filter(where s = 'T') t
from t4
group by sample
order by sample;

How to write SQL join to find description of id using Oracle?

I have 2 input tables, and I need output in string format.
I tried following query, but it does not work. How can I get the above output?
with
cte1 as --table 1
(select 1 as id , 'A' as abc from dual
union
select 2 as id , 'B' as abc from dual
union
select 3 as id , 'C' as abc from dual
union
select 4 as id , 'D' as abc from dual
union
select 5 as id , 'E' as abc from dual
union
select 6 as id , 'F' as abc from dual
),
cte2 as --table2
(select 1 as id, 3 as name from dual
union
select 1 as id, 5 as name from dual
union
select 1 as id, 4 as name from dual
union
select 2 as id, 3 as name from dual
union
select 2 as id, 6 as name from dual
)
SELECT e.id, e.abc, m.id as mgr, e.abc, c.*
FROM
cte1 e, cte2 m, cte2 c
WHERE e.id = m.id
and
e.id=c.name;
You are trying to join each row in table 1 to two rows in table 2, and the conditions can never both be true.
You want to join each row in table 2 to two rows in table 1:
SELECT e.abc, m.abc
FROM cte2 c, cte1 e, cte1 m
WHERE e.id = c.id
AND m.id = c.name
ORDER BY c.id, c.name;
A A
- -
A C
A D
A E
B C
B F
or with 'modern' join syntax, which you should really be using:
SELECT e.abc, m.abc
FROM cte2 c
JOIN cte1 e ON e.id = c.id
JOIN cte1 m ON m.id = c.name
ORDER BY c.id, c.name;
A A
- -
A C
A D
A E
B C
B F

Get previous record where date is not null - SQL SERVER

I have two tables as below :
table 1:
ID B C D E Date
1 b c D E 2018/10/10
1 c d A B 2018/10/14
Table 2 :
ID B C Date
1 b c 2018/10/10
1 x y 2018/10/11
1 y x 2018/10/12
1 p q 2018/10/13
1 c d 2018/10/14
Table A has 6 columns, where as table 2 has 4 columns.
Using left join the result is :
Select * from Table2 t2 left join table1 t1
on t2.id=t1.id and t2.Date = t1.Date
Left join result is :
ID B C D E Date1 ID B C Date2
1 b c D E 2018/10/10 1 b c 2018/10/10
- - - - - - 1 x y 2018/10/11
- - - - - - 1 y x 2018/10/12
- - - - - 1 p q 2018/10/13
1 c d A B 2018/10/14 1 c d 2018/10/14
Note :
'-' represnt NULL.
Dates are sorted in left join result -- order by table2.date ,
table1.date asc
I have taken result of join in cte. not able to
generate final result.
Expected Result :
ID B C D E Date
1 b c D E 2018/10/10
1 x y D E 2018/10/11
1 y x D E 2018/10/12
1 p q D E 2018/10/13
1 c d A B 2018/10/14
Where Date from table1 is null in left join result, the search for previous non null date of table 1 , which would be just less than current date of table 2.
And fetch values of column D and E from there and keep the values of column B and C from current record where Date1 is null.
Being a newbie in sql, I am stuck in this. Please help.
Thanks in advance.
use outer apply with top 1. It will give your result, is fast and very short:
-- create data from example:
-- ------------------------
select * into #Table1 from
(select 1 as ID, 'b' as B, 'c' as C, 'D' as D, 'E' as E, cast('2018-10-10' as date) as [Date]
union all select 1, 'c', 'd', 'A', 'B', '2018-10-14')t
select * into #Table2 from
(select 1 as ID, 'b' as B, 'c' as C , cast('2018-10-10' as date) as [Date]
union all select 1, 'x', 'y', '2018-10-11'
union all select 1, 'y', 'x', '2018-10-12'
union all select 1, 'p', 'q', '2018-10-13'
union all select 1, 'c', 'd', '2018-10-14')t
-- SOLUTION
-- --------
select
T2.ID,
T2.B,
T2.C,
T1.D,
T1.E,
T2.[Date]
from
#Table2 T2
outer apply
(
select top 1 * from #Table1 T1
where T1.ID=T2.ID and T1.[Date] <= T2.[Date]
order by T1.[Date] desc
) T1
-- clean everything
-- ----------------
drop table #Table1
drop table #Table2
If you are using SQL Server 2012 or later, the following query returns the value you expected. I have used CTE and first_value() function and the query is optimized.
with
cte
as
(
select
t2.ID ,
t2.B ,
t2.C ,
t1.D ,
t1.E ,
t2.[Date] ,
sum(case when t1.D is null then 0 else 1 end) over (order by t2.[Date]) as D_partition,
sum(case when t1.E is null then 0 else 1 end) over (order by t2.[Date]) as E_partition
from
Table2 t2
left join
table1 t1
on
t2.id = t1.id
and
t2.[Date] = t1.[Date]
)
select
cte.ID ,
cte.B ,
cte.C ,
first_value(D) over(partition by D_partition order by D desc) as D ,
first_value(E) over(partition by E_partition order by E desc) as E ,
cte.Date
from
cte;
It seems you are left joining table2 to table 1, and you want the values of table1 if they exist, else from table2. This "if" in the level of data is typically implemented with the CASE function. However, in your case, we can use a more specific function, ISNULL(a,b), which returns a when a has a value and b when a is null:
select
t2.ID,
isnull(t1.B,t2.B) as B,
isnull(t1.C,t2.C) as C,
isnull(t1.D,t2.D) as D,
isnull(t1.E,t2.E) as E,
isnull(t1.[Date],t2.[Date]) as [Date]
from Table2 t2
left join table1 t1
on t2.id=t1.id and t2.Date = t1.Date
However, are you sure that t2.Date = t1.Date is needed? Ususally, the column named ID is unique/primary key, so that would make the additional join condition on date redundunt. You should remove it if this is the case.
Could you check to see if below is something you are looking for?
Select t2.ID,t2.B,t2.C,t1.D,t1.E, t2.Date from Table2 t2 left join table1 t1
on t2.id=t1.id and (t2.Date >= t1.Date)
where not exists (select 1 from table1 t12 where t2.Date > t1.Date and t2.Date >= t12.Date and t12.Date > t1.Date)
Here we tried to open table 1 two times (t1 and t12) to make sure the date in table 2 must >= a date in table1 and < an other date.

join tables on different amount of columns

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?