SQL to select child rows only if all parents meet criteria - sql

I have two tables Data and Parents. Basically what I want to do is make a search query that only selects a row from the Data table if all its parents have been tested (Tested = True). The following are examples of my tables:
Data
-------------------
ID Version Tested
A 1.1 True
B 1.2 True
C 0.2 False
D 0.6 False
-
Parents
-------------------
ChildID ParentID
C A
C B
D A
D B
D C
In this case C would only be returned in the query since it is the only one with parents that have all been tested:
Query
-------------------
ChildID Version Tested
C 0.2 False

Try:
select *
from data d
where not exists
(select 1
from parents p
where p.childid = d.id
and parentid in (select id from data where tested <> 'True'))
http://sqlfiddle.com/#!2/1bf20f/1/0
The only reason C is not the only one returned (A and B are also) is because A and B do not have any parents, do you want them to not be returned on that basis? (it is unknown whether all their parents have been tested because they have no parents)
If you do want them removed on that basis, use the following, and C will be the only returned:
http://sqlfiddle.com/#!2/1bf20f/7/0
select *
from data d
where not exists
(select 1
from parents p
where p.childid = d.id
and parentid in (select id from data where tested <> 'True'))
and id in (select childid from parents)

Related

count of record in one table not exist in multiple tables

I have a station tables consider table A,table B, table C,table D;
consider these table have some production data and consider there tables as a my production stations and in each station table i have a single column status which shows the flag 1,2
and in each table there is 1 primary number which is my serial no;
the flag 1 defines "OK" product and flag 2 defines "Fail Product";
but if the product get failed in table A then it may be reworked and introduced to production table A,B & C and that can be may make flag 1 in that station.
now i want to display the product which is failed in Table A and not further processed in table B and Table C.
means I want to display the total scrap product which is not processed in any station once it is failed
I have taken the data of failed product from each station into another table
and then i am comparing that serial no with each station table with flag 1
but not able to find exact how much count of scrap serial no's from each table
In Pseudo-SQL:
Select count(serial_no)
from table "Failedparts"
where /* serial_no neither in table "b" nor in table "c" with status = 1 */
;
From your verbal description it is a bit unclear which state you wish to query:
a failure on station A without subsequent production attempts ( disregarding their outcome )
or
a failure on station A with subsequent failures on both, station B and C
The following queries operate on the original tables according to your verbal description. It is unclear how exactly you filled table "Failedparts" ( your description suggests that you loaded failed attempts from each table A, B, C ).
Case 1. would translate into
SELECT count(a.serial_no)
FROM table_a a
WHERE a.status = 2
AND NOT EXISTS ( SELECT 1 FROM table_b b WHERE b.serial_no = a.serial_no )
AND NOT EXISTS ( SELECT 1 FROM table_c c WHERE c.serial_no = a.serial_no )
;
For case 2., use:
SELECT count(a.serial_no)
FROM table_a a
JOIN table_b b ON ( b.serial_no = a.serial_no AND b.status = 2 )
JOIN table_c c ON ( c.serial_no = a.serial_no AND c.status = 2 )
WHERE a.status = 2
;
(Possibly you aim at a slightly different notion, but the above queries should get you started).
Literally as you commented in the query stub
Select count(serial_no)
from table "Failedparts" a
where /* serial_no neither in table "b" nor in table "c" with status = 1 */
not exists (select 1 from table_b b where b.serial_no = a.serial_no and b.flag = 1)
and not exists (select 1 from table_c c where c.serial_no = a.serial_no and c.flag = 1);

SELECT JOIN Table Operations

