SQL query with one to many relation - sql

I have following table
Table1
id name col1 col2 col3 col4
-----------------------------------
1 test 1.1 1.2 1.3 1.4
2 test2 2.1 2.2 2.3 2.4
Table2
id fk_table1 amt type(fk_table3)
-----------------------------------
1 1 2 1
2 1 3 1
3 1 9 2
4 2 1 1
and I want to query such that I have get below result
id | name | total_type1_amt |total_type2_amt | col1 col2 col3 col4
-----------------------------------------------------------------------
1 test 5 (2+3) 9 1.1 1.2 1.3 1.4
2 test2 1 0 2.1 2.2 2.3 2.4
Basically in result I want group by table1.id with added columns for total_typeX_amt, there will be millions of rows in table1 and table2 so basically looking for optimized way to do it.

SELECT t1.id,
t1.name,
t2.total_type1_amt,
t2.total_type2_amt
FROM table1 t1
INNER JOIN
(
SELECT fk_table1,
SUM(CASE WHEN type = 1 THEN amt END) AS total_type1_amt,
SUM(CASE WHEN type = 2 THEN amt END) AS total_type2_amt
GROUP BY fk_table1
) t2
ON t1.id = t2.fk_table1
If you need this to run fast, you can try creating a view using the subquery (which I called t2 above), with an index on the fk_table1 column. Assuming that table1 also has an index on id, then the join should run reasonably fast.

It's not 100% your desired result, but you could try something like
select fk_table1, type, sum(amt)
from table1
inner join table2 on table1.id = table2.fk_table1
group by fk_table1, type
which should lead to something like
fk_table1 | type | sum
1 1 5
1 2 9
2 1 1

try dis to get total for total_type1_amt
select table1.id, table2.name ,(select count(table2.amt) as total_type1_amt where table1.id = table2.fk_table1 from table.1) from table1
inner join table2 on table1.id = table2.fk_table1
group by table.id

SELECT
T1.id,
T1.name,
SUM(CASE T2.type WHEN 1 THEN T2.amt ELSE 0 END) AS total_type1_amt,
SUM(CASE T2.type WHEN 2 THEN T2.amt ELSE 0 END) AS total_type2_amt
FROM #tbl1 T1
LEFT JOIN #tbl2 T2 ON T1.id=T2.fk_table1
GROUP BY T1.id,T1.name
Output:

You can try like this
;WITH cte
AS (SELECT
fk_table1, SUM([1]) total_type1_amt, COALESCE(SUM([2]), 0) total_type2_amt
FROM #table1 PIVOT (MAX(amt) FOR type IN ([1], [2])) p
GROUP BY fk_table1)
SELECT
t.id, t.name, c.total_type1_amt, c.total_type2_amt
FROM #table1 t
LEFT JOIN cte c
ON t.id = c.fk_table1

There at least 2 ways:
SELECT t1.id,
t1.name,
COALESCE(SUM(CASE WHEN [type] = 1 THEN amt END),0) AS total_type1_amt,
COALESCE(SUM(CASE WHEN [type] = 2 THEN amt END),0) AS total_type2_amt,
col1,
col2,
col3,
col4
FROM table1 t1
LEFT JOIN table2 t2
ON t1.id = t2.fk_table1
GROUP BY t1.id, t1.name, col1, col2, col3, col4
Another:
SELECT *
FROM (
SELECT t1.id,
t1.name,
t2.[type],
SUM(t2.amt) as sum
FROM table1 t1
LEFT JOIN table2 t2
ON t1.id = t2.fk_table1
GROUP BY t1.id, t1.name, t2.[type]
) as t
PIVOT (
MAX(sum) FOR type IN ([1],[2])
) as pvt

Related

SQL join two tables that have the same columns, with an overlapping `id` column, but merge based on if table1.col1 >= table2.col1

