Upload
kirk
View
50
Download
0
Embed Size (px)
DESCRIPTION
Joins And Subqueries. Multi-table queries (JOIN) 2 tables. Retrieve the part name for all parts shipped in quantities equal to 200. SELECT DISTINCT PART.PartName FROM PART, SHIPMENT WHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200;. Conjunct #1 is a PK FK comparison. - PowerPoint PPT Presentation
Citation preview
JoinsAnd
Subqueries
Multi-table queries(JOIN) 2 tablesRetrieve the part name for all parts shipped in quantities equal to 200SELECT DISTINCT PART.PartNameFROM PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200;
Multi-table queries(JOIN) 2 tablesSELECT DISTINCT PART.PartNameFROM PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200;
So how does this work?
Conjunct #1 is a PK FK comparison
Conjunct #2 qualifies retrieval
Multi-table queries(JOIN) 2 tablesSELECT DISTINCT PART.PartNameFROM PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200;
Conceptually, we can understand the execution of the query in terms of row scans.
A Scan is a sequential inspection of many rows for the purpose of returning rows that meet a criteria.
The following demonstrations are a simplified version of the Nested Loop technique (as opposed to “merge sort” and “hashed join”)
Multi-table queries(JOIN) 2 tablesSELECT DISTINCT PART.PartNameFROM PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200;
PK/FK comparison requires one scan of the FK set for each PK in the parent table.Row is included in the view where PK=FK and Qty = 200
Multi-table queries(JOIN) 2 tablesSELECT DISTINCT PART.PartNameFROM PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200;
(Scan 1)
.
.
.(FK Scan)
(No Matches: TT)
(P1=P1 300=200)(P1=P2 200=200)
(P1=P5 100=200)
Multi-table queries(JOIN) 2 tablesSELECT DISTINCT PART.PartNameFROM PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200;
(Scan 2)
.
.
.(FK Scan)
(Two Matches: TT)
(P2=P1 300=200)(P2=P2 200=200)
(P2=P5 100=200)
This removes duplicates in results
Multi-table queries(JOIN) 2 tablesSELECT DISTINCT PART.PartNameFROM PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200;
(Scan 3)
.
.
.(FK Scan)
( No Matches: TT)
(P3=P1 300=200)(P3=P2 200=200)
(P3=P5 100=200)
Multi-table queries(JOIN) 2 tablesSELECT DISTINCT PART.PartNameFROM PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200;
(Scan 4)
.
.
.(FK Scan)
( Two Matches: TT)
(P4=P1 300=200)(P4=P2 200=200)
(P4=P5 100=200)
(And So On...)
Multi-table queries(JOIN) 2 tablesSELECT DISTINCT PART.PartNameFROM PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200;
(Scan 6)
How many row comparisons?
rows(PK-table) X rows(FK-table)
6 X 12 = 72
Multi-table queriesJOIN Evaluation Techniques
• Three common methods used by DBMS optimizers to evaluate Joins– Nested Loop– Merge Scan– Hash Join
Multi-table queriesJOIN Evaluation Techniques
• Nested Loop– Essentially the preceding demo– One table defined as external and one
table defined as internal– (External:Internal)– (1:M)– (Parent:Child)
– If there isn’t an index on the FK, the internal table has to be opened for a scan for every row of the external table
Multi-table queriesJOIN Evaluation Techniques
• Merge Scan– Both tables have to be ordered by PK/FK– Parallel scans are executed on both
tables– Qualifying rows are found by merging
the order lists into groups or partitions
Multi-table queriesJOIN Evaluation Techniques
• Hash Join– Both tables are stored using hash
function on join attributes (PK/FK)– Execute join on each partition
Multi-table queries(JOIN) 3 tablesRetrieve supplier name and part name for parts shipped in quantities less than 400.
SELECT DISTINCT SUPPLIER.SupplierName, PART.PartNameFROM SUPPLIER, PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400;
Multi-table queries(JOIN) 3 tablesSELECT DISTINCT SUPPLIER.SupplierName, PART.PartNameFROM SUPPLIER, PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400;
(Scan 1)
(One Match: TT T)
.
.
.(FK Scan)
(P1=P1 S1=S1 400>300)(P1=P2 S1=S1 400>200)
(P1=P5 S1=S4 400>100)
Multi-table queries(JOIN) 3 tablesSELECT DISTINCT SUPPLIER.SupplierName, PART.PartNameFROM SUPPLIER, PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400;
(Scan 2)
(No Matches: TT T)
.
.
.(FK Scan)
(P1=P1 S2=S1 400>300)(P1=P2 S2=S1 400>200)
(P1=P5 S2=S4 400>100)
Multi-table queries(JOIN) 3 tablesSELECT DISTINCT SUPPLIER.SupplierName, PART.PartNameFROM SUPPLIER, PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400;
(Scan 2-5)
(No Matches: TT T)
.
.
.
So we finish out the “P1” Supplier scan with no matches
Multi-table queries(JOIN) 3 tablesSELECT DISTINCT SUPPLIER.SupplierName, PART.PartNameFROM SUPPLIER, PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400;
(Scan 6)
(One Match: TT T)
.
.
.(FK Scan)
(P2=P1 S1=S1 400>300)(P2=P2 S1=S1 400>200)
(P2=P5 S1=S4 400>100)
Multi-table queries(JOIN) 3 tablesSELECT DISTINCT SUPPLIER.SupplierName, PART.PartNameFROM SUPPLIER, PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400;
(Scan 7)
(One Match: TT T)
.
.
.(FK Scan)
(P2=P1 S2=S1 400>300)(P2=P2 S2=S1 400>200)
(P1=P5 S2=S4 400>100)
Multi-table queries(JOIN) 3 tablesSELECT DISTINCT SUPPLIER.SupplierName, PART.PartNameFROM SUPPLIER, PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400;
(Scan 8)
(One Match: TT T)
.
.
.(FK Scan)
(P2=P1 S3=S1 400>300)(P2=P2 S3=S1 400>200)
(P1=P5 S3=S4 400>100)
(And so on...)
Multi-table queries(JOIN) 3 tablesSELECT DISTINCT SUPPLIER.SupplierName, PART.PartNameFROM SUPPLIER, PART, SHIPMENTWHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400;
How many row comparisons?
6x5x12 = 360
Multi-table queries(JOIN) 3 tablesRetrieve the supplier name and part name for all shipments with quantity greater than or equal to 200 for which the warehouse is located in the same city as the supplier
SELECT DISTINCT SUPPLIER.SupplierName, PART.PartNameFROM SUPPLIER, PART, SHIPMENTWHERE (PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber) AND (SHIPMENT.Quantity >= 200 AND SUPPLIER.SupplierCity = PART.PartCity);
Multi-table queries(JOIN) Let’s recap...For each row in the outer table (Parent), every row in the inner table (Child) is scanned for a PK/FK match and any qualifying predicate.
In your WHERE clause, you must have a PK=FK statement for every Parent/Child relationship involved in the query.
(PK=FK Scan)
Data
Data
Data
Data
Data
FK Value
FK Value
FK Value
FK Value
FK Value
PK Value
PK Value
PK Value
Data
Data
Data
(Outer Table) (Inner Table)
OuterTable
InnerTable
Sub-Queries (nested)UncorrelatedSo... Joins are really just the combination of the PRODUCT and SELECT relational operators.
But in the case where you have a qualifying predicate such as PartNumber=‘P2’, why do a PRODUCT of the outer table with the entire inner table?Why not eliminate some of the rows from the inner table first with a simple query and then do a PRODUCT of the resulting (and smaller) table?Recall, that the result of a relational operation is a table. So we can Join the outer table with the intermediate table resulting from the inner or sub-query.
Data
Data
Data
Data
Data
FK Value
FK Value
FK Value
FK Value
FK Value
PK Value
PK Value
PK Value
Data
Data
Data
(Outer Table) (Inner Table)Scan inner table
for PartNumber = ‘P2’
(Intermediate Table)
FK valueData
FK ValueData
FK ValueData
Remove non-
qualifying rows
Then Join Outer with
Intermediate
Sub-Queries (nested)Introduction
• Nested queries can be either correlated or uncorrelated– correlated:inner query depends on row that is
currently being examined in the outer query– uncorrelated: inner query performed
independently of outer query
• The nested or sub-query usually appears in the WHERE clause of a query
• Sub-queries can also appear in the FROM and HAVING clause
Sub-Queries (nested)Introduction
• General structure of uncorrelated sub-query
SELECT Item FROM table1 WHERE Item IN [NOT IN] (SELECT Item FROM table2 WHERE predicate);
(Inner Query)
(Outer Query)
Sub-Queries (nested)UncorrelatedRetrieve supplier names for suppliers who supply part P2
SELECT DISTINCTROW SUPPLIER.SupplierNameFROM SUPPLIERWHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber = 'P2');
Sub-Queries (nested)UncorrelatedSELECT DISTINCTROW SUPPLIER.SupplierNameFROM SUPPLIERWHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber = 'P2');
Intermediate
Now join outer table to intermediate result
Outer Result
Execute inner query: Scan SHIPMENT for ‘P2’
Sub-Queries (nested)UncorrelatedSELECT DISTINCTROW SUPPLIER.SupplierNameFROM SUPPLIERWHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber = 'P2');
Intermediate
Now join outer table to intermediate result
Outer Result
Sub-Queries (nested)UncorrelatedSELECT DISTINCTROW SUPPLIER.SupplierNameFROM SUPPLIERWHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber = 'P2');
How many row comparisons?12 + (5 x 4) = 32
Intermediate
If formulated as a join?(5 x 12) = 60
Sub-Queries (nested)UncorrelatedSELECT DISTINCTROW SUPPLIER.SupplierNameFROM SUPPLIERWHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber = 'P2');
Formulated as a Join
SELECT DISTINCT SUPPLIER.SupplierNameFROM SUPPLIER, SHIPMENTWHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber=‘P2’;
Use a join if you need columns from multiple tables. If, as in the example above, you need columns from only one table, use either a join or a subquery.
A subquery may include the GROUP BY and HAVING clauses, but not the ORDER BY and UNION.
Sub-Queries (nested)Uncorrelated (3 tables)Retrieve supplier name and city for all suppliers who supply at least one galvanized part.SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCityFROM SUPPLIERWHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber IN (SELECT PART.PartNumber FROM PART WHERE METAL = 'GALV'));
Sub-Queries (nested)Uncorrelated (3 tables)SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCityFROM SUPPLIERWHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber IN (SELECT PART.PartNumber FROM PART WHERE METAL = 'GALV'));
PART is scanned for Metal=‘GALV’
Inner-most query first
Inner-most Intermediate
result
Sub-Queries (nested)Uncorrelated (3 tables)SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCityFROM SUPPLIERWHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber IN (SELECT PART.PartNumber FROM PART WHERE METAL = 'GALV'));
Join SHIPMENT with Intermediate result of inner query
Inner-most Intermediate
result
Join
Intermediate SHIPMENT Result
Sub-Queries (nested)Uncorrelated (3 tables)SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCityFROM SUPPLIERWHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber IN (SELECT PART.PartNumber FROM PART WHERE METAL = 'GALV'));
Join SUPPLIER with Intermediate result of inner query
Intermediate SHIPMENT Result
Join
Sub-Queries (nested)Uncorrelated (3 tables)SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCityFROM SUPPLIERWHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber IN (SELECT PART.PartNumber FROM PART WHERE METAL = 'GALV'));
In Summary...(Join)
(Join)
Sub-Queries (nested)Uncorrelated (3 tables)SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCityFROM SUPPLIERWHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber IN (SELECT PART.PartNumber FROM PART WHERE METAL = 'GALV'));
SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCityFROM SUPPLIER, SHIPMENT, PARTWHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = PART.PartNumber AND PART.METAL = 'GALV';
SUPPLIER
SHIPMENT
PART
Written as a Join
Sub-Queries (nested)Uncorrelated (3 tables)SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCityFROM SUPPLIERWHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber IN (SELECT PART.PartNumber FROM PART WHERE METAL = 'GALV'));
How many row comparisons for subquery?
(Join)
6 + (2*12) + (3*5) = 45
As a Join?
6*5*12 = 360
(Join)
Sub-Queries (nested)An alternative formulation
SELECT T1.Item …, T2.Item… FROM table1 T1, (SELECT Item FROM table2 WHERE predicate) As T2 WHERE T1.PK = T2.FK;
In this case, the inner table is filtered by the predicate and then joined to the outer table using the alias T2. This overcomes the shortcoming of the IN method by allowing attributes from the inner table to be included in the outer SELECT. See handout for examples from class.
Sub-Queries (nested)Introduction
• General structure of correlated sub-query• Using Exists
SELECT Item FROM table1 WHERE EXISTS [NOT EXISTS] (SELECT Item FROM table2 WHERE PK = FK AND predicate);
(Inner Query)
(Outer Query)
Sub-Queries (nested)Introduction
• General structure of correlated sub-query• Using IN
SELECT Item FROM table1 WHERE predicate IN (SELECT Item FROM table2 WHERE PK = FK);
(Inner Query)
(Outer Query)
Sub-Queries (nested)CorrelatedRetrieve supplier names for suppliers who supply part P1
SELECT SUPPLIER.SupplierNameFROM SUPPLIERWHERE EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1');
Sub-Queries (nested)CorrelatedRetrieve supplier names for suppliers who supply part P1
SELECT SUPPLIER.SupplierNameFROM SUPPLIERWHERE EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1');
This is what makes it correlated.
The inner query is executed once for every row in the SUPPLIER table. That is, the value for SupplierNumber is passed by value into the sub-query.
Sub-Queries (nested)CorrelatedRetrieve supplier names for suppliers who supply part P1
SELECT SUPPLIER.SupplierNameFROM SUPPLIERWHERE EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1');
Tests if intermediate result is nonempty (empty set)
For each nonempty intermediate result, the SupplierName is selected.The EXISTS test will be performed for each row in the SUPPLIER table.
Sub-Queries (nested)CorrelatedSELECT SUPPLIER.SupplierNameFROM SUPPLIERWHERE EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1');
Intermediate
EXISTS?
Yes
Execute nested query
Sub-Queries (nested)CorrelatedSELECT SUPPLIER.SupplierNameFROM SUPPLIERWHERE EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1');
EXISTS?
Yes
Execute nested query
Intermediate
Sub-Queries (nested)CorrelatedSELECT SUPPLIER.SupplierNameFROM SUPPLIERWHERE EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1');
EXISTS?
No
Execute nested query
Intermediate
Sub-Queries (nested)CorrelatedSELECT SUPPLIER.SupplierNameFROM SUPPLIERWHERE EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1');
EXISTS isFalse for S4 and S5
Intermediate
Sub-Queries (nested)CorrelatedRetrieve supplier names for suppliers who DO NOT supply part P1
SELECT SUPPLIER.SupplierNameFROM SUPPLIERWHERE NOT EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1');
Sub-Queries (nested)CorrelatedUsing “IN”
SELECT DISTINCTROW SUPPLIER.SupplierNameFROM SUPPLIERWHERE 'P1' IN (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber =SHIPMENT.SupplierNumber);
It’s still correlated.
But now, the intermediate result will be a list of PartNumbers for the current Supplier.If ‘P1’ is IN that resulting set, the SupplierName is selected.
Sub-Queries (nested)CorrelatedSELECT DISTINCTROW SUPPLIER.SupplierNameFROM SUPPLIERWHERE 'P1' IN (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber =SHIPMENT.SupplierNumber);
S5
S3 S4S1 S2
Sub-Queries (nested)Correlated (in HAVING clause)Find the average Weight of PARTs for each City that has at least two parts.SELECT P1.PartCity, AVG (Weight) AS AverageFROM PART P1GROUP BY P1.PartCityHAVING 1 < (SELECT COUNT(*) FROM PART P2 WHERE P1.PartCity=P2.PartCity);
Only has 1
Sub-Queries (nested)Correlated (in HAVING clause)Find the average Weight of PARTs for each City that has at least two parts.SELECT P1.PartCity, AVG (Weight) AS AverageFROM PART P1GROUP BY P1.PartCityHAVING 1 < (SELECT COUNT(*) FROM PART P2 WHERE P1.PartCity=P2.PartCity);
A sub-query on the same table
Correlated with P1 cursor in the PART table
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
• As stated in the definition of the DIVISION operator, this is a general operation for any case where you want to know if a particular instance in one table corresponds to every instance in another table.
• For example– List employees that have passed all the exams
for MS certification– List students that have taken all courses for
graduation
Division
Page 206
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
• How would you do it procedurally?
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
An outer loop that scrolls through Suppliers
Supplier Loop
End Loop
An inner loop that scrolls through Parts for every row in Suppliers
End Loop
Part Loop An inner-most loop that scrolls through Shipment for every row in Part looking for PK=FK matchesEnd Loop
Shipment Loop
PK=FK?
Sub-Queries (nested)Correlated (DIVISION)Priming read (Supplier)While NOT EOF Supplier
Initialize exit condition to TRUEPriming read (Part)While (NOT EOF Part) AND (PK=FK found)
Set exit condition to FALSEPriming read (Shipment)While (NOT EOF Shipment) AND (NOT PK=FK found)
Evaluate PK=FKSet found booleanMove cursor (Shipment)
End While Move cursor (Part)
End WhileIf supply all parts, print Supplier Info Move cursor (Supplier)
End While
Pseudo-code
Sub-Queries (nested)Correlated (DIVISION)
rstSupplier.MoveFirstDo While Not rstSupplier.EOF FoundPart = True rstParts.MoveFirst Do While (Not rstParts.EOF) And FoundPart FoundPart = False rstShipment.MoveFirst Do While Not rstShipment.EOF And (Not FoundPart) FoundPart = (rstShipment.Fields("PartNumber") = rstParts.Fields("PartNumber")) _ And (rstShipment.Fields("SupplierNumber") = rstSupplier.Fields("SupplierNumber")) rstShipment.MoveNext Loop rstParts.MoveNext Loop If FoundPart Then With rstSupplier picBox.Print .Fields("SupplierNumber"), .Fields("SupplierName"), .Fields("SupplierCity") End With End IfLoop
Visual BasicCourtesy Dr. Landry*
* His original code was well documented...
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber, SUPPLIER.SupplierName
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
Starting from the inner most query and working up...
SHIPMENT, using cursor ‘S2’, ...
is scanned once for every row in PART.
SQL
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber, SUPPLIER.SupplierName
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
PART is scanned once, ...
For every row in SHIPMENT, ...
using cursor ‘S1’.
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber, SUPPLIER.SupplierName
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
The resulting set of SupplierNumbers from SHIPMENT using ‘S1’, ...
is joined with SUPPLIERto retrieve SupplierName
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
Uses two cursors into SHIPMENT
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
(TRUE)(S1=S1) AND (P1=P1)
P1
Intermediate result
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
(FALSE)(S1=S1) AND (P2=P1)
P1
Intermediate result
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
(FALSE)(S1=S1) AND (P3=P1)
P1
Intermediate result
and so on...
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
P1
Intermediate result
(FALSE)(S1=S1) AND (P5=P1)
Is Intermediate result empty?
NOT(True) = False
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
(FALSE)(S1=S1) AND (P5=P1)
So we don’t retrieve the PartNumber from PART
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
(FALSE)(S1=S1) AND (P1=P2)
and so on...
Now we re-run the inner-most query for the next value in PART
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
P2
Intermediate result
(FALSE)(S1=S1) AND (P5=P2)
Is Intermediate result empty?
NOT(True) = False
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
(FALSE)(S1=S1) AND (P5=P2)
So we don’t retrieve the PartNumber from PART And this
cycle gets repeated for every row in
PART
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
(FALSE)(S1=S1) AND (P5=P6)
So we don’t retrieve the PartNumber from PART And this
cycle gets repeated for every row in
PART
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
(FALSE)(S1=S1) AND (P5=P6)
Once we’ve searched every row in PART,
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2for the first row in SHIPMENT, we evaluate the WHERE clause
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2Did the inner query return an empty set?
Yes. So retrieve the SupplierNumber
S1
Intermediate result
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2Now we move to the next row in SHIPMENT
And repeat the entire sub-query
again.
(TRUE)(S1=S1) AND (P1=P1)
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2However... when we get to this point in the execution
(FALSE)(S1=S2) AND (P1=P3)
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2there will be no instances of (S2=S2) AND (P3=P3)
Intermediate result
So the innermost query does not return a PartNumber
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
Intermediate result
Yes, so...
Is Intermediate result empty?
NOT(False) = True
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2the PART sub-query will return the PartNumber that S2 doesn’t supply
P3
Intermediate result
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2At this point, the PART sub-query will have returned the set of Parts not supplied by S2.
P3
Intermediate result
P4
P5
P6
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2Once we’ve searched every row in PART,
P3
Intermediate result
P4
P5
P6
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
P3
Intermediate result
P4
P5
P6
for the Supplier ‘S2’, we evaluate the WHERE clause
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
P3
Intermediate result
P4
P5
P6
Did the PART sub-query return an empty set?
No. So don’t retrieve the SupplierNumber ‘S2’
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE NOT EXISTS
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber AND
S2.PartNumber = PART.PartNumber)));
S1 S2
S1
And so finally... the SupplierNumber result is joined with SUPPLIER
to get SupplierName
Sub-Queries (nested)Correlated (DIVISION)Retrieve all the suppliers that have shipped all parts in the PART table.
SELECT SUPPLIER.SupplierNumber, SUPPLIER.SupplierName
FROM SUPPLIER
WHERE SUPPLIER.SupplierNumber IN
(SELECT SupplierNumber
FROM SHIPMENT S1
WHERE NOT EXISTS
(SELECT PART.PartNumber
FROM PART
WHERE PART.PartNumber NOT IN
(SELECT PartNumber
FROM SHIPMENT S2
WHERE S2.SupplierNumber = S1.SupplierNumber)));
An alternative formulation using IN
Sub-Queries (nested)Correlated (DIVISION)List the companyname from the Suppliers table in the NorthWind database for those suppliers who supply ALL products in Category 7.