SQL query over 2 different tables for some common value grouping - sql

I have 2 tables, one for student/class & the other for student/interests as below, for example;
studentID class
---------------------
4134 1
4135 1
4136 1
4137 1
4138 2
4139 2
4140 2
4141 2
studentID interests
---------------------
4134 basketball
4134 football
4135 basketball
4136 basketball
4137 football
4138 swimming
4138 football
4139 running
4140 tennis
4141 tennis
What will be the best way to query about which students have same interests with his classmates ONLY? The hardest part is the requirement of ONLY.
The query should not result in 4135, 4136, 4140 & 4141. As even 4134 has same interest with 4137, both of their interests are same as 4138, who is not in class 1.

I have created two table for your data -
CREATE TABLE #temp (id INT , class INT)
CREATE TABLE #temp2 (id INT, activity VARCHAR(500))
INSERT INTO #temp
SELECT 4134 , 1 UNION ALL
SELECT 4135 , 1 UNION ALL
SELECT 4136 , 1 UNION ALL
SELECT 4137 , 1 UNION ALL
SELECT 4138 , 2 UNION ALL
SELECT 4139 , 2 UNION ALL
SELECT 4140 , 2 UNION ALL
SELECT 4141 , 2
INSERT INTO #temp2
SELECT 4134 , 'basketball' UNION ALL
SELECT 4134 , 'football' UNION ALL
SELECT 4135 , 'basketball' UNION ALL
SELECT 4136 , 'basketball' UNION ALL
SELECT 4137 , 'football' UNION ALL
SELECT 4138 , 'swimming' UNION ALL
SELECT 4138 , 'football' UNION ALL
SELECT 4139 , 'running' UNION ALL
SELECT 4140 , 'tennis' UNION ALL
SELECT 4141 , 'tennis'
AND use select statement
SELECT DISTINCT t.id id
FROM #temp2 t
INNER JOIN #temp2 t1 ON t.activity = t1.activity AND t.id <> t1.id
INNER JOIN #temp t3 ON t.id = t3.id
INNER JOIN #temp t4 ON t1.id = t4.id
WHERE t3.class = t4.class AND t.id NOT IN (SELECT DISTINCT te.id id
FROM #temp2 te
INNER JOIN #temp2 te1 ON te.activity = te1.activity AND te.id <> te1.id
INNER JOIN #temp te3 ON te.id = te3.id
INNER JOIN #temp te4 ON te1.id = te4.id
WHERE te3.class <> te4.class)
This will return -
id
4135
4136
4140
4141

I have tried writing query using joins and partition by just have a look at it
SELECT * FROM
(SELECT STUD_ID,CLASS,INTREST,COUNT(INTREST) OVER(partition BY INTREST) AS RR FROM
(SELECT COUNT(*) OVER( PARTITION BY A.STUD_ID ) CN, B.STUD_ID,A.CLASS,B.INTREST FROM
STUDENTS A
JOIN
INTREST B
ON
A.STUD_ID = B.STUD_ID )
WHERE CN =1 )WHERE RR >1 ;

Related

Find exactly equal rows in 2 tables, both in terms of value and number