I want to join two tables that have the same columns, with an overlapping id column, but merge based on if table1.col1 >= table2.col1. This is in SQL.
If table1.col1>=table2.col1, use the columns from table1.
If table1.col1< table2.col1, then use columns from table2.
If the id does not exist in table1 but exists in table2, use the columns from table2
If the id does not exist in table2 but exists in table1, use the columns from table1
For example:
Table1:
id
col1
col2
col3
A
3
5
4
B
1
2
3
C
8
9
7
Table2:
id
col1
col2
col3
A
2
5
6
B
5
7
8
D
2
3
4
I want the result to be:
id
col1
col2
col3
A
3
5
4
B
5
7
8
C
8
9
7
D
2
3
4
I have tried union, full outer join, and CASE statements, but am stuck
I think individual case expressions for each column might be best:
select id,
(case when t1.col1 < t2.col1 then t2.col1 else t1.col1 end) as col1,
(case when t1.col1 < t2.col1 then t2.col2 else t1.col2 end) as col2,
(case when t1.col1 < t2.col1 then t2.col3 else t1.col3 end) as col3
from t1 full join
t2
using (id);
If that is cumbersome, another approach uses not exists:
select t1.*
from t1
where not exists (select 1
from t2
where t2.id = t1.id and t2.col1 > t1.col1
)
union all
select t2.*
from t2
where not exists (select 1
from t1
where t2.id = t1.id and t1.col1 >= t2.col1
);
Another solution:
SELECT DISTINCT ON (id) *
FROM (
SELECT *
FROM table1
UNION ALL
SELECT *
FROM table2
) AS aux
ORDER BY id, col1 DESC;
I tried it in Postgresql.

Select with not exist in the same table with multiple fields

I need to select registers from table that not exist in the same table. I mean if i have this table:
ID VALUE1 VALUE2 VALUE3
1 1 1 1
2 2 2 1
3 3 4 1
4 1 5 1
5 2 2 2
6 3 4 2
7 1 8 2
8 2 2 2
The result of the query should be
ID VALUE1 VALUE2 VALUE3
1 1 1 1
4 1 5 1
Because the rest of the values are the same for the value1 and value2 but diferent value3. I mean the row 2 of the table is the same that the row 5.
I try to do something like but not works:
select t1.value1, t1.value2 from table1 t1 where value3=1
and not exist
(select t2.value1, t2.value2 from table2 t2
where t1.value1=t2.value1 and t1.value2=t2.value2 and value3=2)
Thank you in advise and sorry for my english
You can use the NOT EXISTS as follows:
SELECT *
FROM YOUR_TABLE T1
WHERE T1.VALUE3 = 1
AND NOT EXISTS
(SELECT 1
FROM YOUR_TABLE T2
WHERE T1.VALUE1 = T2.VALUE1
AND T1.VALUE2 = T2.VALUE2)
I think not exists does what you want:
select t1.*
from table1 t1
where t1.value3 = 1 and
not exist (select 1
from table2 t2
where t2.value1 = t1.value1 and
t2.value2 = t1.value2 and
t2.value3 = 2
);
That said, you can also use window functions:
select t1.*
from (select t1.*,
max(value3) over (partition by value1, value2) as max_value3
from table1 t1
where value3 in (1, 2)
) t1
where max_value3 = 1;
I think that you are on the right track with exists.
I would phrase your query like:
select t1.*
from table1 t1
where
t1.value3 = 1
and not exist (
select 1 from table1 t2
where t1.value1 = t2.value1 and t1.value2 = t2.value2 and t2.value3 = 2
)
Key points:
the exists subquery should be from table1 (your query uses table2 but it seems like this table does not actually exists)
you don't actually need to return columns from the exists subquery, since all it does is check if some row is produced - hence the select 1

Aggregate functions as column results from multiple tables