In my query below, I joined tables b, c, d to a by the base_id column.
However, I need to do some operations in my SELECT statement.
Since b, c, and d are not joined to each other,
is my (a.qty - (b.qty - (c.qty + d.qty))) formula computing only those tables that have the same base_id column?
SELECT (a.qty - (b.qty - (c.qty + d.qty))) AS qc_in
FROM receiving a
LEFT JOIN (
SELECT SUM(qty) AS qty, base_id
FROM quality_control bb
WHERE location_to = 6
AND is_canceled = 0
GROUP BY base_id
) b
ON b.base_id = a.base_id
LEFT JOIN (
SELECT SUM(qty) AS qty, base_id
FROM quality_control ba
WHERE location_from = 6
AND is_canceled = 0
GROUP BY base_id
) c
ON c.base_id = a.base_id
LEFT JOIN (
SELECT SUM(qty) AS qty, base_id
FROM issuance
WHERE location_from = 6
AND is_canceled = 0
GROUP BY base_id
) d
ON d.base_id = a.base_id
WHERE a.is_canceled = 0
I think you're confused by how joining works (if I'm reading the question correctly). If you have:
select *
from table1 a
join table2 b
on a.Id = b.Id
join table3c
on a.Id = c.Id
Then yes a is joined to b, and b is joined to c, but that also means that a is joined to c. One way to think of it is as one giant in-memory table that has all of the a columns then all of the b columns and then all of the c columns in the one result.
If a.Id is 1, and you select the row from b where Id is the same (1) and then you join to c where the Id is the same as a (1), then a, b and c all have the same id.
So yes, (a.qty - (b.qty - (c.qty + d.qty))) will only be doing that calculation for rows where a, b, c and d all have the same base_id .
Surely. In the nested statements you join all the tables on base_id. That means, as the result of those joins you will get a huge table containing columns from all of the joined tables with a common column you join on (base_id in your case).

How to get all children of parent item when all are in the same row and table

I have a table of parts and sub-parts where each record contains the primary part for that record along with its ChildPart.
Part - ChildPart
A - B
A - C
A - D
C - F
C - Z
F - R
Z - R
Q - B
Q - C
So for the example above, part A has 7 total descendants (B, C, D, F, Z, R, R). A parent part can have multiple children and a child part can belong to more than 1 parent; notice that part B is used for both A and Q.
How can I efficiently show all the child parts of a given parent part just using joins and not using SQL cursors or loops? The hierarchical tree could theoretically be infinitely deep.
You can use a Recursive CTE:
DECLARE #pID VARCHAR(20) = 'A'
;WITH CTE AS (
SELECT ChildPart
FROM mytable
WHERE Part = #pID
UNION ALL
SELECT t1.ChildPart
FROM mytable AS t1
INNER JOIN CTE AS t2 ON t1.Part = t2.ChildPart
)
SELECT ChildPart
FROM CTE

Linking and counting records with one or multiple criteria

