From 5d9164fba7f54e780ab806de8d69766694a23af5 Mon Sep 17 00:00:00 2001 From: "CODE, POTATO" <68235853+mrpotatocode@users.noreply.github.com> Date: Tue, 17 Dec 2024 20:40:24 -0500 Subject: [PATCH 1/5] in class code #4 --- .../module_4/FULL_OUTER_JOIN_UNION.sql | 47 +++++++++++++++++++ .../live_code/module_4/IFNULL_NULLIF.sql | 23 +++++++++ .../live_code/module_4/INTERSECT_EXCEPT.sql | 31 ++++++++++++ .../module_4/INTERSECT_VS_INNER_JOIN.sql | 36 ++++++++++++++ 04_this_cohort/live_code/module_4/NTILE.sql | 24 ++++++++++ .../live_code/module_4/ROW_NUMBER.sql | 19 ++++++++ .../live_code/module_4/UNION_UNION_ALL.sql | 23 +++++++++ .../module_4/budget_coalesce_NULLIF.sql | 24 ++++++++++ .../module_4/row_rank_dense_comparison.sql | 29 ++++++++++++ .../module_4/string_manipulations.sql | 43 +++++++++++++++++ .../module_4/substring_instring_together.sql | 16 +++++++ 11 files changed, 315 insertions(+) create mode 100644 04_this_cohort/live_code/module_4/FULL_OUTER_JOIN_UNION.sql create mode 100644 04_this_cohort/live_code/module_4/IFNULL_NULLIF.sql create mode 100644 04_this_cohort/live_code/module_4/INTERSECT_EXCEPT.sql create mode 100644 04_this_cohort/live_code/module_4/INTERSECT_VS_INNER_JOIN.sql create mode 100644 04_this_cohort/live_code/module_4/NTILE.sql create mode 100644 04_this_cohort/live_code/module_4/ROW_NUMBER.sql create mode 100644 04_this_cohort/live_code/module_4/UNION_UNION_ALL.sql create mode 100644 04_this_cohort/live_code/module_4/budget_coalesce_NULLIF.sql create mode 100644 04_this_cohort/live_code/module_4/row_rank_dense_comparison.sql create mode 100644 04_this_cohort/live_code/module_4/string_manipulations.sql create mode 100644 04_this_cohort/live_code/module_4/substring_instring_together.sql diff --git a/04_this_cohort/live_code/module_4/FULL_OUTER_JOIN_UNION.sql b/04_this_cohort/live_code/module_4/FULL_OUTER_JOIN_UNION.sql new file mode 100644 index 000000000..7bc45397d --- /dev/null +++ b/04_this_cohort/live_code/module_4/FULL_OUTER_JOIN_UNION.sql @@ -0,0 +1,47 @@ +--FULL OUTER JOIN with a UNION + +DROP TABLE IF EXISTS temp.store1; +CREATE TEMP TABLE IF NOT EXISTS temp.store1 +( +costume TEXT, +quantity INT +); + +INSERT INTO temp.store1 +VALUES("tiger",6), + ("elephant",2), + ("princess", 4); + + +DROP TABLE IF EXISTS temp.store2; +CREATE TEMP TABLE IF NOT EXISTS temp.store2 +( +costume TEXT, +quantity INT +); + +INSERT INTO temp.store2 +VALUES("tiger",2), + ("dancer",7), + ("superhero", 5); + +SELECT +s1.costume +,s1.quantity as store1_quantity +,s2.quantity as store2_quantity + +FROM store1 s1 +LEFT JOIN store2 s2 + ON s1.costume = s2.costume + +UNION ALL + +SELECT +s2.costume +,s1.quantity +,s2.quantity + +FROM store2 s2 +LEFT JOIN store1 s1 + ON s1.costume = s2.costume +WHERE s1.costume IS NULL diff --git a/04_this_cohort/live_code/module_4/IFNULL_NULLIF.sql b/04_this_cohort/live_code/module_4/IFNULL_NULLIF.sql new file mode 100644 index 000000000..f41eb06a0 --- /dev/null +++ b/04_this_cohort/live_code/module_4/IFNULL_NULLIF.sql @@ -0,0 +1,23 @@ +--IFNULL and coalesce + NULLIF + +SELECT * +,IFNULL(product_size, 'Unknown') as new_product_size + +--less meaningful "conceptual" +,IFNULL(product_size, product_qty_type) -- both null, results with null +,coalesce(product_size,product_qty_type,'missing') -- if first the value is null, the second is null, then do the third value +,IFNULL(IFNULL(product_size, product_qty_type),'missing') -- same but have to wrap within + + +FROM product; + +SELECT * +,IFNULL(product_size, 'Unknown') as new_product_size +,NULLIF(product_size, '') -- finding the values that product_size column is "blank" and setting them to null +,coalesce(NULLIF(product_size, ''), 'unknown') as good_product_size +,IFNULL(NULLIF(product_size, ''), 'unknown') as good_product_size + +FROM product + +WHERE NULLIF(product_size, '') IS NULL -- both blanks and NULLs +--WHERE product_size IS NULL -- only NULLs \ No newline at end of file diff --git a/04_this_cohort/live_code/module_4/INTERSECT_EXCEPT.sql b/04_this_cohort/live_code/module_4/INTERSECT_EXCEPT.sql new file mode 100644 index 000000000..01915c5bd --- /dev/null +++ b/04_this_cohort/live_code/module_4/INTERSECT_EXCEPT.sql @@ -0,0 +1,31 @@ +--INTERSECT / EXCEPT + +--product that have been sold (e.g are in customer_purchases and product) +SELECT product_id +FROM customer_purchases +INTERSECT -- similar to the inner join in this case +SELECT product_id +FROM product; + +--products that NOT been sold (e.g. are NOT in customer_pruchases even though in product) +-- add the name +SELECT x. product_id, product_name +FROM ( + SELECT product_id + FROM product -- what products are NOT in customer_purchases + EXCEPT + SELECT product_id + FROM customer_purchases +) x +JOIN product p on x.product_id = p.product_id + +--NOTHING +--direction matter a lot! +SELECT product_id +FROM customer_purchases -- what products are NOT in product -- NONE! +EXCEPT +SELECT product_id +FROM product; + + + diff --git a/04_this_cohort/live_code/module_4/INTERSECT_VS_INNER_JOIN.sql b/04_this_cohort/live_code/module_4/INTERSECT_VS_INNER_JOIN.sql new file mode 100644 index 000000000..456adcca8 --- /dev/null +++ b/04_this_cohort/live_code/module_4/INTERSECT_VS_INNER_JOIN.sql @@ -0,0 +1,36 @@ +DROP TABLE IF EXISTS temp.temp1; +CREATE TEMP TABLE IF NOT EXISTS temp.temp1 +(id int); + +INSERT INTO temp.temp1 +VALUES(null) +,(1) +,(3) +,(3); + +DROP TABLE IF EXISTS temp.temp2; +CREATE TEMP TABLE IF NOT EXISTS temp.temp2 +(id int); +INSERT INTO temp.temp2 +VALUES(null) +,(2) +,(3) +,(3); + +-- distinct and NULL +select * from temp1 +INTERSECT +select * from temp2 + +select * from temp1 +EXCEPT +select * from temp2 + +select * from temp2 +EXCEPT +select * from temp1 + +-- not distinct and NOT NULL +select * from temp1 t +INNER JOIN temp2 x + on t.id = x.id \ No newline at end of file diff --git a/04_this_cohort/live_code/module_4/NTILE.sql b/04_this_cohort/live_code/module_4/NTILE.sql new file mode 100644 index 000000000..1831f3e53 --- /dev/null +++ b/04_this_cohort/live_code/module_4/NTILE.sql @@ -0,0 +1,24 @@ +--NTILE (4,5,100?) + +--daily sales +SELECT * +,NTILE(4) OVER(PARTITION by vendor_name ORDER by sales asc) as quartile +,NTILE(5) OVER(PARTITION by vendor_name ORDER by sales asc) as quintile +,NTILE(100) OVER(PARTITION by vendor_name ORDER by sales asc) as percentile + +FROM ( + SELECT + md.market_date, + market_week, + market_year, + vendor_name, + sum(quantity*cost_to_customer_per_qty) as sales + + FROM market_date_info md + JOIN customer_purchases cp + on md.market_date = cp.market_date + JOIN vendor v + on cp.vendor_id = v.vendor_id + + GROUP BY md.market_date, v.vendor_id +) x \ No newline at end of file diff --git a/04_this_cohort/live_code/module_4/ROW_NUMBER.sql b/04_this_cohort/live_code/module_4/ROW_NUMBER.sql new file mode 100644 index 000000000..73c714cba --- /dev/null +++ b/04_this_cohort/live_code/module_4/ROW_NUMBER.sql @@ -0,0 +1,19 @@ +--windowed function +-- what was the highest price seen per product for each vendor + +SELECT * + +FROM ( + + SELECT DISTINCT + vendor_id + --,market_date + ,product_id + ,original_price + ,ROW_NUMBER() OVER(PARTITION BY vendor_id,product_id ORDER BY original_price DESC) as price_rank + + FROM vendor_inventory + WHERE vendor_id = 7 +) x + +WHERE x.price_rank = 1 diff --git a/04_this_cohort/live_code/module_4/UNION_UNION_ALL.sql b/04_this_cohort/live_code/module_4/UNION_UNION_ALL.sql new file mode 100644 index 000000000..652a15256 --- /dev/null +++ b/04_this_cohort/live_code/module_4/UNION_UNION_ALL.sql @@ -0,0 +1,23 @@ +--UNION/UNION ALL +--most and least expensive product by vendor with a union + + +SELECT vendor_id, product_id,original_price, rn_max AS [row_number] --renaming because of the union +FROM ( + SELECT DISTINCT + vendor_id, product_id, original_price + ,ROW_NUMBER() OVER(PARTITION BY vendor_id ORDER BY original_price DESC) as rn_max + FROM vendor_inventory +) x +WHERE rn_max = 1 + +UNION -- union returned 5 rows? +--UNION ALL -- returned 6 rows inlcuding vendor #4 who was a duplicate + +SELECT * FROM ( + SELECT DISTINCT + vendor_id, product_id, original_price + ,ROW_NUMBER() OVER(PARTITION BY vendor_id ORDER BY original_price ASC) as rn_min + FROM vendor_inventory +) x +WHERE rn_min = 1 \ No newline at end of file diff --git a/04_this_cohort/live_code/module_4/budget_coalesce_NULLIF.sql b/04_this_cohort/live_code/module_4/budget_coalesce_NULLIF.sql new file mode 100644 index 000000000..667a83fb8 --- /dev/null +++ b/04_this_cohort/live_code/module_4/budget_coalesce_NULLIF.sql @@ -0,0 +1,24 @@ + +-- create a budget temp table +DROP TABLE IF EXISTS temp.budgets; + +-- here i am specifying the column types, this was asked, so budget is a string, current year is an integer, prev year also int +CREATE TEMP TABLE IF NOT EXISTS temp.budgets (budget STRING, current_year INT, previous_year INT); + + +--nothing is yet in budget +INSERT INTO temp.budgets + +-- so put as row 1 +VALUES ('software',1000,1000) +--and row 2 +, ('candles',300,500); + +--show me the average difference in years +--NULLIF, if the numbers are the same, then NULL +--COALESCE, if the result is NULL then 0.00 +--average across the values = change in years +SELECT AVG(COALESCE(NULLIF(current_year, previous_year), 0.00)) +FROM budgets + +--result(300 [current year for candles] +0 / 2 [two rows] = 150.0) diff --git a/04_this_cohort/live_code/module_4/row_rank_dense_comparison.sql b/04_this_cohort/live_code/module_4/row_rank_dense_comparison.sql new file mode 100644 index 000000000..caa4a5994 --- /dev/null +++ b/04_this_cohort/live_code/module_4/row_rank_dense_comparison.sql @@ -0,0 +1,29 @@ +--dense_rank vs rank vs row_number + +DROP TABLE IF EXISTS temp.row_rank_dense; + +CREATE TEMP TABLE IF NOT EXISTS temp.row_rank_dense +( +emp_id INT, +salary INT +); + +INSERT INTO temp.row_rank_dense +VALUES(1,200000), +(2,200000), +(3, 160000), +(4, 120000), +(5, 125000), +(6, 165000), +(7, 230000), +(8, 100000), +(9, 165000), +(10, 100000); + +SELECT * +,ROW_NUMBER() OVER(ORDER BY salary DESC) as [ROW_NUMBER] +,RANK() OVER(ORDER BY salary DESC) as [RANK] +,DENSE_RANK() OVER(ORDER BY salary DESC) as [DENSE_RANK] + + +FROM row_rank_dense \ No newline at end of file diff --git a/04_this_cohort/live_code/module_4/string_manipulations.sql b/04_this_cohort/live_code/module_4/string_manipulations.sql new file mode 100644 index 000000000..c2e440ba4 --- /dev/null +++ b/04_this_cohort/live_code/module_4/string_manipulations.sql @@ -0,0 +1,43 @@ +--string manipulations + +SELECT DISTINCT +LTRIM(' THOMAS ROSENTHAL ') as [ltrim] +,RTRIM(' THOMAS ROSENTHAL ') as [rtrim] +,RTRIM(LTRIM(' THOMAS ROSENTHAL ')) as [both] +,TRIM(' THOMAS ROSENTHAL ') as [also_both] + +,product_name +,REPLACE(product_name, 'a','e') +,REPLACE(product_name,'h','1') -- case sensitivity +,REPLACE(product_name,' ','_') -- replace spaces with underscore (good way to get pot_hole_case) + +,UPPER(product_name) as upper_case +,LOWER(product_name) as lower_case + +,product_name || product_size + +FROM product; + +--concat +SELECT * +,customer_first_name || ' ' || customer_last_name as customer_name +,UPPER(customer_first_name) || ' ' || UPPER(customer_last_name) as upper_last_name + +,SUBSTR(customer_last_name,4) -- any length from the 4th character +,SUBSTR(customer_last_name,4,2) --2 characters long from the 4th character +--,SUBSTR(customer_last_name, -5,4) +--,INSTR(customer_last_name,'a') + +,length(customer_first_name || ' ' || customer_last_name) + +,'THOMAS + +ROSENTHAL' -- added a linebreak +,replace('THOMAS + +ROSENTHAL', char(10), ' ') -- removing all linebreaks from this string + +FROM customer + +WHERE customer_first_name REGEXP '(a)$' + diff --git a/04_this_cohort/live_code/module_4/substring_instring_together.sql b/04_this_cohort/live_code/module_4/substring_instring_together.sql new file mode 100644 index 000000000..3bc04adb2 --- /dev/null +++ b/04_this_cohort/live_code/module_4/substring_instring_together.sql @@ -0,0 +1,16 @@ +--substring & instring together + +SELECT +'FirstWord, SecondWord, ThirdWord', + SUBSTR('FirstWord, SecondWord, ThirdWord',0, INSTR('FirstWord, SecondWord, ThirdWord',',')) as FirstDelim + --,SUBSTR('FirstWord, SecondWord, ThirdWord',0, 10) as FirstDelim -- same thing but not dynamic + ,SUBSTR('FirstWord, SecondWord, ThirdWord', + INSTR('FirstWord, SecondWord, ThirdWord',',')+1, + INSTR('FirstWord, SecondWord, ThirdWord',',')+1) as SecondDelim + + ,SUBSTR('FirstWord, SecondWord, ThirdWord', + INSTR( + (SUBSTR('FirstWord, SecondWord, ThirdWord', + INSTR('FirstWord, SecondWord, ThirdWord',',')+1)) + ,',') + + INSTR('FirstWord, SecondWord, ThirdWord',',')+1) AS ThirdDelim \ No newline at end of file From 9514836a6b1dfab651b551f8a7314211314f2ee3 Mon Sep 17 00:00:00 2001 From: "CODE, POTATO" Date: Wed, 18 Dec 2024 21:22:31 -0500 Subject: [PATCH 2/5] session 5 live code --- .../live_code/module_5/CROSS_JOINS.sql | 15 ++++++++ .../module_5/DYNAMIC_VIEW_AFTER_IMPORT.sql | 29 +++++++++++++++ .../module_5/INSERT_UPDATE_DELETE.sql | 24 +++++++++++++ .../INSERT_UPDATE_FOR_DYNAMIC_VIEW.sql | 9 +++++ .../live_code/module_5/JSON_TO_TABLE.sql | 35 +++++++++++++++++++ .../live_code/module_5/NEW_VIEW.sql | 19 ++++++++++ .../live_code/module_5/SELF_JOIN.sql | 22 ++++++++++++ .../module_5/VIEW_IN_ANOTHER_QUERY.sql | 12 +++++++ 8 files changed, 165 insertions(+) create mode 100644 04_this_cohort/live_code/module_5/CROSS_JOINS.sql create mode 100644 04_this_cohort/live_code/module_5/DYNAMIC_VIEW_AFTER_IMPORT.sql create mode 100644 04_this_cohort/live_code/module_5/INSERT_UPDATE_DELETE.sql create mode 100644 04_this_cohort/live_code/module_5/INSERT_UPDATE_FOR_DYNAMIC_VIEW.sql create mode 100644 04_this_cohort/live_code/module_5/JSON_TO_TABLE.sql create mode 100644 04_this_cohort/live_code/module_5/NEW_VIEW.sql create mode 100644 04_this_cohort/live_code/module_5/SELF_JOIN.sql create mode 100644 04_this_cohort/live_code/module_5/VIEW_IN_ANOTHER_QUERY.sql diff --git a/04_this_cohort/live_code/module_5/CROSS_JOINS.sql b/04_this_cohort/live_code/module_5/CROSS_JOINS.sql new file mode 100644 index 000000000..37e5b859e --- /dev/null +++ b/04_this_cohort/live_code/module_5/CROSS_JOINS.sql @@ -0,0 +1,15 @@ +--CROSS JOIN + +DROP TABLE IF EXISTS temp.sizes; +CREATE TEMP TABLE IF NOT EXISTS temp.sizes (size TEXT); + +INSERT INTO temp.sizes +VALUES('small'), +('medium'), +('large'); + +SELECT * from temp.sizes; + +SELECT product_name, size +FROM product +CROSS JOIN temp.sizes \ No newline at end of file diff --git a/04_this_cohort/live_code/module_5/DYNAMIC_VIEW_AFTER_IMPORT.sql b/04_this_cohort/live_code/module_5/DYNAMIC_VIEW_AFTER_IMPORT.sql new file mode 100644 index 000000000..bb18ac1e2 --- /dev/null +++ b/04_this_cohort/live_code/module_5/DYNAMIC_VIEW_AFTER_IMPORT.sql @@ -0,0 +1,29 @@ +--DYNAMIC VIEW +-- THIS ONLY WORKS IF YOU HAVE DONE THE PROPER STEPS FOR IMPORTING! INSERTING A NEW DATE! AND UPDATING THE NEW DATA TO "TODAY" +DROP VIEW IF EXISTS todays_vendor_daily_sales; +CREATE VIEW IF NOT EXISTS todays_vendor_daily_sales AS + + SELECT + md.market_date + ,market_day + ,market_week + ,market_year + ,vendor_name + ,SUM(quantity*cost_to_customer_per_qty) as sales + + FROM market_date_info md + INNER JOIN ( + SELECT * FROM customer_purchases + UNION + SELECT * FROM new_customer_purchases) cp + + ON md.market_date = cp.market_date + INNER JOIN vendor v + ON cp.vendor_id = v.vendor_id + + /*PICK ONE OF THE WHERE STATEMENTS!*/ + --WHERE md.market_date = strftime('%Y-%m-%d',DATE('now'),'-1 day')-- today! -- + WHERE md.market_date = DATE('now','localtime') + --WHERE md.market_date = '2024-12-18' + + GROUP BY cp.market_date, v.vendor_id \ No newline at end of file diff --git a/04_this_cohort/live_code/module_5/INSERT_UPDATE_DELETE.sql b/04_this_cohort/live_code/module_5/INSERT_UPDATE_DELETE.sql new file mode 100644 index 000000000..d0508e742 --- /dev/null +++ b/04_this_cohort/live_code/module_5/INSERT_UPDATE_DELETE.sql @@ -0,0 +1,24 @@ +-- INSERT UPDATE DELETE + +--1)add a product to the table +--2) change the product_size for that product +--3) delete our product + +DROP TABLE IF EXISTS temp.product_expanded; +CREATE TEMP TABLE product_expanded AS + SELECT * FROM product; + +--INSERT +INSERT INTO product_expanded +VALUES(26,'Almonds','1 lb',1,'lbs'); + +--UPDATE +-- change the product_size for almonds to 1/2 kg +UPDATE product_expanded +SET product_size = '1/2 kg', product_qty_type = 'kg' +WHERE product_id = 26; + +--DELETE our almonds +DELETE FROM product_expanded +--SELECT * FROM product_expanded +WHERE product_id = 26 \ No newline at end of file diff --git a/04_this_cohort/live_code/module_5/INSERT_UPDATE_FOR_DYNAMIC_VIEW.sql b/04_this_cohort/live_code/module_5/INSERT_UPDATE_FOR_DYNAMIC_VIEW.sql new file mode 100644 index 000000000..d63f8d565 --- /dev/null +++ b/04_this_cohort/live_code/module_5/INSERT_UPDATE_FOR_DYNAMIC_VIEW.sql @@ -0,0 +1,9 @@ +UPDATE new_customer_purchases +SET market_date =DATE('now','localtime') + +INSERT INTO market_date_info +VALUES('2024-12-18','Wednesday','51','2024','8:00 AM','2:00 PM','nothing interesting','Winter','0','4',1,0) + + +SELECT * FROM todays_vendor_daily_sales + diff --git a/04_this_cohort/live_code/module_5/JSON_TO_TABLE.sql b/04_this_cohort/live_code/module_5/JSON_TO_TABLE.sql new file mode 100644 index 000000000..b15f47c7d --- /dev/null +++ b/04_this_cohort/live_code/module_5/JSON_TO_TABLE.sql @@ -0,0 +1,35 @@ +--JSON to a TABLE + +--create a temp TABLE +--insert the json as a long string +--write a json_each statement +--use the json_each statement as a subquery to extract our column values +-- now we have a table! + +DROP TABLE IF EXISTS temp.[new_json]; +CREATE TEMP TABLE IF NOT EXISTS temp.new_json +( +the_json BLOB -- the column and the column type +); + +INSERT INTO temp.new_json +VALUES( +'[ + { + "country": "Afghanistan", + "city": "Kabul" + }, + { + "country": "Albania", + "city": "Tirana" + }]' + ); + +SELECT key +,JSON_EXTRACT(value,'$.country') as country +,JSON_EXTRACT(value,'$.city') as city + +FROM ( + SELECT * + FROM new_json,JSON_EACH(new_json.col1, '$') + ) x diff --git a/04_this_cohort/live_code/module_5/NEW_VIEW.sql b/04_this_cohort/live_code/module_5/NEW_VIEW.sql new file mode 100644 index 000000000..ffab90141 --- /dev/null +++ b/04_this_cohort/live_code/module_5/NEW_VIEW.sql @@ -0,0 +1,19 @@ +--VIEW +DROP VIEW IF EXISTS vendor_daily_sales; +CREATE VIEW IF NOT EXISTS vendor_daily_sales AS + + SELECT + md.market_date + ,market_day + ,market_week + ,market_year + ,vendor_name + ,SUM(quantity*cost_to_customer_per_qty) as sales + + FROM market_date_info md + INNER JOIN customer_purchases cp + ON md.market_date = cp.market_date + INNER JOIN vendor v + ON cp.vendor_id = v.vendor_id + + GROUP BY cp.market_date, v.vendor_id \ No newline at end of file diff --git a/04_this_cohort/live_code/module_5/SELF_JOIN.sql b/04_this_cohort/live_code/module_5/SELF_JOIN.sql new file mode 100644 index 000000000..8fec61b59 --- /dev/null +++ b/04_this_cohort/live_code/module_5/SELF_JOIN.sql @@ -0,0 +1,22 @@ +-- SELF JOIN +drop table if exists temp.employees; +create temp table temp.employees +( +emp_id int, +emp_name text, +mgr_id int +); + +insert into temp.employees +Values(1,'Thomas',3) +,(2,'Ernani',4) +,(3,'Rohan',null) +,(4,'Jennie',3); + + +SELECT * FROM temp.employees + +select a.emp_name,b.emp_name as mgr_name +from temp.employees a +left join temp.employees b + on a.mgr_id = b.emp_id diff --git a/04_this_cohort/live_code/module_5/VIEW_IN_ANOTHER_QUERY.sql b/04_this_cohort/live_code/module_5/VIEW_IN_ANOTHER_QUERY.sql new file mode 100644 index 000000000..eb79d08b9 --- /dev/null +++ b/04_this_cohort/live_code/module_5/VIEW_IN_ANOTHER_QUERY.sql @@ -0,0 +1,12 @@ +-- using a view in another query +SELECT +market_year +,market_week +,vendor_name +,SUM(sales) as weekly_sales + +FROM vendor_daily_sales + +GROUP BY market_year +,market_week +,vendor_name \ No newline at end of file From 3db914a11e4560c7630879c19d40fe97caf9b0b8 Mon Sep 17 00:00:00 2001 From: "CODE, POTATO" Date: Thu, 19 Dec 2024 17:08:22 -0500 Subject: [PATCH 3/5] code associated with session 6 slides --- 04_this_cohort/live_code/module_6/1nf.sql | 18 + 04_this_cohort/live_code/module_6/2nf.sql | 52 ++ 04_this_cohort/live_code/module_6/3nf.sql | 37 + .../module_6/SQLite_and_python.ipynb | 717 ++++++++++++++++++ .../live_code/module_6/denormalized.sql | 14 + .../module_6/penguins_in_python_sql.sql | 9 + 6 files changed, 847 insertions(+) create mode 100644 04_this_cohort/live_code/module_6/1nf.sql create mode 100644 04_this_cohort/live_code/module_6/2nf.sql create mode 100644 04_this_cohort/live_code/module_6/3nf.sql create mode 100644 04_this_cohort/live_code/module_6/SQLite_and_python.ipynb create mode 100644 04_this_cohort/live_code/module_6/denormalized.sql create mode 100644 04_this_cohort/live_code/module_6/penguins_in_python_sql.sql diff --git a/04_this_cohort/live_code/module_6/1nf.sql b/04_this_cohort/live_code/module_6/1nf.sql new file mode 100644 index 000000000..f57239761 --- /dev/null +++ b/04_this_cohort/live_code/module_6/1nf.sql @@ -0,0 +1,18 @@ +--1nf +drop table if exists temp.hold; +CREATE TABLE temp.hold AS +SELECT DISTINCT +name, +OS, +SUBSTR(software, 1, INSTR(software,',')-1) AS s1, +SUBSTR(software,INSTR(software,',')+1, INSTR(SUBSTR(software, INSTR(software, ',')+1),',')-1) as s2, +SUBSTR(software,INSTR(SUBSTR(software,INSTR(software,',')+1),',')+INSTR(software,',')+1) as s3, +supervisor + +FROM skills; + +SELECT name,OS,s1 as software, supervisor FROM hold +UNION +SELECT name,OS,s2 as software, supervisor FROM hold +UNION +SELECT name,OS,s3 as software, supervisor FROM hold \ No newline at end of file diff --git a/04_this_cohort/live_code/module_6/2nf.sql b/04_this_cohort/live_code/module_6/2nf.sql new file mode 100644 index 000000000..234d98d9a --- /dev/null +++ b/04_this_cohort/live_code/module_6/2nf.sql @@ -0,0 +1,52 @@ +-- 2nf +drop table if exists temp.student; +drop table if exists temp.supervisor; +drop table if exists temp.student_software; + +create temp table if not exists temp.supervisor +( +id INTEGER PRIMARY KEY AUTOINCREMENT, +name TEXT +); + +INSERT INTO temp.supervisor(name) +select distinct supervisor +from skills; + +create temp table if not exists temp.student +( +id INTEGER PRIMARY KEY AUTOINCREMENT, +name TEXT, +OS TEXT, +supervisor_id INTEGER, +CONSTRAINT "fk_supervisor_id" FOREIGN KEY ("supervisor_id") REFERENCES "supervisor" ("id") +) + +INSERT INTO student(name, OS, supervisor_id) +SELECT DISTINCT +h.name +,OS +,s.id AS supervisor_id + +FROM hold h +JOIN supervisor s + on h.supervisor = s.name + +CREATE TABLE temp.student_software AS +SELECT id, software + +FROM student s +JOIN ( + SELECT name,OS,s1 as software, supervisor FROM hold + UNION + SELECT name,OS,s2 as software, supervisor FROM hold + UNION + SELECT name,OS,s3 as software, supervisor FROM hold +) u +ON s.name = u.name + +--select * from student +--select * from supervisor +select * from student_software + + diff --git a/04_this_cohort/live_code/module_6/3nf.sql b/04_this_cohort/live_code/module_6/3nf.sql new file mode 100644 index 000000000..d5f037013 --- /dev/null +++ b/04_this_cohort/live_code/module_6/3nf.sql @@ -0,0 +1,37 @@ +--3nf +drop table if exists temp.OS; +drop table if exists temp.software; +create temp table if not exists temp.OS +( +OS_id INTEGER, +OS TEXT, +win_only TEXT +); + +insert into temp.OS +values(1,"win","TRUE"), + (2,"mac","FALSE"); + + +create temp table if not exists temp.software +( +software_id INTEGER PRIMARY KEY AUTOINCREMENT, +software TEXT, +win_only TEXT +); + +INSERT INTO temp.software(software, win_only) +SELECT DISTINCT software, win_only +FROM student_software s +CROSS JOIN ( + SELECT * FROM OS WHERE OS = 'mac' +); + +UPDATE software +SET win_only = 'TRUE' +WHERE software.software = ' MSSQL'; + +SELECT * FROM OS +--SELECT * FROM software + + diff --git a/04_this_cohort/live_code/module_6/SQLite_and_python.ipynb b/04_this_cohort/live_code/module_6/SQLite_and_python.ipynb new file mode 100644 index 000000000..9fddb799a --- /dev/null +++ b/04_this_cohort/live_code/module_6/SQLite_and_python.ipynb @@ -0,0 +1,717 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "05e1dbf0", + "metadata": {}, + "source": [ + "# Connect to FarmersMarket.db" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "f1d8cb62", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import sqlite3\n", + "#set your location, slash direction will change for windows and mac\n", + "DB = '/Users/thomas/Documents/GitHub/02-intro_sql/05_src/sql/farmersmarket.db' \n", + "#establish your connection\n", + "conn = sqlite3.connect(DB, isolation_level=None,\n", + " detect_types=sqlite3.PARSE_COLNAMES)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "1204e343", + "metadata": {}, + "outputs": [], + "source": [ + "#run your query, use \"\\\" to allow line breaks\n", + "db_df = pd.read_sql_query(\"SELECT p.*,pc.product_category_name \\\n", + " FROM product p \\\n", + " JOIN product_category pc \\\n", + " ON p.product_category_id = pc.product_category_id\"\n", + " ,conn)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "5c7863ee-08cd-4095-b80a-61f82425bd2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
product_idproduct_nameproduct_sizeproduct_category_idproduct_qty_typeproduct_category_name
01Habanero Peppers - Organicmedium1lbsFresh Fruits & Vegetables
12Jalapeno Peppers - Organicsmall1lbsFresh Fruits & Vegetables
23Poblano Peppers - Organiclarge1unitFresh Fruits & Vegetables
34Banana Peppers - Jar8 oz3unitPackaged Prepared Food
45Whole Wheat Bread1.5 lbs3unitPackaged Prepared Food
56Cut Zinnias Bouquetmedium5unitPlants & Flowers
67Apple Pie10\"3unitPackaged Prepared Food
79Sweet Potatoesmedium1lbsFresh Fruits & Vegetables
810Eggs1 dozen6unitEggs & Meat (Fresh or Frozen)
911Pork Chops1 lb6lbsEggs & Meat (Fresh or Frozen)
1012Baby Salad Lettuce Mix - Bag1/2 lb1unitFresh Fruits & Vegetables
1113Baby Salad Lettuce Mix1 lb1lbsFresh Fruits & Vegetables
1214Red PotatoesNone1NoneFresh Fruits & Vegetables
1315Red Potatoes - Small1NoneFresh Fruits & Vegetables
1416Sweet CornEar1unitFresh Fruits & Vegetables
1517Carrotssold by weight1lbsFresh Fruits & Vegetables
1618Carrots - Organicbunch1unitFresh Fruits & Vegetables
1719Farmer's Market Resuable Shopping Bagmedium7unitNon-Edible Products
1820Homemade Beeswax Candles6\"7unitNon-Edible Products
1921Organic Cherry Tomatoespint1unitFresh Fruits & Vegetables
2022Roma Tomatoesmedium1lbsFresh Fruits & Vegetables
2123Maple Syrup - Jar8 oz2unitPackaged Pantry Goods
228Cherry Pie10\"3unitPackaged Prepared Food
\n", + "
" + ], + "text/plain": [ + " product_id product_name product_size \\\n", + "0 1 Habanero Peppers - Organic medium \n", + "1 2 Jalapeno Peppers - Organic small \n", + "2 3 Poblano Peppers - Organic large \n", + "3 4 Banana Peppers - Jar 8 oz \n", + "4 5 Whole Wheat Bread 1.5 lbs \n", + "5 6 Cut Zinnias Bouquet medium \n", + "6 7 Apple Pie 10\" \n", + "7 9 Sweet Potatoes medium \n", + "8 10 Eggs 1 dozen \n", + "9 11 Pork Chops 1 lb \n", + "10 12 Baby Salad Lettuce Mix - Bag 1/2 lb \n", + "11 13 Baby Salad Lettuce Mix 1 lb \n", + "12 14 Red Potatoes None \n", + "13 15 Red Potatoes - Small \n", + "14 16 Sweet Corn Ear \n", + "15 17 Carrots sold by weight \n", + "16 18 Carrots - Organic bunch \n", + "17 19 Farmer's Market Resuable Shopping Bag medium \n", + "18 20 Homemade Beeswax Candles 6\" \n", + "19 21 Organic Cherry Tomatoes pint \n", + "20 22 Roma Tomatoes medium \n", + "21 23 Maple Syrup - Jar 8 oz \n", + "22 8 Cherry Pie 10\" \n", + "\n", + " product_category_id product_qty_type product_category_name \n", + "0 1 lbs Fresh Fruits & Vegetables \n", + "1 1 lbs Fresh Fruits & Vegetables \n", + "2 1 unit Fresh Fruits & Vegetables \n", + "3 3 unit Packaged Prepared Food \n", + "4 3 unit Packaged Prepared Food \n", + "5 5 unit Plants & Flowers \n", + "6 3 unit Packaged Prepared Food \n", + "7 1 lbs Fresh Fruits & Vegetables \n", + "8 6 unit Eggs & Meat (Fresh or Frozen) \n", + "9 6 lbs Eggs & Meat (Fresh or Frozen) \n", + "10 1 unit Fresh Fruits & Vegetables \n", + "11 1 lbs Fresh Fruits & Vegetables \n", + "12 1 None Fresh Fruits & Vegetables \n", + "13 1 None Fresh Fruits & Vegetables \n", + "14 1 unit Fresh Fruits & Vegetables \n", + "15 1 lbs Fresh Fruits & Vegetables \n", + "16 1 unit Fresh Fruits & Vegetables \n", + "17 7 unit Non-Edible Products \n", + "18 7 unit Non-Edible Products \n", + "19 1 unit Fresh Fruits & Vegetables \n", + "20 1 lbs Fresh Fruits & Vegetables \n", + "21 2 unit Packaged Pantry Goods \n", + "22 3 unit Packaged Prepared Food " + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "db_df" + ] + }, + { + "cell_type": "markdown", + "id": "8b7c36c0", + "metadata": {}, + "source": [ + "Export the query:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ee17555e", + "metadata": {}, + "outputs": [], + "source": [ + "#save\n", + "db_df.to_csv('database-py.CSV', index=False)" + ] + }, + { + "cell_type": "markdown", + "id": "ed14b573", + "metadata": {}, + "source": [ + "# Run a SQL query with pandasql" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "ac82fb05", + "metadata": {}, + "outputs": [], + "source": [ + "#!pip install pandasql" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "4f783bd4", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import pandasql as sql #this allows us to run SQLite queries!\n", + "p = \"https://raw.githubusercontent.com/allisonhorst/palmerpenguins/master/inst/extdata/penguins.csv\"\n", + "penguins = pd.read_csv(p) #create a dataframe\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "7892f454", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
speciesislandbill_length_mmbill_depth_mmflipper_length_mmbody_mass_gsexyear
0AdelieTorgersen39.118.7181.03750.0male2007
1AdelieTorgersen39.517.4186.03800.0female2007
2AdelieTorgersen40.318.0195.03250.0female2007
3AdelieTorgersenNaNNaNNaNNaNNaN2007
4AdelieTorgersen36.719.3193.03450.0female2007
...........................
339ChinstrapDream55.819.8207.04000.0male2009
340ChinstrapDream43.518.1202.03400.0female2009
341ChinstrapDream49.618.2193.03775.0male2009
342ChinstrapDream50.819.0210.04100.0male2009
343ChinstrapDream50.218.7198.03775.0female2009
\n", + "

344 rows × 8 columns

\n", + "
" + ], + "text/plain": [ + " species island bill_length_mm bill_depth_mm flipper_length_mm \\\n", + "0 Adelie Torgersen 39.1 18.7 181.0 \n", + "1 Adelie Torgersen 39.5 17.4 186.0 \n", + "2 Adelie Torgersen 40.3 18.0 195.0 \n", + "3 Adelie Torgersen NaN NaN NaN \n", + "4 Adelie Torgersen 36.7 19.3 193.0 \n", + ".. ... ... ... ... ... \n", + "339 Chinstrap Dream 55.8 19.8 207.0 \n", + "340 Chinstrap Dream 43.5 18.1 202.0 \n", + "341 Chinstrap Dream 49.6 18.2 193.0 \n", + "342 Chinstrap Dream 50.8 19.0 210.0 \n", + "343 Chinstrap Dream 50.2 18.7 198.0 \n", + "\n", + " body_mass_g sex year \n", + "0 3750.0 male 2007 \n", + "1 3800.0 female 2007 \n", + "2 3250.0 female 2007 \n", + "3 NaN NaN 2007 \n", + "4 3450.0 female 2007 \n", + ".. ... ... ... \n", + "339 4000.0 male 2009 \n", + "340 3400.0 female 2009 \n", + "341 3775.0 male 2009 \n", + "342 4100.0 male 2009 \n", + "343 3775.0 female 2009 \n", + "\n", + "[344 rows x 8 columns]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "penguins" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "8036d336", + "metadata": {}, + "outputs": [], + "source": [ + "yrly_penguins = sql.sqldf('''SELECT DISTINCT year, COUNT(*) AS count, \n", + " SUM(COUNT(*)) OVER (ORDER BY year) AS running_total\n", + " FROM penguins\n", + " GROUP BY year''') #run a SQLite query with sqldf()" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "80fd4dd6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
yearcountrunning_total
02007110110
12008114224
22009120344
\n", + "
" + ], + "text/plain": [ + " year count running_total\n", + "0 2007 110 110\n", + "1 2008 114 224\n", + "2 2009 120 344" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "yrly_penguins" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0cd3de3f-fb4f-46ac-ad42-23971226e5d0", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/04_this_cohort/live_code/module_6/denormalized.sql b/04_this_cohort/live_code/module_6/denormalized.sql new file mode 100644 index 000000000..da4208587 --- /dev/null +++ b/04_this_cohort/live_code/module_6/denormalized.sql @@ -0,0 +1,14 @@ +-- normal forms creation + +drop table if exists temp.skills; +create temp table if not exists temp.skills +( +name TEXT, +OS TEXT, +software TEXT, +supervisor TEXT +); + +insert into temp.skills +values("A","win","VSCode, MSSQL, RStudio", "Eric Yu"), + ("Thomas","mac", "Spyder, SQLite, RStudio", "Rohan Alexander"); diff --git a/04_this_cohort/live_code/module_6/penguins_in_python_sql.sql b/04_this_cohort/live_code/module_6/penguins_in_python_sql.sql new file mode 100644 index 000000000..2326c1d29 --- /dev/null +++ b/04_this_cohort/live_code/module_6/penguins_in_python_sql.sql @@ -0,0 +1,9 @@ +select * from penguins; + +-- how many penguins were identified each year +SELECT DISTINCT year +,COUNT(*) AS count +,SUM(COUNT(*)) OVER (ORDER BY year) AS running_total + + FROM penguins +GROUP BY year \ No newline at end of file From 6a44d4dfba8fc75ae01c0b4aa38ad7329947e88b Mon Sep 17 00:00:00 2001 From: drdhennessy Date: Sun, 22 Dec 2024 18:30:42 -0500 Subject: [PATCH 4/5] Assignment 2 (4 files) --- 02_activities/assignments/Assignment2.md | 14 +- .../assignments/Bookstore1.drawio.pdf | Bin 0 -> 38007 bytes .../assignments/Bookstore2.drawio.pdf | Bin 0 -> 41105 bytes 02_activities/assignments/assignment2.sql | 196 +++++++++++++++++- 4 files changed, 202 insertions(+), 8 deletions(-) create mode 100644 02_activities/assignments/Bookstore1.drawio.pdf create mode 100644 02_activities/assignments/Bookstore2.drawio.pdf diff --git a/02_activities/assignments/Assignment2.md b/02_activities/assignments/Assignment2.md index 05244623b..d90a0944c 100644 --- a/02_activities/assignments/Assignment2.md +++ b/02_activities/assignments/Assignment2.md @@ -44,9 +44,13 @@ Additionally, include a date table. There are several tools online you can use, I'd recommend [Draw.io](https://www.drawio.com/) or [LucidChart](https://www.lucidchart.com/pages/). **HINT:** You do not need to create any data for this prompt. This is a conceptual model only. +# This is the pdf for this prompt: +[text](Bookstore1.drawio.pdf) #### Prompt 2 We want to create employee shifts, splitting up the day into morning and evening. Add this to the ERD. +# This is the pdf for this prompt: +[text](Bookstore2.drawio.pdf) #### Prompt 3 The store wants to keep customer addresses. Propose two architectures for the CUSTOMER_ADDRESS table, one that will retain changes, and another that will overwrite. Which is type 1, which is type 2? @@ -54,7 +58,15 @@ The store wants to keep customer addresses. Propose two architectures for the CU **HINT:** search type 1 vs type 2 slowly changing dimensions. ``` -Your answer... +The two different types of architectures related to the concept of Slowly Changing Dimensions (SCD) can be used to store a CUSTOMER_ADDRESS table are: + +Overwrite (Type 1 SCD): simply overwrites an existing record with the new data. +Only the latest address is stored. This is simple to implement and maintain, and requires less storage space as no historical data is kept. That no historical data is kept is also the downside since the system can't track changes over time. + +Add New Row (Type 2 SCD): a new row is added for each change, while the old one is retained, which allows you to maintain a complete history of address changes for each customer. If knowing about address changes is important then this is obviously useful; however, it is more complex and does require more storage space. + + + ``` *** diff --git a/02_activities/assignments/Bookstore1.drawio.pdf b/02_activities/assignments/Bookstore1.drawio.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fdb44b1fa0559238866976c1073aa69c7269f20b GIT binary patch literal 38007 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

  • -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 literal 0 HcmV?d00001 diff --git a/02_activities/assignments/Bookstore2.drawio.pdf b/02_activities/assignments/Bookstore2.drawio.pdf new file mode 100644 index 0000000000000000000000000000000000000000..788f010e9cf3390261bcbe2d216cd4824231d60c GIT binary patch literal 41105 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®D3ujj8Tm>{nfO{bk$_} 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 bD;}N}?w-Exxc^v?Pe?%YAsd^bw$lFry~iM< literal 0 HcmV?d00001 diff --git a/02_activities/assignments/assignment2.sql b/02_activities/assignments/assignment2.sql index 5ad40748a..6d14482da 100644 --- a/02_activities/assignments/assignment2.sql +++ b/02_activities/assignments/assignment2.sql @@ -19,7 +19,11 @@ HINT: keep the syntax the same, but edited the correct components with the strin The `||` values concatenate the columns into strings. Edit the appropriate columns -- you're making two edits -- and the NULL rows will be fixed. All the other rows will remain the same.) */ - +SELECT + product_name || ', ' || + COALESCE(product_size, '') || ' (' || + COALESCE(product_qty_type, 'unit') || ')' +FROM product; --Windowed Functions @@ -32,17 +36,63 @@ each new market date for each customer, or select only the unique market dates p (without purchase details) and number those visits. HINT: One of these approaches uses ROW_NUMBER() and one uses DENSE_RANK(). */ +--Using Row_number: +SELECT + customer_id, + market_date, + product_id, + quantity, + cost_to_customer_per_qty, + ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY market_date) AS visit_number +FROM customer_purchases; + +--Using Dense_rank: +SELECT + customer_id, + market_date, + DENSE_RANK() OVER (PARTITION BY customer_id ORDER BY market_date) AS visit_number +FROM + (SELECT DISTINCT customer_id, market_date FROM customer_purchases) AS unique_visits; /* 2. Reverse the numbering of the query from a part so each customer’s most recent visit is labeled 1, then write another query that uses this one as a subquery (or temp table) and filters the results to only the customer’s most recent visit. */ - +--Reverse the numbering +SELECT + customer_id, + market_date, + ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY market_date DESC) AS visit_number +FROM customer_purchases; + +--Using a sub-query: +SELECT + customer_id, + market_date, + visit_number +FROM + ( + SELECT + customer_id, + market_date, + ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY market_date DESC) AS visit_number + FROM + customer_purchases + ) AS subquery +WHERE + visit_number = 1; /* 3. Using a COUNT() window function, include a value along with each row of the customer_purchases table that indicates how many different times that customer has purchased that product_id. */ - +SELECT + customer_id, + product_id, + market_date, + quantity, + cost_to_customer_per_qty, + COUNT(*) OVER (PARTITION BY customer_id, product_id) AS purchase_count +FROM customer_purchases; -- String manipulations @@ -57,10 +107,23 @@ Remove any trailing or leading whitespaces. Don't just use a case statement for Hint: you might need to use INSTR(product_name,'-') to find the hyphens. INSTR will help split the column. */ +SELECT + product_name, + CASE + WHEN INSTR(product_name, '-') > 0 THEN + TRIM(SUBSTR(product_name, INSTR(product_name, '-') + 1)) + ELSE + NULL + END AS description +FROM product; /* 2. Filter the query to show any product_size value that contain a number with REGEXP. */ - +SELECT + product_name, + product_size +FROM product +WHERE product_size REGEXP '[0-9]'; -- UNION @@ -73,7 +136,34 @@ HINT: There are a possibly a few ways to do this query, but if you're struggling 3) Query the second temp table twice, once for the best day, once for the worst day, with a UNION binding them. */ - +WITH SalesPerDate AS ( + SELECT + market_date, + SUM(quantity * cost_to_customer_per_qty) AS total_sales + FROM customer_purchases + GROUP BY market_date +), +RankedSales AS ( + SELECT + market_date, + total_sales, + RANK() OVER (ORDER BY total_sales DESC) AS rank_desc, + RANK() OVER (ORDER BY total_sales ASC) AS rank_asc + FROM SalesPerDate +) +SELECT + market_date, + total_sales, + 'Best Day' AS sales_category +FROM RankedSales +WHERE rank_desc = 1 +UNION +SELECT + market_date, + total_sales, + 'Worst Day' AS sales_category +FROM RankedSales +WHERE rank_asc = 1; /* SECTION 3 */ @@ -89,26 +179,94 @@ Think a bit about the row counts: how many distinct vendors, product names are t How many customers are there (y). Before your final group by you should have the product of those two queries (x*y). */ - - +WITH VendorProduct AS ( + SELECT + v.vendor_name, + p.product_name, + vi.original_price AS price + FROM + vendor_inventory vi + JOIN + vendor v ON vi.vendor_id = v.vendor_id + JOIN + product p ON vi.product_id = p.product_id +), +CustomerCount AS ( + SELECT + COUNT(*) AS customer_count + FROM + customer +) +SELECT + vp.vendor_name, + vp.product_name, + SUM(5 * vp.price * cc.customer_count) AS total_revenue +FROM + VendorProduct vp, + CustomerCount cc +GROUP BY + vp.vendor_name, vp.product_name; + + -- INSERT /*1. Create a new table "product_units". This table will contain only products where the `product_qty_type = 'unit'`. It should use all of the columns from the product table, as well as a new column for the `CURRENT_TIMESTAMP`. Name the timestamp column `snapshot_timestamp`. */ +CREATE TABLE product_units AS +SELECT + *, + CURRENT_TIMESTAMP AS snapshot_timestamp +FROM + product +WHERE + product_qty_type = 'unit'; /*2. Using `INSERT`, add a new row to the product_units table (with an updated timestamp). This can be any product you desire (e.g. add another record for Apple Pie). */ +INSERT INTO product_units ( + product_id, + product_name, + product_size, + product_category_id, + product_qty_type, + snapshot_timestamp +) +VALUES ( + 1001, + 'Apple Pie', + '10"', + 3, + 'unit', + CURRENT_TIMESTAMP +); -- DELETE /* 1. Delete the older record for the whatever product you added. HINT: If you don't specify a WHERE clause, you are going to have a bad time.*/ +--Identify the product_id of the older record: +SELECT + product_id, + product_name, + snapshot_timestamp +FROM + product_units +WHERE + product_name = 'Apple Pie' +ORDER BY + snapshot_timestamp ASC; + +--Delete older record: +DELETE FROM + product_units +WHERE + product_id = 1001; -- UPDATE @@ -128,6 +286,30 @@ Finally, make sure you have a WHERE statement to update the right row, you'll need to use product_units.product_id to refer to the correct row within the product_units table. When you have all of these components, you can run the update statement. */ +-- 1: Add the column +ALTER TABLE product_units +ADD current_quantity INT; + +-- 2: Get the last quantity per product using ROW_NUMBER() +WITH LastQuantity AS ( + SELECT + vi.product_id, + COALESCE(vi.quantity, 0) AS last_quantity, + ROW_NUMBER() OVER (PARTITION BY vi.product_id ORDER BY vi.market_date DESC) AS row_num + FROM + vendor_inventory vi +) +-- 3: Update the current_quantity in product_units +UPDATE product_units +SET current_quantity = ( + SELECT + lq.last_quantity + FROM + LastQuantity lq + WHERE + lq.product_id = product_units.product_id + AND lq.row_num = 1 +); From a4f28e26b7fc192d94ace9e8129033d127d99f15 Mon Sep 17 00:00:00 2001 From: drdhennessy Date: Mon, 30 Dec 2024 15:40:35 -0500 Subject: [PATCH 5/5] Assignment 2 (revised) --- 02_activities/assignments/assignment2.sql | 111 +++++++++++++--------- 1 file changed, 65 insertions(+), 46 deletions(-) diff --git a/02_activities/assignments/assignment2.sql b/02_activities/assignments/assignment2.sql index 6d14482da..726c96017 100644 --- a/02_activities/assignments/assignment2.sql +++ b/02_activities/assignments/assignment2.sql @@ -179,33 +179,42 @@ Think a bit about the row counts: how many distinct vendors, product names are t How many customers are there (y). Before your final group by you should have the product of those two queries (x*y). */ +-- Get vendor names, product names, and their prices WITH VendorProduct AS ( SELECT v.vendor_name, p.product_name, vi.original_price AS price - FROM - vendor_inventory vi - JOIN - vendor v ON vi.vendor_id = v.vendor_id - JOIN - product p ON vi.product_id = p.product_id + FROM vendor_inventory vi + JOIN vendor v ON vi.vendor_id = v.vendor_id + JOIN product p ON vi.product_id = p.product_id ), + +-- Get the count of customers CustomerCount AS ( SELECT COUNT(*) AS customer_count - FROM - customer + FROM customer +), + +-- Cross join to simulate each product being sold to every customer +VendorProductCustomer AS ( + SELECT + vp.vendor_name, + vp.product_name, + vp.price, + cc.customer_count + FROM VendorProduct vp + CROSS JOIN CustomerCount cc ) + +-- Calculate total revenue for each vendor and product SELECT - vp.vendor_name, - vp.product_name, - SUM(5 * vp.price * cc.customer_count) AS total_revenue -FROM - VendorProduct vp, - CustomerCount cc -GROUP BY - vp.vendor_name, vp.product_name; + vendor_name, + product_name, + SUM(5 * price * customer_count) AS total_revenue +FROM VendorProductCustomer +GROUP BY vendor_name, product_name; -- INSERT @@ -218,10 +227,8 @@ CREATE TABLE product_units AS SELECT *, CURRENT_TIMESTAMP AS snapshot_timestamp -FROM - product -WHERE - product_qty_type = 'unit'; +FROM product +WHERE product_qty_type = 'unit'; /*2. Using `INSERT`, add a new row to the product_units table (with an updated timestamp). @@ -243,30 +250,33 @@ VALUES ( 'unit', CURRENT_TIMESTAMP ); - +--Note: I gave the new record Apple Pie a different product_id to verify +--that the last one was deleted in the next part. -- DELETE /* 1. Delete the older record for the whatever product you added. HINT: If you don't specify a WHERE clause, you are going to have a bad time.*/ ---Identify the product_id of the older record: +--Identify the product_id of the older record: SELECT product_id, product_name, snapshot_timestamp -FROM - product_units -WHERE - product_name = 'Apple Pie' -ORDER BY - snapshot_timestamp ASC; +FROM product_units +WHERE product_name = 'Apple Pie' +ORDER BY snapshot_timestamp ASC +LIMIT 1; --Delete older record: -DELETE FROM - product_units -WHERE - product_id = 1001; +DELETE FROM product_units +WHERE product_id = ( + SELECT product_id + FROM product_units + WHERE product_name = 'Apple Pie' + ORDER BY snapshot_timestamp ASC + LIMIT 1 + ); -- UPDATE @@ -286,30 +296,39 @@ Finally, make sure you have a WHERE statement to update the right row, you'll need to use product_units.product_id to refer to the correct row within the product_units table. When you have all of these components, you can run the update statement. */ --- 1: Add the column + +-- Add the column: ALTER TABLE product_units ADD current_quantity INT; --- 2: Get the last quantity per product using ROW_NUMBER() -WITH LastQuantity AS ( +-- Get the last quantity per product: +WITH last_quantity AS ( SELECT - vi.product_id, - COALESCE(vi.quantity, 0) AS last_quantity, - ROW_NUMBER() OVER (PARTITION BY vi.product_id ORDER BY vi.market_date DESC) AS row_num + pu.product_id, + first_value(quantity) OVER (PARTITION BY pu.product_id ORDER BY market_date DESC) AS quantity FROM vendor_inventory vi + RIGHT JOIN + product_units pu + ON vi.product_id = pu.product_id ) --- 3: Update the current_quantity in product_units +-- Update the current_quantity in product_units: UPDATE product_units -SET current_quantity = ( - SELECT - lq.last_quantity +SET current_quantity = COALESCE(last_quantity.quantity,0) FROM - LastQuantity lq + last_quantity WHERE - lq.product_id = product_units.product_id - AND lq.row_num = 1 -); + product_units.product_id = last_quantity.product_id; +--Check +SELECT + product_id, + product_name, + product_size, + product_category_id, + product_qty_type, + current_quantity +FROM + product_units;