Select only complete groups in SQL? - sql

Suppose there are 2 tables as shown below. Table A contains rows in a few groups - 11 (1,2,3) and 22 (4,5,6,7) in this case. Table B contains book rows that indirectly reference groups in table A via B.a_id = A.a_id foreign key relationship):
Table A:
a_id grp
---------
1 11
2 11
3 11
4 22
5 22
6 22
7 22
Table B:
b_id a_id book
--------------
1 1 AA
2 2 AA
3 3 AA
4 1 BB
5 2 BB
6 3 BB
7 1 CC
8 3 CC
9 4 AA
10 5 AA
11 6 AA
12 4 BB
13 5 BB
14 6 BB
15 7 BB
16 6 CC
17 7 CC
I need to select only those book/group combinations that are complete, i.e.
a_id grp book
---------------
1 11 AA
2 11 AA
3 11 AA
1 11 BB
2 11 BB
3 11 BB
4 22 BB
5 22 BB
6 22 BB
7 22 BB
11 CC, 22 AA or 22 CC are not eligible, as they do not represent complete groups.
One way to get the desired result is to use row counts by each group in a subquery, but its seems kind of shaky. Any other ideas?
Thanks!
Edit: I don't think that the referenced question is really the same problem.

This is what I could come up with:
select
A.a_id, A.grp, B.book
from
A
inner join B
on A.a_id = B.a_id
left outer join
(select distinct A.grp, B.book
from A, B
where not exists
(
select *
from A a2
inner join B b2
on a2.a_id = b2.a_id
where
a2.a_id = A.a_id
and a2.grp = A.grp
and b2.book = B.book
)
) T1 -- < -- represents incomplete book/group combos
on A.grp = T1.grp
and B.book = T1.book
where
T1.grp is null -- < -- exclude incomplete book/group combos

Related

Nested group by with conditional query

I have 3 tables:
table: a
id name
2 A
3 B
table: b
id a b
3 2 Asd Ter Gsdt
4 2 Gsd Gsdt Gsda
5 2 Asd Gsd
6 3 Uty Kggs
7 3 Tyud Hffddf
table: c
id a b
6 3 d
7 3 a
8 3 g
9 3 h
10 4 j
11 5 y
12 5 s
13 6 d
14 6 h
expected output:
a b c d
A 2019-04-06 3 a
B 2019-04-06 6 b
I am unsure how to proceed from this, how?
This query do the job, but there is always a question about speed and performance.
select a.name,
(select c_date from c
join b on (c.b_id = b.id)
where b.a = a.id order by c_date desc limit 1) last_c_date,
popular.b_id,
(select photos->0 from b where id = popular.b_id) photo
from a
join (
select distinct on (a)
b.id b_id, a from b
join c on (b.id = c.b_id)
group by b.id, a
order by a, count(*) desc, b.id
) popular on (popular.a = a.id)
order by a.name
If there will be 2 equaly popular b objects in a region, query takes this with smaller id.
If it will be no b object with entries in c than subquery for photo can be surrounded with coalesce (but now it should work too with null value).

Records in one table not present in another table

I am trying to use LEFT JOIN, NOT EXISTS and NOT IN, where I want records from pursuit table, expect matching records from condition table using foreign key instance = id. When I execute the query, it shows empty (no records).
LEFT JOIN
SELECT p.id, p.dept
FROM
pursuit p LEFT JOIN condition c USING (dept)
WHERE
p.id = c.instance
NOT EXISTS
SELECT p.id, p.dept
FROM
pursuit p
WHERE
p.id NOT EXISTS IN (SELECT c.instance FROM condiiton c)
NOT IN
SELECT p.id, p.dept
FROM
pursuit p
WHERE
p.id NOT IN (SELECT c.instance FROM condiiton c)
TABLES
condition
id instance dept
1 3 5
5 2 5
2 7 5
3 4 5
4 10 5
5 2 3
6 15 6
pursuit
id name dept
3 C1 5
2 C9 5
7 C77 5
4 C9 5
10 C6 5
19 C23 5
17 C45 5
15 C12 5
23 C33 5
3,2,7,4 = matching records of dept 5
19,17,15,23 = not matching records of dept 5
Output
id name dept
19 C23 5
17 C45 5
15 C12 5
23 C33 5
Edit2:
SELECT p.id, p.dept
FROM pursuit p LEFT JOIN condition c
on (p.id = c.instance and p.dep=c.dep)
WHERE c.instance IS NULL
this returns rows of pursuit not present in condition table
I would use NOT EXISTS instead :
SELECT P.*
FROM pursuit p
WHERE NOT EXISTS (SELECT 1
FROM condition c
WHERE p.id = c.instance
);
The only problem were with NOT EXISTS you haven't established any relation between them.

Hiding a row where value = 0 but count its other column values in total calculations - sql2008