I have the following table structures:
Table1
--------------
Table1Id
Field1
Field2
Table2
------------
Table2Id
Table1Id
Field1
Field2
Table3
-----------
Table3Id
Table1Id
Field1
Field2
I need to be able to select all fields in Table1, count of records in Table2, and count of records in Table3 Where count of records in Table2 > count of records in Table3
Here is an example of expected output with the given data:
Table1 Data
-------------
1 Record1Field1 Record1Feild2
2 Record2Field1 Record2Feild2
3 Record3Field1 Record3Feild2
4 Record4Field1 Record4Feild2
Table2 Data
------------
1 1 Record1Field1 Record1Feild2
2 1 Record2Field1 Record2Feild2
3 2 Record3Field1 Record3Feild2
4 2 Record4Field1 Record4Feild2
5 2 Record5Field1 Record5Feild2
6 4 Record6Field1 Record6Feild2
7 4 Record6Field1 Record6Feild2
8 4 Record6Field1 Record6Feild2
Table3 Data
------------
1 2 Record1Field1 Record1Feild2
2 2 Record2Field1 Record2Feild2
3 3 Record3Field1 Record3Feild2
4 3 Record4Field1 Record4Feild2
5 3 Record5Field1 Record5Feild2
6 4 Record6Field1 Record6Feild2
Desired Results
Table1Id Field1 Field2 Table2Count Table3Count
1 Record1Field1 Record1Field2 2 0
2 Record2Field1 Recird2Field2 3 2
4 Record4Field1 Recird4Field2 3 1
Notice record 3 in Table 1 is not shown because the record count in Table2 is less than the record count in Table3. I was able to make this work using a very ugly query similar to the one below but feel there is a much better way to do this using joins.
SELECT
t1.Table1Id,
t1.Field1,
t1.Field2
(Select Count(Table2Id) From Table2 t2 Where t2.Table1Id = t1.Table1Id) as Table2Count,
(Select Count(Table3Id) From Table3 t3 Where t3.Table1Id = t1.Table1Id) as Table3Count,
From
Table1 t1
Where
(Select Count(Table2Id) From Table2 t2 Where t2.Table1Id = t1.Table1Id) > (Select Count(Table3Id) From Table3 t3 Where t3.Table1Id = t1.Table1Id)
Hard to test it without working examples but something along these lines should be a good starting point.
SELECT
t1.Table1Id,
t1.Field1,
t1.Field2,
COUNT(DISTINCT t2.Table2Id),
COUNT(DISTINCT t3.Table3Id)
From Table1 t1
LEFT OUTER JOIN Table2 t2 ON t1.Table1Id = t2.Table1Id
LEFT OUTER JOIN Table3 t3 ON t1.Table1Id = t3.Table1Id
GROUP BY t1.Table1Id
HAVING COUNT(DISTINCT t2.Table2Id) > COUNT(DISTINCT t3.Table3Id)
You could get all the value in t1 and the data form t2 e t3 for your comparision using a couple of join on grouped values
SELECT
t1.Table1Id
,t1.Field1
,t1.Field2
, tt2.count_t2
, tt3.count_t3
from table1 t1
join (
select Table1Id, count(*) count_t2
From Table2
group by Table1Id
) tt2 on tt2.Table1Id = t1.Table1Id
join (
select Table1Id, count(*) count_t3
From Table3
group by Table1Id
) tt3 on tt3.Table1Id = t1.Table1Id
where tt2.count_t2 < tt3.count_t3 <

require to form a sql query

