-- SQL Examples for DS 1300 -- by Michael Hahsler -- Create tables -- ------------- CREATE TABLE Company ( CName varchar(255) PRIMARY KEY, StockPrice money, Country varchar(255) ); CREATE TABLE Product ( PName varchar(255) PRIMARY KEY, Price money, Category varchar(255), Manufacturer varchar(255), FOREIGN KEY (Manufacturer) REFERENCES Company(CName) ); CREATE TABLE Purchase( id varchar(255) PRIMARY KEY, product varchar(255) NOT NULL, buyer varchar(255) NOT NULL, FOREIGN KEY (product) REFERENCES Product(PName) ); -- NOTE: datatype money is not standard, you may need to use Decimal instead. -- Populate with data INSERT INTO Company VALUES ('GWorks', 25, 'USA'); INSERT INTO Company VALUES ('Canon', 65, 'Japan'); INSERT INTO Company VALUES ('Hitachi', 15, 'Japan'); INSERT INTO Company VALUES ('IBM', 140, 'USA'); INSERT INTO Product VALUES ('Gizmo', 19, 'Gadgets', 'GWorks'); INSERT INTO Product VALUES ('Powergizmo', 29, 'Gadgets', 'GWorks'); INSERT INTO Product VALUES ('SingleTouch', 149, 'Photography', 'Canon'); INSERT INTO Product VALUES ('MultiTouch', 203, 'Household', 'Hitachi'); INSERT INTO Purchase VALUES (1, 'Gizmo', 'Joe Blow'); INSERT INTO Purchase VALUES (2, 'Gizmo', 'Joe Blow'); INSERT INTO Purchase VALUES (3, 'SingleTouch', 'Mr Smith'); INSERT INTO Purchase VALUES (4, 'MultiTouch', 'Mr Smith'); INSERT INTO Purchase VALUES (5, 'Gizmo', 'Mr Smith'); -- Single Table Queries -- -------------------- -- Simple Selection (select rows with WHERE) SELECT * FROM Product WHERE Category = 'Gadgets' -- Projection (select columns) SELECT PName, Price FROM Product WHERE Category = 'Gadgets' -- Projection with changing names for columns SELECT PName AS Product, Price AS 'Price in USD' FROM Product WHERE Category = 'Gadgets' -- DISTINCT: What product categories do we have? SELECT Category FROM Product SELECT DISTINCT Category FROM Product SELECT DISTINCT Category, price FROM Product -- AND: Find Gadgets that cost less than 50 dollars SELECT * FROM Product WHERE Category = 'Gadgets' AND Price < 50 ORDER BY Price, PName -- ORDER BY and DESC: sort from most expensive to cheapest SELECT * FROM Product WHERE Category = 'Gadgets' AND Price < 50 ORDER BY Price DESC, PName -- COUNT: How many products are in the database? SELECT count(*) AS NumProduct FROM Product -- BETWEEN: What products have a price between $20 and $200 SELECT * FROM Product WHERE PRICE BETWEEN 20 AND 200 -- BETWEEN: What products with manufacturer names starting with between A and C SELECT * FROM Product WHERE Manufacturer BETWEEN 'A' AND 'C' -- Note: For strings, 'Canon' > 'C' because it has additional characters -- fixed version (only compare first letter using substring) SELECT * FROM Product WHERE SUBSTR(Manufacturer, 1, 1) BETWEEN 'A' AND 'C' -- IN: Select products by GWorks and Canon SELECT * FROM Product WHERE Manufacturer IN ('GWorks', 'Canon') -- LIKE: Find all "touch" products SELECT * FROM Product WHERE PName LIKE '%touch%' -- CASE: Is a product expensive SELECT PName, CASE WHEN price > 200 THEN 'Yes' ELSE 'No' END AS expensive FROM Product SELECT PName, CASE WHEN price > 200 THEN 'Very' WHEN price > 100 THEN 'Yes' ELSE 'No' END AS expensive FROM Product -- LIMIT: Find the three most expensive products SELECT * FROM product ORDER BY price DESC LIMIT 3 -- NOTE: LIMIT is not standard SQL (e.g., MS SQL Server uses SELECT TOP instead) -- Joins -- ----- -- JOIN: Products manufactured by a Japanese company with a price <= 200 SELECT PName, Price FROM Product, Company WHERE Manufacturer = CName AND Price <= 200 AND Country = 'Japan' SELECT PName, Price FROM Product JOIN Company ON Manufacturer = CName WHERE Price <= 200 AND Country = 'Japan' SELECT p.PName, p.Price FROM Product p JOIN Company c ON p.Manufacturer = c.CName WHERE p.Price <= 200 AND c.Country = 'Japan' -- In what counties are Gadgets manufactured? SELECT Country FROM Product JOIN Company ON Manufacturer = CName WHERE Category = 'Gadgets' SELECT DISTINCT Country FROM Product JOIN Company ON Manufacturer = CName WHERE Category = 'Gadgets' -- Aggregation -- ----------- -- Count the number of products with a price < 50 SELECT COUNT(*) FROM Product WHERE price < 50 -- Find min, avg and max price SELECT MIN(price) AS minPrice, AVG(price) AS avgPrice, MAX(price) AS maxPrice FROM Product -- What do the next few queries calculate? SELECT category, SUM(CASE WHEN price > 200 THEN 1 ELSE 0 END) AS expensive FROM Product GROUP BY category SELECT SUM(price) AS total, SUM(price) *1.08 AS totalPlusTax FROM Product pr JOIN Purchase p ON pr.PName = p.product WHERE p.buyer = 'Joe Blow' SELECT p.buyer, SUM(price) AS total, SUM(price) *1.08 AS totalPlusTax FROM Product pr JOIN Purchase p ON pr.PName = p.product GROUP BY p.buyer ORDER BY 1 SELECT p.buyer, SUM(price) AS total, COUNT(*) AS purchases FROM Product pr JOIN Purchase p ON pr.PName = p.product GROUP BY p.buyer HAVING purchases >2 ORDER BY 1 SELECT p1.Pname, p1.Category, count(*) AS '# of more expensive products in the Cat.' FROM Product p1, Product p2 WHERE p1.Category = p2.Category AND p2.price > p1.price GROUP BY p1.Pname -- why does it not show products with no more expensive products? SELECT p1.Pname, p1.Category, count(*)-1 AS '# of more expensive products in the Cat.' FROM Product p1, Product p2 WHERE p1.Category = p2.Category AND p2.price >= p1.price GROUP BY p1.Pname -- Dates and Time ----------------- -- Format string as date/time -- Note: Make sure you do not forget leading zeros! SELECT DATETIME('2017-08-01') SELECT DATETIME('2017-08-01') > DATETIME('2017-01-01') -- use days SELECT JULIANDAY('2017-08-01') - JULIANDAY('2017-01-01') -- formating data/time SELECT strftime('%m/%d','now') -- Is it December? SELECT strftime('%m','now') = 12 -- Convert other date formats (e.g., day/month/year) SELECT DATETIME(REPLACE( SUBSTR(d, CASE SUBSTR(d, 6, 1) WHEN '/' THEN 7 ELSE 5 END), '/', '')||'-'|| --Year SUBSTR('0'||REPLACE(SUBSTR(d, 3, CASE SUBSTR(d, 4, 1) WHEN '/' THEN 2 ELSE 3 END), '/', ''), -2)||'-'|| --Month SUBSTR('0'||REPLACE(SUBSTR(d, 1, 2), '/', ''), -2) --Day ) FROM (SELECT '11/1/2013' AS d); -- Subqueries -- ---------- -- Note: Subqueries are usually put in parentheses -- Subquery in WHERE -- In what countries are the products purchased by Joe Blow manufactured? SELECT c.Country FROM Company c WHERE c.CName IN ( SELECT pr.Manufacturer FROM Purchase p, Product pr WHERE p.product = pr.PName AND p.buyer = 'Joe Blow' ) -- Join instead of subquery SELECT c.Country FROM Company c, Product pr, Purchase p WHERE c.CName = pr.Manufacturer AND pr.PName = p.product AND p.buyer = 'Joe Blow' -- fixed duplicates SELECT DISTINCT c.Country FROM Company c, Product pr, Purchase p WHERE c.CName = pr.Manufacturer AND pr.PName = p.product AND p.buyer = 'Joe Blow' -- find the most expensive product using a subquery (without using LIMIT) SELECT PName, price FROM Product WHERE price = (SELECT MAX(price) FROM Product) -- Subquery in FROM -- Find products with more than 2 purchases SELECT * FROM (SELECT product, count(product) AS count FROM Purchase GROUP BY product) WHERE count > 2 -- Rewrite with WITH WITH ProductCounts AS ( SELECT product, count(product) AS count FROM Purchase GROUP BY product) SELECT * FROM ProductCounts WHERE count > 2 -- Rewrite with HAVING SELECT product, count(product) AS count FROM Purchase GROUP BY product HAVING count > 2 -- Subquery in SELECT -- Add a column with the number of rows (products) for each category (correlated query) SELECT *, (SELECT count(*) FROM Product p1 WHERE p1.category = p2.category) AS '# Products in Cat.' FROM Product p2 -- Subquery in JOIN -- Add a column with the number of products by the manufacturer of the product SELECT * FROM Product p1 JOIN (SELECT Manufacturer, count(*) AS Number_of_Products_by_Manufacturer FROM Product GROUP BY Manufacturer) AS p2 ON p1.Manufacturer = p2.Manufacturer -- NULLS -- ----- SELECT 2 > 1 SELECT 2 > NULL SELECT 2 + NULL -- COUNT ignors NULL values SELECT COUNT(NULL) -- COALESCE returns the first non-NULL value in the list SELECT COALESCE(NULL, NULL, 12, 10) SELECT COALESCE(Price, 0) FROM Product -- Find rows with a Price being NULL (there is none) SELECT * FROM Product WHERE Price IS NULL -- OUTER JOINS -- ----------- -- A product-buyer list SELECT pr.Pname, p.buyer FROM Product pr JOIN Purchase p ON pr.PName = p.product -- Products without buyers are not in the list! SELECT pr.Pname, p.buyer FROM Product pr LEFT OUTER JOIN Purchase p ON pr.PName = p.product -- Use COALESCE to replace NULL values (COALESCE returns the first non-NULL value in a list) SELECT pr.Pname, COALESCE(p.buyer, '*No Buyer*') FROM Product pr LEFT OUTER JOIN Purchase p ON pr.PName = p.product -- Count the number of purchases per product (misses the products with 0 purchases) SELECT PName, count(*) AS sold FROM Product JOIN Purchase ON Product.PName = Purchase.product GROUP BY PName -- Try to fix by left outer join. -- Note this does not work since it even shows for unsold products a count of 1! SELECT PName, count(*) AS sold FROM Product LEFT OUTER JOIN Purchase ON Product.PName = Purchase.product GROUP BY PName -- This is what it groups and then counts SELECT * FROM Product LEFT OUTER JOIN Purchase ON Product.PName = Purchase.product -- Fix: We only count the buyer field and COUNT ignors NULL values. SELECT PName, count(buyer) AS sold FROM Product LEFT OUTER JOIN Purchase ON Product.PName = Purchase.product GROUP BY PName -- Create a new table and views from select -- ---------------------------------------- -- CREATE TABLE AS SELECT -- Note: Some databases support SELECT INTO CREATE TABLE Buyer AS SELECT buyer AS name, count(product) AS purchases FROM Purchase GROUP BY buyer DROP TABLE Buyer -- Create a "TEMPORARY" table (will be gone if you open the database again) CREATE TEMPORARY TABLE Buyer AS SELECT buyer AS name, count(product) AS purchases FROM Purchase GROUP BY buyer -- Create a "View" (a dynamically updating table, try to add or remove data in Purchase and see how -- the view updates) CREATE VIEW Buyer AS SELECT buyer AS name, count(product) AS purchases FROM Purchase GROUP BY buyer SELECT * FROM Buyer Drop VIEW Buyer