Select Containers having subitems in given range - sql

I have two tables:
Project:
Id
Name
1
ABC
2
DEF
3
GHI
4
JKL
5
MNO
6
PQR
Attachment:
Id
Status
ProjectId
1
a1
1
2
a1
1
3
a2
2
4
a2
2
5
a1
3
6
a2
3
7
a1
4
8
a2
4
9
a3
4
10
a1
5
11
a2
5
12
a4
5
I'd like to get projectnames which has assignments only with statuses a1 and a2 (must have both of them) and doesn't havve assignments in statuses a3 and a4.
So the result should be:
GHI
What I've tried wass:
select distinct
p.Name
from
Attachment a
inner join Project p on p.Id = a.ProjectId
group by
p.Name, a.status
having (a.Status = 'a1' or a.Status = 'a2'

You can use exist|not exists keywords to achieve this.
select [Name] from Project t1
where
exists (select 1 from Attachment where [Status] = 'a1' and ProjectId = t1.Id)
and
exists (select 1 from Attachment where [Status] = 'a2' and ProjectId = t1.Id)
and
not exists (select 1 from Attachment where [Status] = 'a3' and ProjectId = t1.Id)
and
not exists (select 1 from Attachment where [Status] = 'a4' and ProjectId = t1.Id)

use String_agg,Subquery and join to get your desired result
SELECT name
FROM (SELECT P.name,
String_agg(status, ',')
within GROUP (ORDER BY status) Status
FROM project P
join attachment A
ON A.projectid = P.id
GROUP BY P.name) t
WHERE status = 'a1,a2'
dbfiddle

You can use count distinct in combination with NOT IN:
select P.name
from Project P
left join Attachment A on P.id = A.ProjectId
where p.id not in (select A2.ProjectId
from Attachment A2
where A2.status in ('a3', 'a4'))
and A.status in ('a1', 'a2')
group by P.name
having count(distinct A.status) = 2
Here is a demo

Related

Subquery - case when - subselect - group by - count

Given 2 tables, I need a grouped summary of the awards for a shopping. More items means more awards.
tab_basket
id
name
shopping_no
type
001
Mike
00001
A
002
Mike
00001
A
003
Mike
00001
A
004
Tom
00002
B
005
Tom
00002
B
006
Tony
00003
A
007
Heinz
00004
A
tab_award
items
award
type_award
1
0.05
A
2
0.50
A
3
0.90
A
4
1.00
A
1
0.15
B
2
0.70
B
3
1.10
B
4
1.30
B
I need following table as result.
award
items
shopping_no
type _award
0.90
3
00001
A
0.70
2
00002
B
0.05
1
00003
A
0.05
1
00004
A
My solution attempt. I every time only get null as result for award (the else result) or the error "sql0811 result of select more than one row".
I am happy for any help, thanks!
SELECT award,
items,
shopping_no,
type_award
FROM tab_basket t
LEFT OUTER JOIN tab_award u ON t.TYPE = u.type_award
AND CASE WHEN (SELECT COUNT(shopping_no)
FROM tab_basket m
WHERE t.id = m.id
AND t.name = m.name
AND t.shopping_no = m.shopping_no
AND t.TYPE = u.TYPE
GROUP BY shopping_no ) = '1' THEN '1'
case WHEN (SELECT COUNT(shopping_no)
FROM tab_basket m
WHERE t.id = m.id
AND t.name = m.name
AND t.shopping_no = m.shopping_no
AND t.TYPE = u.TYPE
GROUP BY shopping_no ) = '2' THEN '2'
CASE WHEN (SELECT COUNT(shopping_no)
FROM tab_basket m
WHERE t.id = m.id
AND t.name = m.name
AND t.shopping_no = m.shopping_no
AND t.TYPE = u.TYPE
GROUP BY shopping_no ) = '3' THEN '3'
CASE WHEN (SELECT COUNT(shopping_no)
FROM tab_basket m
WHERE t.id = m.id
AND t.name = m.name
AND t.shopping_no = m.shopping_no
AND t.TYPE = u.TYPE
GROUP BY shopping_no ) = '4' THEN '4'
ELSE 'no_price'
END = u.award
You can first aggregate your tab_basket table and then can join that with tab_award table -
SELECT TB.award, TB.items, TA.shopping_no, TB.type_award
FROM (SELECT name, shopping_no, type, COUNT(*) CNT
FROM tab_basket
GROUP BY name, shopping_no, type) TB
JOIN tab_award TA ON TB.type = TA.type_award
AND TB.CNT = TA.items

Compare rows with condition

Edit: when 5 or 9 does not exist, i need a null value (or another flag)
I have 3 columns. SECTION, STATUS and NAME. Within a SECTION there are a maximum of 10 rows (STATUS 1 to 10). I have to compare the value of NAME for STATUS 5 and 9 within a SECTION. AND then indicate if those 2 NAMES (for STATUS 5 and 9) are the same for each SECTION.
section status name
1 5 a
1 6 a
1 9 b
2 4 c
2 5 d
2 9 d
2 10 d
3 5 e
3 10 e
Desired output
Section equalnames
1 no
2 yes
3 null/flag
select
a.section,
case
when a.name = b.name then 'YES'
when a.name <> b.name then 'NO'
when (a.name is null or b.name is null) then 'NULL' end
from
(select * from <table> where status = 5) a
full join (select * from <table> where status = 9) b
on a.section = b.section
With conditional aggregation:
SELECT section,
MAX(CASE WHEN status = 5 THEN name END) =
MAX(CASE WHEN status = 9 THEN name END) equalnames
FROM tablename
WHERE status IN (5, 9)
GROUP BY section
ORDER BY section
See the demo.
Results:
section | equalnames
------- | ----------
1 | f
2 | t
3 | null
You could try using a left join on same table for 5 and 9
select a.section, a.status s5, a.name n5, b.status b9, b.name n9
, case when a.name = b.name the yes
when a.name is null or b.name is nul the NULL
when a.name <> b.name then no end equalname
from my_table a
left join my_table b a.section = b.section and a.status =5 and b.status=9
A more optimised solution could be with cte and window function:
with cte as (
select section, count(status)over(partition by section,name order by section) count_with_same_name,
count(status)over(partition by section order by section) count_with_different_name
from tname where status in (5,9))
select section,(case when (max(count_with_same_name)=2) then 'Yes' when (max(count_with_different_name)=2) then 'No' else 'null/flag' end)
from cte
group by section
Output:
I think you just want conditional aggregation:
select section,
(case when min(case when status = 5 then name end) =
min(case when status = 9 then name end)
then 'yes'
when count(case when status in (5, 9) then status end) < 2
then 'null/flag'
else 'no'
end)
from t
group by section;
No join is needed and I would not advise one for this problem.

SQL Server Join Query returns redundant rows

I have two table
tblMaster tblTrans
ID Desc ID IDMaster Qty Garage
== ====== == ========= ===== =====
1 Type1 1 1 1 1
2 Type2 2 2 2 1
3 1 3 2
4 2 2 2
5 1 2 3
6 2 4 3
And i want this output when i join them :
ID Desc Garage1Qty Garagae2Qty Garage3Qty Garage4Qty
== ==== =========== =========== =========== ==========
1 Type1 1 3 2 null
2 Type2 2 2 4 null
Note that the "Garage" value is something that could be added in the future. So how do i achieve that? Tried this one:
SELECT M.*, Garage1Qty.*, Garage2Qty.* FROM tblMaster M
LEFT JOIN ( SELECT a.Id, b.Qty FROM tblMaster a JOIN tblTrans b on a.Id =b.Id WHERE Garage = 1 ) as Garage1Qty on Garage1Qty.Id = M.Id )
LEFT JOIN ( SELECT a.Id, b.Qty FROM tblMaster a JOIN tblTrans b on a.Id =b.Id WHERE Garage = 2 ) as Garage2Qty on Garage2Qty.Id = M.Id )
but it always returns something like:
ID Desc Garage1Qty Garage2Qty Garage3Qty Garage4Qty
== ==== =========== =========== =========== ==========
1 Type1 1 null null null
1 Type1 null 3 null null
1 Type1 null null 2 null
2 Type2 2 null null null
2 Type2 null 2 null null
2 Type2 null null 4 null
Well, you could use subquery
select ID, Desc,
(select sum(Qty) from tblTrans where IDMaster = m.ID and Garage = 1) as Garage1Qty,
(select sum(Qty) from tblTrans where IDMaster = m.ID and Garage = 2) as Garage2Qty,
(select sum(Qty) from tblTrans where IDMaster = m.ID and Garage = 3) as Garage3Qty,
(select sum(Qty) from tblTrans where IDMaster = m.ID and Garage = 4) as Garage4Qty
from tblMaster m;
In other way, you could also do that via conditional aggregation
select m.ID, m.Desc,
sum(case when t.Garage = 1 then t.Qty else 0 end) as Garage1Qty,
...
sum(case when t.Garage = 4 then t.Qty else 0 end) as Garage4Qty
from tblMaster m left join tblTrans t
on t.IDMaster = m.ID
group by m.ID, m.Desc;