I am attempting to link together some records to identify sibling groups. The way we can do this is to identify clients who share the same parents.
A sample of the SQL follows:
SELECT
A.ClientID,
B.ParentID
FROM A
LEFT JOIN B ON B.ClientID to A.ClientID
AND B.REL_END is NULL AND B.REL_CODE = 'PAR'
Which would return data in the following format:
Client ID Parent ID
1 A
1 B
2 C
2 D
3 C
3 E
4 C
4 D
How I would like it to appear follows:
Client ID No. of Siblings
1 0
2 2
3 2
4 2
Hopefully the table shows that child 1 has 0 siblings (shares no parents with 2,3,4), child 2 has 2 siblings (3 and 4), child 3 has 2 siblings (2 and 4) and child 4 has 2 siblings (2,3). It seems like it should be simple enough to achieve this but I'm really struggling at the minute to think how! I think it's made slightly more confusing because a child may share only one parent with another child to be considered as a sibling.
Hopefully this is clear, thanks.
Edit: Just to make it clear, the relationship is identified by a child sharing a parent ID with another child (the ids are unique, but I provided generic ones for this example). So as Child 2, 3 and 4 all have a parent with an id of C they are considered siblings.
You may try this query, it displays to me the desired output.
with c_data as (
select a.clientid, b.parentid, count(a.clientid) over (partition by parentid order by parentid) as c_parents
FROM A
LEFT JOIN B ON (B.ClientID = A.ClientID)
AND B.REL_END is NULL AND B.REL_CODE = 'PAR'
)
select clientid as "Client ID", max(c_parents) -1 as "No of Siblings"
from c_data
group by clientid;
Example:
SQL> with c_data as (
2 select a.clientid, b.parentid, count(a.clientid) over (partition by parentid order by parentid) as c_parents
3 FROM A
4 LEFT JOIN B ON (B.ClientID = A.ClientID)
5 AND B.REL_END is NULL AND B.REL_CODE = 'PAR'
6 )
7 select clientid as "Client ID", max(c_parents) -1 as "No of Siblings"
8 from c_data
9 group by clientid;
Client ID No of Siblings
---------- --------------
1 0
2 2
4 2
3 2
Transcurrido: 00:00:00.03
SQL>
With the analytic function we count all client id's partitioned by the parentid related of the current tupple to count all clients that have in common the same parent.
After in the projection, we get the max number of parent in common for each client and substracts 1, the client itself.
Hope this helps!
Regards!
This is rather complicated. If you can assume exactly two parents for each child, then you can do something like:
select c.*, count(*) over (partition by min_parent, max_parent) - 1 as NumSiblings
from (SELECT A.ClientID, min(B.ParentID) as min_parent, max(b.parentid) as max_parent
FROM A LEFT JOIN
B
ON B.ClientID to A.ClientID AND B.REL_END is NULL AND B.REL_CODE = 'PAR'
group by a.clientid
) c
What this does is calculate the two parents for each client. It then uses the windows function to count the number of clients that have exactly the same parents. The "-1" is because all children are counted, and we don't want to count the current child.
If you can have more than two parents, then the query is more complicated.
If you want only one parent being shared (rather than two), then you can handle using a self join:
with cp as (SELECT A.ClientID, B.ParentID
FROM A LEFT JOIN
B
ON B.ClientID to A.ClientID AND B.REL_END is NULL AND B.REL_CODE = 'PAR'
)
select cp.client_id, count(distinct cp1.client_id) as NumSiblings
from cp left outer join
cp cp1
on cp.parent_id = cp1.parent_id and cp.client_id <> cp1.client_id
group by cp.client_id
Well, I don't understand how the table and relation are made, but you could do something like that :
SELECT ClientID, sum(NumberOfSibling) as NumberOfSibling
from(
SELECT A.ClientID, (select count(ClientID)
from A2
LEFT JOIN B2 ON B2.ClientID to A2.ClientID
AND B2.REL_END is NULL AND B2.REL_CODE = 'PAR'
where B2.ParentID = B.ParentID) as NumberOfSibling
FROM A
LEFT JOIN B ON B.ClientID to A.ClientID
AND B.REL_END is NULL AND B.REL_CODE = 'PAR'
) GROUP BY ClientID
Explanation : I took your request and modify the second result to return the sum of siblings wich possess the same Parent (that's the select count(*) ..., that's also the part I'm not sure about the conditions). And put this in another to group by ClientID to have the sum of the 2 parents.

Joining 3 Tables Using Newest Rows

I have 3 tables in my database: children, families, statuslog
Every time a child is checked in or out of the database, it is updated in the statuslog. I've done this a long time ago, but I can't seem to figure out how to do it anymore. I want to create a new view that joins all 3 tables, but I only want the newest entry from statuslog (by using the highest id).
For example, statuslog looks like this:
childID researcher status id
1 Dr. A out 1
1 Dr. A in 2
1 Dr. B out 3
1 Dr. B in 4
2 Dr. C out 5
2 Dr. C in 6
3 Dr. B out 7
3 Dr. B in 8
This is what I want to do:
SELECT *
FROM children, families, statuslog
WHERE children.familyID = families.familyID AND children.childID = statuslog.childID
Obviously, this will return the children+families tuples coupled with every single log entry, but I can't remember how to only combine it with the newest log entry.
Any help would be appreciated!
Aggregate query with max(id) retrieves last ID given a childID. This is then joined to statuslog to retrieve other columns.
SELECT *
FROM children
INNER JOIN families
ON children.familyID = families.familyID
INNER JOIN
(
SELECT childID, researcher, status
FROM statuslog
INNER JOIN
(
SELECT childID, max(ID) ID
FROM statuslog
GROUP BY childID
) lastSL
ON statuslog.childID = lastSL.childid
AND statuslog.ID = lastSL.ID
) sl
ON children.childID = sl.childID
This seems to be the typical greatest-n-per-group in which the higher id is interpreted as the newest. This query should do the trick:
select * from (
select s1.* from statusLog s1
left join statusLog s2
on s1.childId = s2.childId and s1.id < s2.id
where s2.id is null
) final
join children c on c.childId = final.childId
join families f on f.familyId = c.familyId
Correct any syntactical errors.