Chapter 6 Structured Query Language Chapter 6 SQL The relational operators define permissible data manipulation functions. They are not a specification for a data access language. These operators imply a certain functionality. Chapter 6 Introduction to SQL • IBM in the mid-1970s as SEQUEL • SQL is a standard – slight variations among implementations • data access language that is embedded in application programs • result of an SQL statement is a relation – Transform-oriented language Chapter 6 Introduction to SQL • Four verbs – SELECT, UPDATE, DELETE, INSERT – Basic CRUD functionality • • • • Create - INSERT Read - SELECT Update - UPDATE Delete - DELETE Chapter 6 SELECT “We must SELECT the illusion which appeals to our temperament, and embrace it with passion, if we want to be happy.” Cyril Connolly Introduction to SQL Chapter 6 General Form: SELECT [DISTINCT] Item(s) FROM table(s) [WHERE predicate] [GROUP BY field(s) [HAVING predicate]] [ORDER BY field(s)]; [Optional parameters] Simple Retrieval (SELECT) Chapter 6 Retrieve rows from SUPPLIER for suppliers located in Finland SELECT * FROM SUPPLIER WHERE SUPPLIER.SupplierCity = ‘FINLAND’; Simple Retrieval (SELECT) Chapter 6 SELECT * FROM SUPPLIER WHERE SUPPLIER.SupplierCity = ‘FINLAND’; Do you think using * (all columns) is, in general, a good practice? In applications? For exploring a table yes, but embedding it in an application is risky. Order of columns can change, column names can change. It’s usually best to enumerate the column names in the SELECT. Chapter 6 Chapter 6 SELECT “What is known as success assumes nearly as many aliases as there are those who seek it.“ Stephen Birmingham Simple Retrieval (SELECT) Chapter 6 Often you want to present the columns in the result not in their native format, but in an alternative, more application specific format. We do this with a column Alias. Simple Retrieval (SELECT) Chapter 6 SELECT OrderDetail.OrderID FROM [Order Details] AS OrderDetail WHERE OrderDetail.UnitPrice = 14 We can also use a Table Alias In this case, we can now reference the Order Details table in the query without the troublesome [ ]’s. Simple Retrieval (SELECT) Chapter 6 I often use Aliases to abbreviate long object names. As a short hand, it is convenient; but it makes the query much less readable and maintainable. Meaningful aliases, just like meaningful identifiers in programming, is important. Simple Retrieval (SELECT) Chapter 6 SELECT Orders.OrderID, [Order Details].OrderDate FROM Orders, [Order Details] WHERE Order.OrderID=[Order Details].OrderID AND [Order Details].UnitPrice = 14 Becomes… SELECT O.OrderID, O.OrderDate FROM Orders O, [Order Details] OD WHERE O.OrderID=OD.OrderID AND OD.UnitPrice = 14 Simple Retrieval Chapter 6 (WHERE) The WHERE clause specifies a condition or conditions that restricts the rows return in the result set. NOT AND OR T T T T T T T F F T F T F T F F T T F F F F F F T F F T Simple Retrieval (WHERE) Chapter 6 WHERE clauses can be as complex as they need to be. List the OrderID, OrderDate, and RequiredDate of orders for employee 5, shipped to the USA, for either customers SAVEA or RATTC, that has a freight cost exceeding $50. SELECT OrderID, OrderDate, RequiredDate FROM Orders WHERE EmployeeID = 5 AND ShipCountry = 'USA' AND (CustomerID = 'SAVEA' OR CustomerID = 'RATTC') AND Freight > 50 Simple Retrieval (WHERE) Chapter 6 An alternative means of expressing the OR clause below is with the IN() clause. SELECT OrderID, OrderDate, RequiredDate FROM Orders WHERE EmployeeID = 5 AND ShipCountry = 'USA' AND (CustomerID = 'SAVEA' OR CustomerID = 'RATTC') AND Freight > 50 Becomes… SELECT OrderID, OrderDate, RequiredDate FROM Orders WHERE EmployeeID = 5 AND ShipCountry = 'USA' AND CustomerID IN ('SAVEA','RATTC') AND Freight > 50 Simple Retrieval (WHERE) Chapter 6 The IN() clause is an example of an uncorrelated subquery. The expression evaluates to TRUE if the test expression matches any values in the list. test_expression [NOT] IN (value1, value2,…valueN) The list can be “hard-coded” as below, or it can be the result of a SELECT statement. The only restriction is that the SELECT statement has to return an enumerated list, not a matrix (a list of values versus a table). SELECT OrderID, OrderDate, RequiredDate FROM Orders WHERE EmployeeID = 5 AND ShipCountry = 'USA' AND CustomerID IN ('SAVEA','RATTC') AND Freight > 50 Simple Retrieval (WHERE) Chapter 6 test_expression [NOT] IN (value1, value2,…valueN) The list can be “hard-coded” as below, or it can be the result of a SELECT statement. The only restriction is that the SELECT statement has to return an enumerated list, not a matrix (a list of values versus a table). SELECT OrderID, OrderDate, RequiredDate FROM Orders WHERE EmployeeID = 5 AND ShipCountry = 'USA' AND CustomerID IN (SELECT CustomerID FROM Customers WHERE Condition = value) AND Freight > 50 We’re going to spend a great deal more time on uncorrelated subqueries later… Simple Retrieval Chapter 6 (WHERE) Speaking of complex WHERE clauses, how would you answer this question? List the EmployeeID, OrderID, and OrderDate for Employees 1 and 2 where they have orders placed on the same day. Simple Retrieval Chapter 6 (WHERE) How would you output the data such that the employee records were sequential rather than side-by-side? List the EmployeeID, OrderID, and OrderDate for Employees 1 and 2 where they have orders placed on the same day. Simple Retrieval Chapter 6 (WHERE) “The difference BETWEEN the right word and the almost right word is the difference BETWEEN lightning and a lightning bug. “ Mark Twain Simple Retrieval (WHERE) Chapter 6 The BETWEEN clause specifies a range of values to test against the test expression. test_expression [NOT] BETWEEN begin_value AND end_value Simple Retrieval Chapter 6 (WHERE) “All women become LIKE their mothers. That is their tragedy. No man does. That's his. “ Oscar Wilde Simple Retrieval (WHERE) Chapter 6 The LIKE clause performs character pattern matching. match_expression [NOT] LIKE pattern List the CustomerID and Phone number of customers that have a phone number with area code (503). Simple Retrieval (WHERE) Chapter 6 The LIKE clause performs character pattern matching. Wildcard character Description % Any string of zero or more characters. _ (underscore) Any single character. [] Any single character within the specified range ([a-f]) or set ([abcdef]). [^] Any single character not within the specified range ([^a-f]) or set ([^abcdef]). Return all the Quarterly Productivity results: WHERE Report LIKE ‘Q_ProductivityResult’ Simple Retrieval Chapter 6 (WHERE) A word about NULL… “The very impossibility in which I find myself to prove that God is not, discovers to me his existence.” Voltaire Simple Retrieval (WHERE) Chapter 6 NULL implies that an attribute value has not been supplied. It is not empty string or zero. It has no existence. Permitting NULL “values” in your database introduces ambiguity regarding the “existence” of a value. Null can mean: 1. The value is unknown 2. The value is not appropriate 3. The value is known to be blank Simple Retrieval (WHERE) Chapter 6 DBMS’s provide a NULL function to check for NULL values. In SQL Server this function is IS [NOT] NULL List the Employee name of the employee who doesn’t report to anyone. Simple Retrieval (WHERE) Chapter 6 List the Employee name of the employee who doesn’t report to anyone. Should this be NULL? What does it mean that Fuller has a Null value for ReportsTo? Simple Retrieval Chapter 6 (WHERE) “ORDER is never observed; it is disorder that attracts attention because it is awkward and intrusive.” Eliphas Levi ” Simple Retrieval (ORDER BY) Chapter 6 Although the order of rows and columns is unimportant in a “true” relation, the order of the result set is. ORDER BY specifies the sort for the result set. ORDER BY { order_by_expression [ ASC | DESC ] ORDER BY can sort on multiple columns. Simple Retrieval (ORDER BY) Chapter 6 Simple Retrieval (ORDER BY) Chapter 6 Note that the sort fields don’t have to appear in the SELECT statement Simple Retrieval (ORDER BY) The optimizer first did a scan of the available indexes. SQL Server automatically creates an index on the primary key. If the data are to be sorted by a field frequently, you would want to create and index on that field to improve sorting performance. Chapter 6 Simple Retrieval (Built-in Functions) Chapter 6 Also known as aggregate functions, these functions perform standard calculations on rows or groups of rows of records. Aggregate functions return a single value. With the exception of COUNT, aggregate functions ignore NULLs. Some of the functions in SQL Server are: AVG, COUNT, MAX, MIN, SUM Simple Retrieval (Built-in Functions) Chapter 6 Display the average product UnitPrice. SELECT AVG(UnitPrice) AverageUnitPrice FROM Products Display The number of orders placed by a specific customer. Simple Retrieval (Built-in Functions) What is the total sales for a specific product? Chapter 6 Simple Retrieval (Built-in Functions) Chapter 6 What is the highest and lowest UnitPrice in the Products table? Simple Retrieval (Built-in Functions) Chapter 6 List the ProductID and it’s UnitPrice for the product that has the highest UnitPrice. Why doesn’t this work? Simple Retrieval (Built-in Functions) Chapter 6 Here’s a more challenging one. List the average number of years the employees in the employee table with job_lvl greater than 200 have been with the publisher (assuming no one has retired). That is, compute the difference between the hire_date and the current date in years and take the average. Simple Retrieval (GROUP BY) Chapter 6 It is often the case that we want to perform aggregate functions on groups of records. GROUP BY divides a table into groups. Groups can consist of column names or results or computed columns. For example, if you order the Products table in the Northwind database, you can see that the rows can be group by CategoryID Simple Retrieval (GROUP BY) Chapter 6 CategoryID can be used to group the rows. GROUP BY enables us to perform operations on those groups. Simple Retrieval (GROUP BY) Chapter 6 Count the number of products in each category in the Products table. Simple Retrieval (HAVING) Chapter 6 The HAVING clause specifies a search condition for a group or an aggregate. HAVING is usually used with the GROUP BY clause. When GROUP BY is not used, HAVING behaves like a WHERE clause. [ HAVING < search_condition > ] Chapter 6 Simple Retrieval Chapter 6 (Date Functions) “And summer's lease hath all too short a date.” William Shakespeare BETWEEN Chapter 6 Use BETWEEN to pull rows in a given date range. For example, in the Northwind database, list the product names of the products ordered in the first week of January 1997. SELECT PRODUCTNAME FROM PRODUCTS WHERE PRODUCTID IN (SELECT PRODUCTID FROM ORDERS, [ORDER DETAILS] ORDERDETAIL WHERE ORDERS.ORDERID=ORDERDETAIL.ORDERID AND ORDERDATE BETWEEN '1997-01-01' AND '1997-01-07') Date functions Chapter 6 But how to get this output? List the orders in the month of January 1997 that shipped later than 10 days from the order date. ORDERID ------10400 10405 10407 ORDERDATE SHIPPEDDATE Days ---------------------- ----------------------- ------1997-01-01 00:00:00.000 1997-01-16 00:00:00.000 15 1997-01-06 00:00:00.000 1997-01-22 00:00:00.000 16 1997-01-07 00:00:00.000 1997-01-30 00:00:00.000 23 Date Functions Chapter 6 List the orders in the month of January 1997 that shipped later than 10 days from the order date. SELECT ORDERS.ORDERID, ORDERDATE, SHIPPEDDATE, DATEDIFF(DAY,ORDERDATE, SHIPPEDDATE) FROM ORDERS WHERE ORDERDATE BETWEEN '1997-01-01' AND '1997-01-07' AND DATEDIFF(DAY,ORDERDATE, SHIPPEDDATE) > 10 Syntax DATEDIFF ( datepart , startdate , enddate ) How about this one? Chapter 6 For the year 1997, show the percentage of orders that shipped within 7 days by month. MONTH ----------1 2 3 4 5 6 7 8 9 10 11 12 NUMOFORDERS ----------88 83 103 105 46 30 55 58 60 64 59 79 NUMOFORDERS7 -----------46 51 52 58 22 19 34 40 31 44 34 42 PercentbyMonth -----------------------52.272725 61.445785 50.485438 55.238098 47.826087 63.333333 61.818182 68.965518 51.666665 68.75 57.627118 53.16456 DatePart Chapter 6 For the year 1997, show the percentage of orders that shipped within 7 days by month. SELECT T1.MONTH, NUMOFORDERS, NUMOFORDERS7, CAST(NUMOFORDERS7 AS REAL)/CAST(NUMOFORDERS AS REAL)*100 PercentbyMonth FROM (SELECT DATEPART(MONTH, ORDERDATE) MONTH, COUNT(*) NUMOFORDERS FROM ORDERS GROUP BY DATEPART(MONTH, ORDERDATE)) T1, (SELECT DATEPART(MONTH, ORDERDATE) MONTH, COUNT(*) NUMOFORDERS7 FROM ORDERS WHERE DATEDIFF(DAY, ORDERDATE, SHIPPEDDATE) <=7 GROUP BY DATEPART(MONTH, ORDERDATE)) T2 WHERE T1.MONTH=T2.MONTH Syntax DATEPART ( datepart , date ) Division Chapter 6 “If an integer dividend is divided by an integer divisor, the result is an integer that has any fractional part of the result truncated.” Therefore, we had to CAST the dividend and divisor as REAL before doing the division. SELECT T1.MONTH, NUMOFORDERS, NUMOFORDERS7, CAST(NUMOFORDERS7 AS REAL)/CAST(NUMOFORDERS AS REAL) PercentbyMonth Simple Retrieval Chapter 6 (PROJECT) Retrieve a list of Metals used in parts SELECT PART.Metal FROM PART; Is this the correct result? Simple Retrieval (PROJECT) Retrieve a list of Metals used in parts SELECT DISTINCT PART.Metal FROM PART; Removed duplicate Chapter 6 Multi-table queries (JOIN) 2 tables Chapter 6 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; Multi-table queries (JOIN) 2 tables SELECT DISTINCT PART.PartName So how does this FROM PART, SHIPMENT WHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200; Chapter 6 work? Conjunct #1 is a PK FK comparison Conjunct #2 qualifies retrieval Multi-table queries (JOIN) 2 tables Chapter 6 SELECT DISTINCT PART.PartName FROM PART, SHIPMENT WHERE 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 tables SELECT DISTINCT PART.PartName FROM PART, SHIPMENT WHERE 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 Chapter 6 Multi-table queries Chapter 6 (JOIN) 2 tables SELECT DISTINCT PART.PartName FROM PART, SHIPMENT WHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200; (Scan 1) (No Matches: TT) (P1=P1 300=200) (P1=P2 200=200) (FK Scan) (P1=P5 100=200) . . . Multi-table queries Chapter 6 (JOIN) 2 tables This removes duplicates in results SELECT DISTINCT PART.PartName FROM PART, SHIPMENT WHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200; (Scan 2) (Two Matches: TT) (P2=P1 300=200) (P2=P2 200=200) (FK Scan) (P2=P5 100=200) . . . Multi-table queries Chapter 6 (JOIN) 2 tables SELECT DISTINCT PART.PartName FROM PART, SHIPMENT WHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200; (Scan 3) ( No Matches: TT) (P3=P1 300=200) (P3=P2 200=200) (FK Scan) (P3=P5 100=200) . . . Multi-table queries Chapter 6 (JOIN) 2 tables SELECT DISTINCT PART.PartName FROM PART, SHIPMENT WHERE PART.PartNumber = SHIPMENT.PartNumber AND SHIPMENT.quantity = 200; (Scan 4) (And So On...) (P4=P1 300=200) (P4=P2 200=200) (FK Scan) (P4=P5 100=200) . . . ( Two Matches: TT) Multi-table queries (JOIN) 2 tables SELECT DISTINCT PART.PartName FROM PART, SHIPMENT WHERE 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 Chapter 6 Multi-table queries JOIN Evaluation Techniques Chapter 6 • Three common methods used by DBMS optimizers to evaluate Joins – Nested Loop – Merge Scan – Hash Join Multi-table queries JOIN Evaluation Techniques Chapter 6 • 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 queries JOIN Evaluation Techniques Chapter 6 • 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 queries JOIN Evaluation Techniques Chapter 6 • Hash Join – Both tables are stored using hash function on join attributes (PK/FK) – Execute join on each partition Multi-table queries (JOIN) 3 tables Chapter 6 Retrieve supplier name and part name for parts shipped in quantities less than 400. SELECT DISTINCT SUPPLIER.SupplierName, PART.PartName FROM SUPPLIER, PART, SHIPMENT WHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400; Multi-table queries Chapter 6 (JOIN) 3 tables SELECT DISTINCT SUPPLIER.SupplierName, PART.PartName FROM SUPPLIER, PART, SHIPMENT WHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400; (Scan 1) (One Match: TT T) (P1=P1 S1=S1 400>300) (P1=P2 S1=S1 400>200) (FK Scan) (P1=P5 S1=S4 400>100) . . . Multi-table queries Chapter 6 (JOIN) 3 tables SELECT DISTINCT SUPPLIER.SupplierName, PART.PartName FROM SUPPLIER, PART, SHIPMENT WHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400; (Scan 2) (No Matches: TT T) (P1=P1 S2=S1 400>300) (P1=P2 S2=S1 400>200) (FK Scan) (P1=P5 S2=S4 400>100) . . . Multi-table queries Chapter 6 (JOIN) 3 tables SELECT DISTINCT SUPPLIER.SupplierName, PART.PartName FROM SUPPLIER, PART, SHIPMENT WHERE 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 Chapter 6 (JOIN) 3 tables SELECT DISTINCT SUPPLIER.SupplierName, PART.PartName FROM SUPPLIER, PART, SHIPMENT WHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400; (Scan 6) (One Match: TT T) (P2=P1 S1=S1 400>300) (P2=P2 S1=S1 400>200) (FK Scan) (P2=P5 S1=S4 400>100) . . . Multi-table queries Chapter 6 (JOIN) 3 tables SELECT DISTINCT SUPPLIER.SupplierName, PART.PartName FROM SUPPLIER, PART, SHIPMENT WHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400; (Scan 7) (One Match: TT T) (P2=P1 S2=S1 400>300) (P2=P2 S2=S1 400>200) (FK Scan) (P1=P5 S2=S4 400>100) . . . Multi-table queries Chapter 6 (JOIN) 3 tables SELECT DISTINCT SUPPLIER.SupplierName, PART.PartName FROM SUPPLIER, PART, SHIPMENT WHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400; (Scan 8) (And so on...) (One Match: TT T) (P2=P1 S3=S1 400>300) (P2=P2 S3=S1 400>200) (FK Scan) (P1=P5 S3=S4 400>100) . . . Multi-table queries (JOIN) 3 tables SELECT DISTINCT SUPPLIER.SupplierName, PART.PartName FROM SUPPLIER, PART, SHIPMENT WHERE PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.Quantity < 400; How many row comparisons? 6x5x12 = 360 Chapter 6 Multi-table queries (JOIN) 3 tables Chapter 6 Retrieve 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.PartName FROM SUPPLIER, PART, SHIPMENT WHERE (PART.PartNumber = SHIPMENT.PartNumber AND SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber) AND (SHIPMENT.Quantity >= 200 AND SUPPLIER.SupplierCity = PART.PartCity); Multi-table queries Chapter 6 (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. (Inner Table) (Outer Table) PK Value Data PK Value Data PK Value Data Outer Table Inner Table (PK=FK Scan) Data FK Value Data FK Value Data FK Value Data FK Value Data FK Value Sub-Queries Uncorrelated (nested) Chapter 6 So... 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. (Outer Table) PK Value Data PK Value Data PK Value Data Then Join Outer with Intermediate (Inner Table) Remove non-qualifying rows (Intermediate Table) Data FK Value Data FK Value Data FK value Data FK Value Data FK Value Data FK Value Data FK Value Data FK Value Scan inner table for PartNumber = ‘P2’ Sub-Queries Introduction (nested) Chapter 6 • 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 Introduction (nested) Chapter 6 • General structure of uncorrelated sub-query SELECT Item (Outer Query) FROM table1 WHERE Item IN [NOT IN] (SELECT Item (Inner Query) FROM table2 WHERE predicate); Sub-Queries Uncorrelated (nested) Retrieve supplier names for suppliers who supply part P2 SELECT DISTINCTROW SUPPLIER.SupplierName FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber = 'P2'); Chapter 6 Sub-Queries Uncorrelated (nested) SELECT DISTINCTROW SUPPLIER.SupplierName FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber = 'P2'); Now join outer table to intermediate result Execute inner query: Scan SHIPMENT for ‘P2’ Intermediate Chapter 6 Outer Result Sub-Queries Uncorrelated (nested) SELECT DISTINCTROW SUPPLIER.SupplierName FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber = 'P2'); Now join outer table to intermediate result Intermediate Chapter 6 Outer Result Sub-Queries Uncorrelated (nested) SELECT DISTINCTROW SUPPLIER.SupplierName FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber = 'P2'); How many row comparisons? If formulated as a join? 12 + (5 x 4) = 32 (5 x 12) = 60 Intermediate Chapter 6 Sub-Queries Uncorrelated (nested) Chapter 6 SELECT DISTINCTROW SUPPLIER.SupplierName FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber = 'P2'); Formulated as a Join SELECT DISTINCT SUPPLIER.SupplierName FROM SUPPLIER, SHIPMENT WHERE 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) Chapter 6 Retrieve supplier name and city for all suppliers who supply at least one galvanized part. SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCity FROM SUPPLIER WHERE 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) Chapter 6 SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCity FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT Inner-most query first WHERE SHIPMENT.PartNumber IN (SELECT PART.PartNumber PART is scanned for FROM PART Metal=‘GALV’ WHERE METAL = 'GALV')); Inner-most Intermediate result Sub-Queries (nested) Uncorrelated (3 tables) Chapter 6 SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCity FROM SUPPLIER Join SHIPMENT with Intermediate WHERE SUPPLIER.SupplierNumber IN result of inner query (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT WHERE SHIPMENT.PartNumber IN (SELECT PART.PartNumber FROM PART Inner-most WHERE METAL = 'GALV')); Intermediate result Intermediate SHIPMENT Result Join Sub-Queries (nested) Uncorrelated (3 tables) Chapter 6 SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCity FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber Join SUPPLIER with Intermediate FROM SHIPMENT WHERE SHIPMENT.PartNumber IN result of inner query (SELECT PART.PartNumber FROM PART Intermediate WHERE METAL = 'GALV')); SHIPMENT Result Join Sub-Queries (nested) Uncorrelated (3 tables) Chapter 6 SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCity FROM SUPPLIER WHERE 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) Chapter 6 SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCity FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN (SELECT SHIPMENT.SupplierNumber FROM SHIPMENT SUPPLIER PART WHERE SHIPMENT.PartNumber IN (SELECT PART.PartNumber FROM PART WHERE METAL = 'GALV')); SHIPMENT Written as a Join SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCity FROM SUPPLIER, SHIPMENT, PART WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = PART.PartNumber AND PART.METAL = 'GALV'; Sub-Queries (nested) Uncorrelated (3 tables) Chapter 6 SELECT DISTINCTROW SUPPLIER.SupplierName, SUPPLIER.SupplierCity FROM SUPPLIER WHERE 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? 6 + (2*12) + (3*5) = 45 As a Join? 6*5*12 = 360 (Join) (Join) Sub-Queries (nested) An alternative formulation Chapter 6 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 Introduction (nested) Chapter 6 • General structure of correlated sub-query • Using Exists SELECT Item (Outer Query) FROM table1 WHERE EXISTS [NOT EXISTS] (SELECT Item (Inner Query) FROM table2 WHERE PK = FK AND predicate); Sub-Queries Introduction (nested) Chapter 6 • General structure of correlated sub-query • Using IN SELECT Item (Outer Query) FROM table1 WHERE predicate IN (SELECT Item (Inner Query) FROM table2 WHERE PK = FK); Sub-Queries Correlated (nested) Chapter 6 Retrieve supplier names for suppliers who supply part P1 SELECT SUPPLIER.SupplierName FROM SUPPLIER WHERE EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1'); Sub-Queries Correlated (nested) Chapter 6 Retrieve supplier names for suppliers who supply part P1 SELECT SUPPLIER.SupplierName FROM SUPPLIER WHERE 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 Correlated (nested) Chapter 6 Retrieve supplier names for suppliers who supply part P1 SELECT SUPPLIER.SupplierName Tests if intermediate result is FROM SUPPLIER WHERE EXISTS nonempty (empty set) (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1'); For each nonempty intermediate result, the SupplierName is selected. The EXISTS test will be performed for each row in the SUPPLIER table. Sub-Queries Correlated (nested) Chapter 6 SELECT SUPPLIER.SupplierName FROM SUPPLIER WHERE EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1'); EXISTS? Yes Intermediate Execute nested query Sub-Queries Correlated (nested) Chapter 6 SELECT SUPPLIER.SupplierName FROM SUPPLIER WHERE EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1'); EXISTS? Yes Intermediate Execute nested query Sub-Queries Correlated (nested) Chapter 6 SELECT SUPPLIER.SupplierName FROM SUPPLIER WHERE EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1'); EXISTS? No Intermediate Execute nested query Sub-Queries Correlated (nested) Chapter 6 SELECT SUPPLIER.SupplierName FROM SUPPLIER WHERE EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1'); EXISTS is False for S4 and S5 Intermediate Sub-Queries Correlated (nested) Chapter 6 Retrieve supplier names for suppliers who DO NOT supply part P1 SELECT SUPPLIER.SupplierName FROM SUPPLIER WHERE NOT EXISTS (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber = SHIPMENT.SupplierNumber AND SHIPMENT.PartNumber = 'P1'); Sub-Queries Correlated (nested) Chapter 6 Using “IN” SELECT DISTINCTROW SUPPLIER.SupplierName FROM SUPPLIER WHERE '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 Correlated (nested) Chapter 6 SELECT DISTINCTROW SUPPLIER.SupplierName FROM SUPPLIER WHERE 'P1' IN (SELECT SHIPMENT.PartNumber FROM SHIPMENT WHERE SUPPLIER.SupplierNumber =SHIPMENT.SupplierNumber); S1 S2 S3 S4 S5 Sub-Queries (nested) Correlated (in HAVING clause) Chapter 6 Find the average Weight of PARTs for each City that has at least two parts. SELECT P1.PartCity, AVG (Weight) AS Average FROM PART P1 GROUP BY P1.PartCity HAVING 1 < (SELECT COUNT(*) FROM PART P2 WHERE P1.PartCity=P2.PartCity); Only has 1 Sub-Queries (nested) Correlated (in HAVING clause) Chapter 6 Find the average Weight of PARTs for each City that has at least two parts. SELECT P1.PartCity, AVG (Weight) AS Average FROM PART P1 GROUP BY P1.PartCity HAVING 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) Chapter 6 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) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. • How would you do it procedurally? Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. An inner loop that scrolls An outer loop that scrolls through Parts for every through Suppliers row in Suppliers Supplier Loop Part Loop Shipment Loop PK=FK? End Loop End Loop End Loop An inner-most loop that scrolls through Shipment for every row in Part looking for PK=FK matches Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Priming read (Supplier) Pseudo-code While NOT EOF Supplier Initialize exit condition to TRUE Priming read (Part) While (NOT EOF Part) AND (PK=FK found) Set exit condition to FALSE Priming read (Shipment) While (NOT EOF Shipment) AND (NOT PK=FK found) Evaluate PK=FK Set found boolean Move cursor (Shipment) End While Move cursor (Part) End While If supply all parts, print Supplier Info Move cursor (Supplier) End While Sub-Queries (nested) Correlated (DIVISION) Chapter 6 rstSupplier.MoveFirst Do While Not rstSupplier.EOF Visual Basic FoundPart = True Courtesy Dr. Landry* 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 If Loop * His original code was well documented... Sub-Queries (nested) Correlated (DIVISION) Chapter 6 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 SQL Starting from the inner most query and working up... SHIPMENT, using cursor ‘S2’, ... (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS (SELECT PartNumber is scanned once for every row in PART. FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); Sub-Queries (nested) Correlated (DIVISION) Chapter 6 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 using cursor ‘S1’. For every row in SHIPMENT, ... (SELECT PART.PartNumber PART is scanned once, ... FROM PART WHERE NOT EXISTS (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); Sub-Queries (nested) Correlated (DIVISION) Chapter 6 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 is joined with SUPPLIER to retrieve SupplierName The resulting set of SupplierNumbers from SHIPMENT using ‘S1’, ... FROM PART WHERE NOT EXISTS (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. Uses two cursors into SHIPMENT S1 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))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. S1 SELECT SUPPLIER.SupplierNumber FROM SUPPLIER Intermediate result WHERE SUPPLIER.SupplierNumber IN (SELECT SupplierNumber P1 FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS (TRUE) (S1=S1) AND (P1=P1) (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. S1 SELECT SUPPLIER.SupplierNumber FROM SUPPLIER Intermediate result WHERE SUPPLIER.SupplierNumber IN (SELECT SupplierNumber P1 FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS (FALSE) (S1=S1) AND (P2=P1) (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. S1 SELECT SUPPLIER.SupplierNumber FROM SUPPLIER Intermediate result WHERE SUPPLIER.SupplierNumber IN (SELECT SupplierNumber P1 FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS S2 (FALSE) (S1=S1) AND (P3=P1) (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); and so on... Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. S1 Is Intermediate result empty? NOT(True) = False SELECT SUPPLIER.SupplierNumber FROM SUPPLIER Intermediate result WHERE SUPPLIER.SupplierNumber IN (SELECT SupplierNumber P1 FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS (FALSE) (S1=S1) AND (P5=P1) (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. So we don’t retrieve the PartNumber from PART S1 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 (FALSE) (S1=S1) AND (P5=P1) (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. Now we re-run the innermost query for the next value in PART S1 S2 SELECT SUPPLIER.SupplierNumber FROM SUPPLIER and so on... WHERE SUPPLIER.SupplierNumber IN (SELECT SupplierNumber FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS (FALSE) (S1=S1) AND (P1=P2) (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. S1 Is Intermediate result empty? NOT(True) = False SELECT SUPPLIER.SupplierNumber FROM SUPPLIER Intermediate result WHERE SUPPLIER.SupplierNumber IN (SELECT SupplierNumber P2 FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS (FALSE) (S1=S1) AND (P5=P2) (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. So we don’t retrieve the PartNumber from PART SELECT SUPPLIER.SupplierNumber FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN (SELECT SupplierNumber S1 And this cycle gets repeated for every row in PART FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS (FALSE) (S1=S1) AND (P5=P2) (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. So we don’t retrieve the PartNumber from PART SELECT SUPPLIER.SupplierNumber FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN (SELECT SupplierNumber S1 And this cycle gets repeated for every row in PART FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS (FALSE) (S1=S1) AND (P5=P6) (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. Once we’ve searched every row in PART, S1 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 (FALSE) (S1=S1) AND (P5=P6) (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. for the first row in SHIPMENT, we evaluate the WHERE clause S1 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))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. Did the inner query return an empty set? SELECT SUPPLIER.SupplierNumber FROM SUPPLIER Intermediate result WHERE SUPPLIER.SupplierNumber IN (SELECT SupplierNumber S1 S1 FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS (SELECT PartNumber Yes. So retrieve the SupplierNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. Now we move to the next row in SHIPMENT SELECT SUPPLIER.SupplierNumber FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN (SELECT SupplierNumber FROM SHIPMENT S1 S1 And repeat the entire sub-query again. WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS (TRUE) (S1=S1) AND (P1=P1) (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. However... when we get to this point in the execution S1 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 (FALSE) (S1=S2) AND (P1=P3) (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. there will be no instances of (S2=S2) AND (P3=P3) S1 SELECT SUPPLIER.SupplierNumber Intermediate WHERE SUPPLIER.SupplierNumber IN result FROM SUPPLIER (SELECT SupplierNumber FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS So the innermost query does not return a PartNumber (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. Is Intermediate result empty? S1 NOT(False) = True SELECT SUPPLIER.SupplierNumber Intermediate WHERE SUPPLIER.SupplierNumber IN result FROM SUPPLIER (SELECT SupplierNumber FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS Yes, so... (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. the PART sub-query will return the PartNumber that S2 doesn’t supply S1 SELECT SUPPLIER.SupplierNumber Intermediate WHERE SUPPLIER.SupplierNumber IN result FROM SUPPLIER (SELECT SupplierNumber FROM SHIPMENT S1 WHERE NOT EXISTS P3 (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. At this point, the PART subquery will have returned the set of Parts not supplied by S2. S1 SELECT SUPPLIER.SupplierNumber Intermediate WHERE SUPPLIER.SupplierNumber IN result FROM SUPPLIER (SELECT SupplierNumber FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS (SELECT PartNumber FROM SHIPMENT S2 P3 P4 P5 P6 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. S1 Once we’ve searched every row in PART, SELECT SUPPLIER.SupplierNumber Intermediate result FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN (SELECT SupplierNumber FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART P3 P4 P5 P6 WHERE NOT EXISTS (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. S1 for the Supplier ‘S2’, we evaluate the WHERE clause Intermediate result SELECT SUPPLIER.SupplierNumber FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN (SELECT SupplierNumber FROM SHIPMENT S1 WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART P3 P4 P5 P6 WHERE NOT EXISTS (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. S1 Did the PART sub-query return an empty set? Intermediate result 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 P3 P4 P5 P6 No. So don’t retrieve the SupplierNumber ‘S2’ WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 Retrieve all the suppliers that have shipped all parts in the PART table. S1 And so finally... the SupplierNumber result is joined with SUPPLIER SELECT SUPPLIER.SupplierNumber FROM SUPPLIER WHERE SUPPLIER.SupplierNumber IN S1 (SELECT SupplierNumber FROM SHIPMENT S1 WHERE NOT EXISTS to get SupplierName (SELECT PART.PartNumber FROM PART WHERE NOT EXISTS (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber AND S2.PartNumber = PART.PartNumber))); S2 Sub-Queries (nested) Correlated (DIVISION) Chapter 6 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 An alternative formulation using IN WHERE NOT EXISTS (SELECT PART.PartNumber FROM PART WHERE PART.PartNumber NOT IN (SELECT PartNumber FROM SHIPMENT S2 WHERE S2.SupplierNumber = S1.SupplierNumber))); Sub-Queries (nested) Correlated (DIVISION) Chapter 6 List the companyname from the Suppliers table in the NorthWind database for those suppliers who supply ALL products in Category 7. UNION Chapter 6 Retrieve customer info for customers who have NOT had their order shipped to their headquarters city (i.e., City=ShipCity). • UNION stacks the results of one query onto a second query SELECT attribute(s) FROM table UNION SELECT attribute(s) FROM table; You can have multiple UNIONs in one query. UNION Chapter 6 Here’s an example from a team project select Flt_source, Flt_Destination Creates a new table and inserts the into temp resulting rows from the query into it. from Flight a where (a.Flt_source=‘MOB’) and (a.Flt_Destination=‘SJC’) UNION select a.Flt_source, a.Flt_Destination from Flight a, Flight b where ((a.Flt_Destination=b.Flt_Source)and ((a.Flt_source=‘MOB’) and (a.Flt_Destination=‘SJC’)) UNION select b.Flt_source, b.Flt_Destination from Flight a, Flight b where ((a.Flt_Destination=b.Flt_Source) and ((a.Flt_source=‘MOB’) and (a.Flt_Destination=‘SJC’)) UNION Create a combined mailing list for Employees, Customers, and Suppliers Chapter 6 INTERSECT Chapter 6 Intersection returns the rows that appear in both queries • INTERSECT is a set operator like UNION SELECT attribute(s) FROM table INTERSECT SELECT attribute(s) FROM table; DIFFERENCE Chapter 6 • Difference returns the rows that do NOT appear in both queries • EXCEPT is a set operator like UNION and INTERSECT SELECT attribute(s) FROM table EXCEPT SELECT attribute(s) FROM table; Computed Fields Chapter 6 Select all parts and compute shipping cost per unit. Assume cost is 0.001 per unit of weight. SELECT PART.PartNumber, WEIGHT*0.001 AS Cost FROM PART; Outer Joins Chapter 6 Returns rows where PK=FK AND non-matched rows in either or both of the tables joined. BASIC STRUCTURE: SELECT ITEMS FROM TABLE1 LEFT [RIGHT] OUTER JOIN TABLE2 ON TABLE1.PK=TABLE2.FK Where LEFT refers to the table listed first (to the left of the OUTER JOIN clause). Outer Joins Chapter 6 List all product names and the total quantity ordered for the first week of January 1997. An inner join: SELECT PRODUCTNAME, SUM(T1.QUANTITY) TOTAL FROM PRODUCTS P, (SELECT DISTINCT PRODUCTID, ORDERDATE, QUANTITY FROM ORDERS O, [ORDER DETAILS] OD WHERE O.ORDERID=OD.ORDERID AND ORDERDATE BETWEEN '1997-01-01' AND '1997-01-07') T1 WHERE P.PRODUCTID=T1.PRODUCTID GROUP BY PRODUCTNAME Outer Joins Chapter 6 List all product names and the total quantity ordered for the first week of January 1997. Would result in: PRODUCTNAME ---------------------------------------Aniseed Syrup Boston Crab Meat Chai Chocolade Flotemysost Gnocchi di nonna Alice Gudbrandsdalsost Gumbär Gummibärchen Inlagd Sill Louisiana Fiery Hot Pepper Sauce Maxilaku Nord-Ost Matjeshering Pavlova Queso Cabrales Rössle Sauerkraut Singaporean Hokkien Fried Mee Sir Rodney's Scones Steeleye Stout Thüringer Rostbratwurst Tunnbröd Vegie-spread TOTAL ----------50 2 10 70 75 70 15 30 5 20 60 18 21 30 42 40 30 35 21 60 65 This doesn’t show ALL productnames Outer Joins Chapter 6 Formulated as a LEFT OUTER JOIN: SELECT PRODUCTNAME, SUM(T1.QUANTITY) TOTAL FROM PRODUCTS P LEFT OUTER JOIN (SELECT DISTINCT PRODUCTID, ORDERDATE, QUANTITY FROM ORDERS O, [ORDER DETAILS] OD WHERE O.ORDERID=OD.ORDERID AND ORDERDATE BETWEEN '1997-01-01' AND '1997-01-07') T1 ON P.PRODUCTID=T1.PRODUCTID GROUP BY PRODUCTNAME The basic structure is: SELECT PRODUCTNAME, SUM(T1.QUANTITY) TOTAL FROM PRODUCTS P LEFT OUTER JOIN T1 ON P.PK=T1.FK GROUP BY PRODUCTNAME Outer Joins Chapter 6 Formulated as a LEFT OUTER JOIN: PRODUCTNAME ---------------------------------------Alice Mutton Aniseed Syrup Boston Crab Meat Camembert Pierrot Carnarvon Tigers Chai Chang Chartreuse verte Chef Anton's Cajun Seasoning Chef Anton's Gumbo Mix Chocolade Côte de Blaye Escargots de Bourgogne Filo Mix Flotemysost Geitost Genen Shouyu Gnocchi di nonna Alice Gorgonzola Telino Grandma's Boysenberry Spread Gravad lax Guaraná Fantástica {AND SO ON…} TOTAL ----------NULL 50 2 NULL NULL 10 NULL NULL NULL NULL 70 NULL NULL NULL 75 NULL NULL 70 NULL NULL NULL NULL Now we get ALL productnames Outer Joins Chapter 6 An outer join won’t always solve the problem. PRODUCTNAME ---------------------------------------Alice Mutton Aniseed Syrup Boston Crab Meat Camembert Pierrot Carnarvon Tigers Chai Chang Chartreuse verte Chef Anton's Cajun Seasoning Chef Anton's Gumbo Mix Chocolade Côte de Blaye Escargots de Bourgogne Filo Mix Flotemysost Geitost Genen Shouyu Gnocchi di nonna Alice Gorgonzola Telino Grandma's Boysenberry Spread Gravad lax Guaraná Fantástica {AND SO ON…} TOTAL ----------NULL 50 2 NULL NULL 10 NULL NULL NULL NULL 70 NULL NULL NULL 75 NULL NULL 70 NULL NULL NULL NULL Introduction to SQL Chapter 6 • To review: • Four verbs • SELECT, UPDATE, DELETE, INSERT – Basic CRUD functionality • • • • Create - INSERT Read - SELECT Update - UPDATE Delete – DELETE – So far we’ve only looked at SELECT – Before we do DML, let’s look at DDL Chapter 6 Data Definition Language • DDL – CREATE, DROP, ALTER • The simplicity of the Verbs belie the complexity of these commands, e.g., – Oracle8i CREATE TABLE Chapter 6 • CREATE TABLE defines a base table in the database schema. • General structure of CREATE statement CREATE TABLE TableName (AttributeName Domain [Default][Constraint] [, AttributeName Domain ...] [other constraints]) [Optional parameters] CREATE TABLE Chapter 6 CREATE TABLE TableName (AttributeName Domain [Default][Constraint] [, AttributeName Domain ...] [other constraints]) • Domain :set of values an attribute can have • SQL provides six “families” of elementary domains – – – – – – Character (strings) Bit (0,1) Exact Numeric (integer, smallint, decimal ($)) Approximate numeric (floating point) Date and Time Temporal intervals • User Defined CREATE TABLE Chapter 6 CREATE TABLE TableName (AttributeName Domain [Default][Constraint] [, AttributeName Domain ...] [other constraints]) • [Default] the value that is assigned automatically when the row is inserted into the table unless specified in the INSERT INTO statement. CREATE TABLE Customer (CustomerID char(5), Status char(10) Default ‘Active’) CREATE TABLE Chapter 6 CREATE TABLE TableName (AttributeName Domain [Default][Constraint] [, AttributeName Domain ...] [other constraints]) • Intra-relational constraints [Constraint] • For example – NOT NULL – UNIQUE – PRIMARY KEY (implies NOT NULL and UNIQUE) CREATE TABLE Customer (CustomerID char(5) primary key, CustomerName char(50) not null, SocialSecurity Char(9) not null unique, Status char(10) Default ‘Active’) CREATE TABLE Chapter 6 CREATE TABLE TableName (AttributeName Domain [Default][Constraint] [, AttributeName Domain ...] [other constraints]) • Compound primary key CREATE TABLE Customer (FirstName char(20), LastName char(20) not null, SocialSecurity Char(9) not null unique, Status char(10) Default ‘Active’ primary key (FirstName, LastName) CREATE TABLE Chapter 6 CREATE TABLE TableName (AttributeName Domain [Default][Constraint] [, AttributeName Domain ...] [other constraints]) • Inter-relational constraints [other constraints] • Referential Integrity – references TableName(AttributeName) – Foreign key (AtttributeName1, AttributeName2,...) – references TableName(Attribute1, Attribute2,...) CREATE TABLE CREATE TABLE TableName (AttributeName Domain [Default][Constraint] [, AttributeName Domain ...] [other constraints]) • Referential Integrity CREATE TABLE Customer (CustomerID char(5) primary key, CustomerName char(50) not null, ClerkID char(4) references clerk(ClerkID), Status char(10) Default ‘Active’) Chapter 6 CREATE TABLE Chapter 6 CREATE TABLE TableName (AttributeName Domain [Default][Constraint] [, AttributeName Domain ...] [other constraints]) • Referential Integrity CREATE TABLE Customer (CustomerID char(5) primary key, CustomerName char(50) not null, DeptID char(4), BuildingID char (5), Status char(10) Default ‘Active’ foreign key (DeptID, BuildingID) references DeptBuilding(DeptID,BuildingID)) DROP TABLE • Schema Updates DROP TABLE Customer Chapter 6 ALTER TABLE Chapter 6 • Allows modifications of table structure ALTER TABLE TableName alter column AttributeName add constraint drop constraint add column drop column; Chapter 6 Introduction to SQL • Data Manipulation Language (DML) – SELECT, UPDATE, INSERT, DELETE UPDATE Chapter 6 • General structure of UPDATE syntax UPDATE table SET attribute = expression [,attribute = expression] WHERE predicate; OR UPDATE table SET attribute = expression [,attribute = expression] subquery; [Optional parameters] UPDATE Chapter 6 UPDATE table SET attribute = expression [,attribute = expression] WHERE predicate; • The update command makes it possible to update one or more attributes of the rows of table that satisfy some condition. • If no predicate is given, the operation is performed on all rows. UPDATE Chapter 6 UPDATE table SET attribute = expression [,attribute = expression] WHERE predicate; • The new value can be one of the following: – the result of a computed value on the attributes of the table – the result of an SQL query – the null value – or the default value for the domain UPDATE Chapter 6 Set the credit limit of all customers to $10,000. UPDATE Chapter 6 Set the credit limit of customers with Status ‘1’ to $15,000. UPDATE Chapter 6 Add $2,000 to the credit limit of those customers who have placed more than 8 orders. Partial Database Let’s Schema try something a little bit harder... UPDATE Chapter 6 Add $2,000 to the credit limit of those customers who have placed more than 8 orders. (Correlated subquery) UPDATE Chapter 6 Add $2,000 to the credit limit of those customers who have placed more than 8 orders. If there’s a count for the current customerid, then retrieve it in the outer query UPDATE Chapter 6 Add $2,000 to the credit limit of those customers who have placed more than 8 orders. Uses IN to identify the customerids that need to be retrieved in the outer query. INSERT Chapter 6 • General structure of INSERT syntax INSERT INTO table [(attribute1, attribute2,...)] VALUES(constant, constant,...); OR INSERT INTO table [(attribute1, attribute2,...)] subquery; [Optional parameters] INSERT Chapter 6 INSERT INTO table [(attribute1, attribute2,...)] VALUES(constant, constant,...); • The left to right order of attribute and constant must match. • Attribute list is optional – if not present, assumed entire list is being inserted • Do not have to include all attributes in VALUES list INSERT Chapter 6 INSERT INTO table [(attribute1, attribute2,...)] VALUES(constant, constant,...); • If a value (constant) is not provided – uses default value, or failing this – the NULL value – If can’t be NULL, insert is rejected • Much better to always specify a value for each attribute in embedded SQL INSERT Chapter 6 INSERT INTO table [(attribute1, attribute2,...)] VALUES(constant, constant,...); INSERT subquery Chapter 6 INSERT INTO table [(column1, column2,...)] subquery; Notice that this comes from a different table INSERT Chapter 6 • The two forms of INSERT have two different applications • The first form is typically used in programs to insert data from a user interface (a form). • The second form is typically used to create intermediate tables from data already in the database. DELETE • General structure of DELETE syntax DELETE FROM table [WHERE predicate] OR DELETE FROM table [subquery]; [Optional parameters] Chapter 6 DELETE Chapter 6 DELETE FROM table [WHERE predicate] • Eliminates rows from a table based on a condition • If the WHERE clause is not specified, the command deletes all records. • How are these two different? DELETE FROM Customer1 DROP TABLE Customer1 • DELETE doesn’t change schema DELETE Chapter 6 DELETE FROM table [WHERE predicate] How to delete the new records we just entered? DELETE DELETE FROM table [subquery]; Chapter 6