I have a table "Student", now I would like to query the result as same as the second table. One group must have 2 students.

I have a table Student, now I would like to query the result the same as the second table. One group must have 2 students.
Student ID Name Gender Group
1 A M A1
2 B F A1
3 C M A2
4 D M A2
5 E F A3
6 F F A3
Name1 Gender1 Name2 Gender2 Group
A M B F A1
C M D M A2
E F F F A3
Something like:
select t1.name, t1.gender, t2.name, t2.gender, t1.group
from student t1, student t2
where t1.group = t2.group and t1.id < t2.id
Here is the complete example.
CREATE TABLE testing
(id char(10),
name char(10),
gender char(1),
grp char(10))
insert into testing values ('1','A','M','A1')
insert into testing values ('2','B','F','A1')
insert into testing values ('3','C','M','A2')
insert into testing values ('4','D','M','A2')
insert into testing values ('5','E','F','A3')
insert into testing values ('6','F','F','A3')
select name1, gender1, name2, gender2, a.grp from
(
SELECT name1 = CASE CONVERT(int,id)%2 WHEN 1 THEN name ELSE null END,
gender1 = CASE CONVERT(int,id)%2 WHEN 1 THEN gender ELSE null END,
grp FROM testing where CONVERT(int,id)%2 =1
) a
left join
(
SELECT name2 = CASE CONVERT(int,id)%2 WHEN 0 THEN name ELSE null END,
gender2 = CASE CONVERT(int,id)%2 WHEN 0 THEN gender ELSE null END,
grp FROM testing where CONVERT(int,id)%2 =0
) b on a.grp=b.grp
SELECT s1.name AS Name1, s1.gender AS Gender1,
s2.name AS Name2, s2.gender AS Gender2,
groups.group_s
FROM (
SELECT MIN(id) AS st_first, MAX(id) AS st_second, group_s
FROM students
GROUP BY group_s
) AS groups
LEFT JOIN students s1 ON groups.st_first=s1.id
LEFT JOIN students s2 ON groups.st_second=s2.id
students is the name of your table and group_s is the group column