I have two Table, that both of them have 2 field (provinceid,cityid)
i want to find provinceid that have exactly the same cityid in this two table.
for example i have this tables:
table1:
provinceid
cityid
1
1
1
2
2
3
2
4
3
6
table2:
provinceid
cityid
1
1
1
5
2
3
2
4
3
6
3
7
i want a query that just return provinceid =2 and city id =3 and 4.
i try this query and it is right. but i want a better query:
select provinceid ,t1.cityid
from t1
left join t2 on t1=provinceid=t2.provinceid and t1.cityid=t2.cityid
where t2.provinceid is not null and t2.cityid is not null
and t1.provinceid not in (select provinceid
from t2
left join t1 on t1=provinceid=t2.provinceid and t1.cityid=t2.cityid
where t1.provinceid is not null and t1.cityid is not null)
thank you
Try this :
select t1.provinceid ,t1.cityid
from table1 t1 join table2 t2
on t1.provinceid=t2.provinceid
and t1.cityid=t2.cityid
and t1.provinceid in (
select distinct(t1.provinceid)
from
(select provinceid, count(provinceid) as cnt from table1 group by provinceid) as t1
cross join
(select provinceid ,count(provinceid) as cnt from table2 group by provinceid) as t2
where t1.cnt = t2.cnt);
Output:
provinceid
cityid
1
1
2
3
2
4
The simplest method for an exact match is to use string aggregation. The exact syntax varies by database, but in Standard SQL this looks like:
select t1.provinceid, t2.provinceid
from (select provinceid,
listagg(cityid, ',') within group (order by cityid) as cities
from t1
group by provinceid
) t1 join
(select provinceid,
listagg(cityid, ',') within group (order by cityid) as cities
from t2
group by provinceid
) t2
on t1.cities = t2.cities;
If you want the provinceids to be the same as well, just add t1.provinceid = t2.provinceid to the on clause.
Or, if you want the provinceids to be the same, you can use full join instead:
select provinceid
from t1 full join
t2
using (provinceid, cityid)
group by provinceid
having count(*) = count(t1.cityid) and count(*) = count(t2.cityid);
Besides match in provid and cityid, we are looking for exactly matching sets of records as well. There might be many different methods to this. I prefer to have string comparison for list of cities for each provide with addition to provide and cityid match clause to remove other sets of provide and cityid which are available in tables but not the exact row match.
WITH table1 AS(
SELECT 1 AS PROVID, 1 AS CITYID FROM DUAL UNION ALL
SELECT 1 AS PROVID, 2 AS CITYID FROM DUAL UNION ALL
SELECT 2 AS PROVID, 3 AS CITYID FROM DUAL UNION ALL
SELECT 2 AS PROVID, 4 AS CITYID FROM DUAL UNION ALL
SELECT 3 AS PROVID, 6 AS CITYID FROM DUAL
),
table2 AS (
SELECT 1 AS PROVID, 1 AS CITYID FROM DUAL UNION ALL
SELECT 1 AS PROVID, 5 AS CITYID FROM DUAL UNION ALL
SELECT 2 AS PROVID, 3 AS CITYID FROM DUAL UNION ALL
SELECT 2 AS PROVID, 4 AS CITYID FROM DUAL UNION ALL
SELECT 3 AS PROVID, 6 AS CITYID FROM DUAL UNION ALL
SELECT 3 AS PROVID, 7 AS CITYID FROM DUAL
),
listed_table1 AS (
SELECT
a.provid,
listagg(cityid,',') within GROUP (ORDER BY cityid) list_city
FROM table1 a
GROUP BY a.provid
),
listed_table2 AS (
SELECT
a.provid,
listagg(cityid,',') within GROUP (ORDER BY cityid) list_city
FROM table2 a
GROUP BY a.provid
)
SELECT
t1.provid, t1.cityid
FROM
(SELECT x.*, x1.list_city FROM table1 x, listed_table1 x1 WHERE x.provid = x1.provid) t1,
(SELECT y.*, y1.list_city FROM table2 y, listed_table2 y1 WHERE y.provid = y1.provid) t2
WHERE t1.provid = t2.provid AND t1.cityid = t2.cityid AND t1.list_city = t2.list_city
;
You can use (union ..)except (inner join..) to detect non-matches. Step by step
with u12 as (
select PROVID, CITYID from table1
union
select PROVID, CITYID from table2
),
c12 as (
select t1.PROVID, t2.CITYID
from table1 t1
join table2 t2 on t1.PROVID=t2.PROVID and t1.CITYID=t2.CITYID
),
nonMatch as (
select distinct PROVID
from (
select PROVID, CITYID from u12
except
select PROVID, CITYID from c12
) t
)
select *
from table1 t
where not exists (
select 1
from nonMatch n
where n.PROVID = t.PROVID);
If a number of doubles counts then count them first
with t1 as (
select PROVID, CITYID, count(*) n
from table1
group by PROVID, CITYID
),
t2 as (
select PROVID, CITYID, count(*) n
from table2
group by PROVID, CITYID
),
u12 as (
select PROVID, CITYID, n from t1
union
select PROVID, CITYID, n from t2
),
c12 as (
select t1.PROVID, t1.CITYID, t1.n
from t1
join t2 on t1.PROVID = t2.PROVID and t1.CITYID = t2.CITYID and t1.n = t2.n
),
nonMatch as (
select distinct PROVID
from (
select PROVID, CITYID, n from u12
except
select PROVID, CITYID, n from c12
) t
)
select *
from table1 t
where not exists (
select 1
from nonMatch n
where n.PROVID = t.PROVID)
db<>fiddle

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

how to find collegue Id in table

i have one table as Employee that have two column Id and Branch. i have to find its collegue Id except his own id from table without using subquery.
Id Branch
==============
1 Delhi
2 Mumbai
3 Delhi
4 Delhi
5 Mumbai
6 Mumbai
if i enter e.g. 3 then my answer has to be 1 and 4.
Without a subquery means with a self join:
select tt.*
from tablename t inner join tablename tt
on t.Branch = tt.Branch and tt.id <> t.id
where t.id = 3
See the demo.
use self join
select t2.id from table_name t1
join table_name t2 on t1.Branch=t2.Branch
where t1.id=3 and t2.id!=3
for data preparation easily i used CTE version below with result
with cte as
(
select 1 as id, 'Delhi' as b
union all
select 2, 'Mumbai'
union all
select 3 , 'Delhi'
union all
select 4 , 'Delhi'
) select t2.id from cte t1 join cte t2 on t1.b=t2.b
where t1.id=3 and t2.id!=3
demo link
here is the output
id
1
4
This query should give you the answer you're after:
SELECT E1.Id
FROM Employee E1
INNER
JOIN Employee E2
ON E1.[Branch] = E2.[Branch]
WHERE E2.Id = 3
AND E1.Id <> 3
It works by joining the Employee table onto itself, by Branch and then:
WHERE E2.Id = 3 - Where the Employee is Id # 3
AND E1.Id <> 3 - excluding any records from the original Employee table where Id is 3
Here's the script I used to create your test data (in SQL Server) for the purposes of validating the query:
CREATE TABLE [Employee]
(
[Id] INT,
[Branch] NVARCHAR(10)
)
INSERT
INTO [Employee]
(
Id,
[Branch]
)
VALUES (1, 'Delhi'),
(2, 'Mumbai'),
(3, 'Delhi'),
(4, 'Delhi'),
(5, 'Mumbai'),
(6, 'Mumbai')