I have tables like that: (C1-C2 varchar(10), C3-Number int)
WaitingData
C1 C2 C3 Number
A B 1 10
A B 2 0
A B 3 4
X B 4 2
CompletedData
C1 C2 C3 Number
A B 1 5
A B 2 2
A B 3 0
X B 4 12
I am using the query below to represent the data:
Select wd.C1,wd.C2,wd.C3,wd.Number as NW,cdd.Number as NC
into #AllData
from (Select C1,C2,C3,sum(Number) from WaitingData group by C1,C2,C3) wd
outer apply (Select C1,C2,C3,sum(Number)
from CompletedData cd
where wd.C1=cd.C1 and wd.C2=cd.C2 and wd.C3=cd.C3
) cdd
Select * from #AllData
union
Select C1='Total',C2='Total',C3=-1, sum(NW),sum(NW)
from #AllData
This is giving me an output like:
C1 C2 C3 NW NC
A B 1 10 5
A B 2 0 2
A B 3 4 0
X B 4 2 12
Total Total -1 16 19
However, I want to hide the rows that has no NW but calculate its regarding values while calculating the Total row (see NC below). The output I want is like:
C1 C2 C3 NW NC
A B 1 10 5
A B 3 4 0
X B 4 2 12
Total Total -1 16 19
I could not find a way to provide an output like this. Any help would be so appreciated!
------------------------------EDIT---------------------------------------
------------------------------EDIT---------------------------------------
When I have data in the tables like below, the outer apply is not working like I want, it does not include the data A B 2.
WaitingData
C1 C2 C3 Number
A B 1 10
A B 3 4
X B 4 2
CompletedData
C1 C2 C3 Number
A B 1 5
A B 2 2
X B 4 12
And the output would be like:
C1 C2 C3 NW NC
A B 1 10 5
A B 3 4 NULL
X B 4 2 12
Total Total -1 16 17
In this situation, what can I do to count "2" NC value having by A B 2 on the final result and see NC as 19 instead 17, except inserting all the records that included by CompletedData but WaitingData? (need an efficient way)
Wrap the final result with one more select and exclude rows where NW = 0.
select * from
(
Select * from #AllData
union
Select C1='Total',C2='Total',C3=-1, sum(NW),sum(NC)
from #AllData
) t
where NW <> 0
Edit: Using a full join to get all values from both tables.
with t as
(select coalesce(w.c1,c.c1) as c1,coalesce(w.c2,c.c2) as c2,coalesce(w.c3,c.c3) as c3
, coalesce(w.number,0) as nw , coalesce(c.number,0) as nc
from waitingdata w
full join completeddata c on w.c1 = c.c1 and w.c2=c.c2 and w.c3=c.c3)
select * from
(select * from t
union all
Select C1='Total',C2='Total',C3=-1, sum(NW),sum(NC)
from t) x where nw <> 0
You can do all of this in one query, without temporary tables, intermediate results, subqueries, or UNION by using the ROLLUP operator:
SELECT
WD.C1,
WD.C2,
WD.C3,
SUM(WD.Number) AS NW,
SUM(CD.Number) AS NC
FROM
dbo.WaitingData WD
LEFT OUTER JOIN CompletedData CD ON
CD.C1 = WD.C1 AND
CD.C2 = WD.C2 AND
CD.C3 = WD.C3
GROUP BY
WD.C1,
WD.C2,
WD.C3
WITH ROLLUP
HAVING
GROUPING_ID(WD.C1, WD.C2, WD.C3) IN (0, 7) AND
SUM(WD.Number) <> 0

Update rows from other rows

how can i update this table (zz col):
id xx yy zz n
---- ----- ----- --- -----
1 AA 20 0 0
2 AA 10 0 1
3 AA 10 0 2
4 BB 45 0 0
5 BB 15 0 1
6 BB 15 0 2
7 BB 15 0 3
[zz = yy where n = 0]
To:
id xx yy zz n
---- ----- ----- --- -----
1 AA 20 20 0
2 AA 10 20 1
3 AA 10 20 2
4 BB 45 45 0
5 BB 15 45 1
6 BB 15 45 2
7 BB 15 45 3
Thanks
update tablename set zz=yy where n=0;
You can join on the same table to do your update.
UPDATE A1
SET A1.zz = A2.yy
from
A AS A1
inner join A AS A2 ON A1.xx = A2.xx AND A2.n = 0
You may need to try this way (not tested, but this should help you)
Your UPDATE statement needs to be a correlated update-- you need some key in A that tells you which row in B to go to in order to retrieve the new value. Your WHERE clause should also be an IN or an EXISTS since it needs to return multiple rows.
UPDATE table1 a
SET a.zz = (SELECT b.yy
FROM table1 b
WHERE b.n = 0
AND a.id = b.id)
WHERE EXISTS (SELECT 1
FROM table1 b
WHERE a.id = b.id);
Assuming the join results in a key-preserved view, you could also
UPDATE (SELECT a.id,
a.yy oldval,
x.yy newval
FROM table1 a,
(SELECT b.id,
b.xx,
b.yy
FROM table1 b
WHERE b.n = 0) x
WHERE a.xx = x.xx)
SET oldval = newval;
Try this query
update tablename
set a.zz=(select a.yy
from tablename a
where a.xx=tablename.xx and a.n=0)
you can try this:
UPDATE table1
SET table1.zz= table2.xx
FROM table2
JOIN table1
ON table1.id=table2.id

multiple result Rows in one row

I have this kind of result from a Select like this
Select a.Header1, a.Header2, a.Header3, b.header4,
From a
Join b on a.id = b.id
give this result
Head1 Head2 Head3 Head4
1 A 1 AA
1 A 1 BB
1 A 1 CC
1 A 2 AA
1 A 2 CC
1 A 2 DD
1 A 2 EE
Is it possible to have
Head1 Head2 Head3 Head4
1 A 1 AA BB CC
1 A 2 AA CC DD EE
Head4 being split in different columns
Not exactly. You can use the listagg() function to concatenate the values into a single column which you can then split after the fact. Example:
SELECT
a.Header1,
a.Header2,
a.Header3,
listagg(b.Header4, ', ') WITHIN GROUP (ORDER BY b.Header4) AS Header4
FROM a
JOIN b ON a.id = b.id
GROUP BY
a.Header1,
a.Header2,
a.Header3
Would yield:
Header1 Header2 Header3 Header4
1 A 1 AA, BB, CC
1 A 2 AA, CC, DD, EE