I was working on preparing a query where I was stuck.
Consider tables below:
table1
id key col1
-- --- -----
1 1 abc
2 2 d
3 3 s
4 4 xyz
table2
id col1 foreignkey
-- ---- ----------
1 12 1
2 13 1
3 14 1
4 12 2
5 13 2
Now what I need is to select only those records from table1 for which the corresponding entries in table2 does not have say col1 value as 12.
So the challenge is after applying join even though it will skip for value 1 corresponding to col1 equal to 12 it still has another multiple rows whose values are say 13, 14 for which also they have same foreignkey. Now what I want is if there is a single row having value 12 then it should not pick that id at all from table1.
How can I form a query with this?
The output which i need is say from above table structure i want to get those records from table1 for which col1 value from table2 does not have value as 14.
so my query should return me only row 2 from table1 and not row 1.
Another way of doing that. The first two queries are just for making the sample data.
;WITH t1(id ,[key] ,col1) AS
(
SELECT 1 , 1 , 'abc' UNION ALL
SELECT 2 , 2 , 'd' UNION ALL
SELECT 3 , 3 , 's' UNION ALL
SELECT 4 , 4 , 'xyz'
)
,t2(id ,col1, foreignkey) AS
(
SELECT 1 , 12 , 1 UNION ALL
SELECT 2 , 13 , 1 UNION ALL
SELECT 3 , 14 , 1 UNION ALL
SELECT 4 ,12 , 2 UNION ALL
SELECT 5 ,13 , 2
)
SELECT id, [key], col1
FROM t1
WHERE id NOT IN (SELECT t2.Id
FROM t2
INNER JOIN t1 ON t1.Id = t2.foreignkey
WHERE t2.col1 = 14)
This is a typical case for NOT EXISTS:
SELECT id, [key], col1
FROM table1 t1
WHERE NOT EXISTS (SELECT 1
FROM table2 t2
WHERE t2.foreignkey = t1.id AND t2.col1 = 14)
The above query will not select a row from table1 if there is a single correlated row in table2 having col1 = 14.
Output:
id key col1
-------------
2 2 d
3 3 s
4 4 xyz
If you want to return records that, in addition to the criterion set above, also have correlated records in table2, then you can use the following query:
SELECT t1.id, MAX(t1.[key]) AS [key], MAX(t1.col1) AS col1
FROM table1 t1
INNER JOIN table2 t2 ON t1.id = t2.foreignkey
GROUP BY t1.id
HAVING COUNT(CASE WHEN t2.col1 = 14 THEN 1 END) = 0
Output:
id key col1
-------------
2 2 d
You can also achieve the same result with the second query using a combination of EXISTS and NOT EXISTS:
SELECT id, [key], col1
FROM table1 t1
WHERE EXISTS (SELECT 1
FROM table2 t2
WHERE t2.foreignkey = t1.id)
AND
NOT EXISTS (SELECT 1
FROM table2 t3
WHERE t3.foreignkey = t1.id AND t3.col1 = 14)
select t1.id,t1.key,
(select ROW_NUMBER() OVER(PARTITION BY col1 ORDER BY col1 DESC) AS Row,* into
#Temp from table1)
from table1 t1
inner join table2 t2 on t1.id=t2.foreignkey
where t2.col1=(select col1 from #temp where row>1)

Best practices for multi table join query

Tables structure are below :
Table1 (ID int, value1 int,...)
ID Value1
---- --------
1 10
2 20
5 12
Table2 (ID int, value2 int,...)
ID Value2
---- --------
1 13
3 24
4 11
Table3 (ID int, value3 int,...)
ID Value3
---- --------
4 150
5 100
My expected output is below.
ID Value1 Value2 Value3
---- -------- -------- --------
1 10 13 NULL
2 20 NULL NULL
3 NULL 24 NULL
4 NULL 11 150
5 12 NULL 100
It should be noted that above tables is huge and I want to have best performance.
My query suggestion is below :
Select ID,
SUM(Value1) AS Value1,
SUM(Value2) AS Value2,
SUM(Value3) AS Value3
From (
Select ID, Value1 , NULL as value2, NULL as value 3
From Table1
Union ALL
Select ID, NULL , value2, NULL
From Table2
Union ALL
Select ID, NULL, NULL, value 3
From Table3
)Z
Group By Z.ID
Assuming you only have one value per id, this should do the trick:
SELECT aux.ID, t1.Value1, t2.Value2, t3.Value3
FROM
(SELECT ID FROM Table1
UNION
select ID FROM Table2
UNION
SELECT ID FROM Table3) aux
LEFT OUTER JOIN Table1 t1 ON aux.ID = t1.ID
LEFT OUTER JOIN Table2 t2 ON aux.ID = t2.ID
LEFT OUTER JOIN Table3 t3 ON aux.ID = t3.ID
If you've more than one value:
SELECT aux.ID, SUM(t1.Value1) as 'Value1', SUM(t2.Value2) as 'Value2', SUM(t3.Value3) as 'Value3'
FROM
(SELECT ID FROM Table1
UNION
select ID FROM Table2
UNION
SELECT ID FROM Table3) aux
LEFT OUTER JOIN Table1 t1 ON aux.ID = t1.ID
LEFT OUTER JOIN Table2 t2 ON aux.ID = t2.ID
LEFT OUTER JOIN Table3 t3 ON aux.ID = t3.ID
GROUP BY aux.ID
I intially wrote the same answer as aF. did above. So, removed it, and used a different approach.
Here,
1st query get all from table1
2nd query gets all from table2 skipping
those already present in table1 3rd query gets all remaining skipping those in above two query.
SELECT T1.ID, T1.VALUE1, T2.VALUE2, T3.VALUE3 --all T1
FROM TABLE1 T1
LEFT JOIN TABLE2 ON T1.ID=T2.ID
LEFT JOIN TABLE3 ON T1.ID=T3.ID
UNION
SELECT T2.ID, T1.VALUE1, T2.VALUE2, T3.VALUE3 --all T2 where T1 is NULL
FROM TABLE1 T2
LEFT JOIN TABLE1 ON T2.ID=T1.ID
LEFT JOIN TABLE3 ON T2.ID=T3.ID
WHERE T1.ID IS NULL
UNION
SELECT T3.ID, T1.VALUE1, T2.VALUE2, T3.VALUE3 --all T3 where T1 is NULL AND T2 IS NUL
FROM TABLE1 T3
LEFT JOIN TABLE1 ON T3.ID=T1.ID
LEFT JOIN TABLE2 ON T3.ID=T2.ID
WHERE T1.ID IS NULL
AND T2.ID IS NULL