How do I find specific values in a table in Oracle?

Suppose my table is TEST_123 Which has the following records:
id | cid | result
------------------
1 | C-1 | TAM
2 | C-1 | TAM
3 | C-2 | RAM
4 | C-2 | TAM
5 | C-3 | SAM
6 | C-3 | SAM
Now I want such cid's which has only one type of result, so the answer should be C-1 AND C-3 but not C-2 since it has two different type of results. Need Oracle query for this?
You simple need to understand GROUP BY and HAVING clause.
The answer is as simple as
select cid
from TEST_123
group by cid
having count(distinct result) = 1
Note group by selects the distinct keys from CID; the having filters on condition valid for all the records in the group, in your case count(distinct result) = 1
Use exists, its a little bit tricky cause every group result should be same
select t1.* from TEST_123 t1 where exists(
select 1 from TEST_123 t2 where t2.cid=t1.cid
and t2.result=t1.result
group by t2.cid,t2.result
having count(*)=
(select count(*) from TEST_123 t3
where t3.cid=t2.cid)
)
Exmple
with TEST_123 as
(
select 1 as id , 'c-1' as cid , 'tam' as result from dual
union all
select 2 as id , 'c-1' as cid , 'tam' as result from dual
union all
select 3 as id , 'c-2' as cid , 'tam' as result from dual
union all
select 4 as id , 'c-2' as cid , 'ram' as result from dual
)
select distinct t1.cid from TEST_123 t1 where exists(
select 1 from TEST_123 t2 where t2.cid=t1.cid
and t2.result=t1.result
group by t2.cid,t2.result
having count(*)=
(select count(*) from TEST_123 t3
where t3.cid=t2.cid)
)
demo
Based on #zaynul's answer, here is another variation:
with TEST_123 as
(
select 1 as id , 'c-1' as cid , 'tam' as result from dual
union all
select 2 as id , 'c-1' as cid , 'tam' as result from dual
union all
select 3 as id , 'c-2' as cid , 'tam' as result from dual
union all
select 4 as id , 'c-2' as cid , 'ram' as result from dual
)
select * from test_123 where cid in (
select cid from test_123 group by cid having count(distinct result) = 1);
select t.cid from
(select cid, count(*) as count from table_1 group by cid, result) t
group by t.cid
having count(*)=1;
Should work for you
I would use NOT EXISTS :
SELECT t.*
FROM table t
WHERE NOT EXISTS (SELECT 1 FROM table t1 WHERE t1.cid = t.cid AND t1.result <> t.result);

TSQL Finding Incomplete courses

From the StudentDetails Table
StudentDetails
SID Name CourseCompleted
1 Andrew CS001
1 Andrew CS002
1 Andrew CS003
2 Grey CS001
2 Grey CS005
2 Grey CS002
3 Jon CS002
3 Jon CS005
3 Jon CS008
How to generate the folowing output ( Course not completed by each student)
SID Name Course Not Completed
1 Andrew CS005
1 Andrew CS008
2 Grey CS003
2 Grey CS008
3 Jon CS001
3 Jon CS003
select distinct a.SID, a.Name, b.CourseCompleted as `Course Not Completed`
from StudentDetails a,
(select distinct CourseCompleted from StudentDetails) b
where not exists
(select 1 from StudentDetails where SID = a.SID and CourseCompleted = b.CourseCompleted)
order by a.SID
select s.SID, s.Name, c.Course as [Course Not Completed]
from (select distinct CourseCompleted [Course] from StudentDetails) c,
StudentDetails s
where not exists (
select * from StudentDetails where SID=s.SID and CourseCompleted=c.Course
)
Of course, if you have a table listing all possible courses, you could replace the subquery in the from clause with that table.
With StudentDetails As
(
SELECT 1 SID, 'Andrew' Name, 'CS001' CourseCompleted union all
SELECT 1, 'Andrew', 'CS002' union all
SELECT 1 , 'Andrew' , 'CS003' union all
SELECT 2 , 'Grey' , 'CS001' union all
SELECT 2 , 'Grey' , 'CS005' union all
SELECT 2 , 'Grey' , 'CS002' union all
SELECT 3 , 'Jon' , 'CS002' union all
SELECT 3 , 'Jon' , 'CS005' union all
SELECT 3 , 'Jon' , 'CS008'
),
Courses AS
(
SELECT DISTINCT CourseCompleted AS Course
FROM StudentDetails
),
Students As
(
SELECT DISTINCT SID, Name
FROM StudentDetails
)
SELECT s.SID, s.name, c.Course AS [Course not Completed] FROM Students s
CROSS JOIN Courses c
EXCEPT
SELECT SID, name, CourseCompleted
FROM StudentDetails
ORDER BY s.SID, c.Course