Design SQL Query for following case

Consider tables
Table1
id, name
1 xyz
2 abc
3 pqr
Table2
id title
1 Mg1
2 Mg2
3 SG1
Table3
Tb1_id tb2_id count
1 1 3
1 2 3
1 3 4
2 2 1
3 2 2
3 3 2
I want to do query to give result like
id title
1 MG1
2 MG2
3 Two or More Title
MG1 has higher preference if MG1 and count >= 1 then it is given as MG1 title , for others corresponding title is used and for count > 1 as two or more
I think this is what you are going for:
select t3.Tb1_id as id,
case
when mg1cnt.count >= 1 then 'MG1'
when cnt.count = 1 then upper(t2.title)
else 'Two or More Titles'
end as title
from (
select Tb1_id, count(*) as count
from Table3
group by Tb1_id
) cnt
inner join (
select Tb1_id, isnull(SUM(case when t2.title='mg1' then 1 end), 0) as count
from Table3 t3
inner join Table2 t2 on t3.tb2_id = t2.id
group by Tb1_id
) as mg1cnt on cnt.Tb1_id = mg1cnt.Tb1_id
inner join Table3 t3 on cnt.Tb1_id = t3.Tb1_id
inner join Table2 t2 on t3.tb2_id = t2.id
group by t3.Tb1_id,
case
when mg1cnt.count >= 1 then 'MG1'
when cnt.count = 1 then upper(t2.title)
else 'Two or More Titles'
end