diff --git a/02_activities/assignments/DC_Cohort/Assignment2.md b/02_activities/assignments/DC_Cohort/Assignment2.md index 72f8c71fd..9dfced9ea 100644 --- a/02_activities/assignments/DC_Cohort/Assignment2.md +++ b/02_activities/assignments/DC_Cohort/Assignment2.md @@ -48,16 +48,42 @@ There are several tools online you can use, I'd recommend [Draw.io](https://www. **HINT:** You do not need to create any data for this prompt. This is a conceptual model only. +![alt text](<../../../Assignments/Small Bookstore Logical Model.drawio.png>) + #### Prompt 2 We want to create employee shifts, splitting up the day into morning and evening. Add this to the ERD. +![alt text](<../../../Assignments/Small Bookstore Logical Model.employee_shifts.png>) + #### 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? **HINT:** search type 1 vs type 2 slowly changing dimensions. ``` -Your answer... +Type 1 architecture is when the database is designed to be overwritten. In this case, if a customer initially lived in one city and then moved, the old address is overwritten with the new one. In this design, the record of the old address is gone forever. + ----------------------- +| customer_address_type1 | + ----------------------- +| customer_id | +| street_address | +| city | + ----------------------- + + +Type 2 architecture is when the database is designed to retain the full history by creating a new row every time new data is inputted. In this case, if a customer initially lived in one city and then moved, the old address is marked inactive and the new address data is retained. +----------------------- +| customer_address_type1 | + ----------------------- +| customer_id | +| street_address | +| city | +| address_start_date | +| address_end_date | +| is_current |--> boolean + ----------------------- + +In my ERD, the customer_address_type1 and customer_address_type2 tables are indicated. The type 2 table has added attributes including address_start_date, address_end_date and is_current which would capture customer addresses at the time orders were placed. ``` *** @@ -193,5 +219,7 @@ Consider, for example, concepts of labour, bias, LLM proliferation, moderating c ``` -Your thoughts... +I think there are two core issues in this article. The first issue is that datasets are at their very foundational level, work that has been done by humans. As technology advances at increasingly faster pace, it is equally difficult to identify those who contributed and built the very databases we have come to rely on. Likely, for the majority of people who access data (myself included), those who are responsible for the creation and maintenance of them, remain nameless and invisible. The major issue here is that, similar to academic research, citations and giving credit to others' work can be done poorly or incorrectly and so, in the same way as those who write may omit credit to those who inspired them, we access and manipulate data without a minor thought of where or who might have created it. As a result, those creators remain invisible despite their hard work. + +The second issue is the politics of the work itself. As Boykis indicates, "every single piece of decision-making...rests on a human being manually putting something together and making a decision". In saying this, it is clear that even the menial tasks of labelling images are layered with implicit bias, government influences and power dynamics relative to the data inputter. As a result, computers who train on these sorts of images, do so with images labelled with a flawed, subjective and biased lenses. Ultimately, the clear ethical implications I noted after reading this article is that data are not neutral but rather infused with the labour of human productivity and can also reflect the biases of its creators. ``` diff --git a/Assignments/Small Bookstore Logical Model.drawio.png b/Assignments/Small Bookstore Logical Model.drawio.png new file mode 100644 index 000000000..f4fa4ce75 Binary files /dev/null and b/Assignments/Small Bookstore Logical Model.drawio.png differ diff --git a/Assignments/Small Bookstore Logical Model.employee_shifts.png b/Assignments/Small Bookstore Logical Model.employee_shifts.png new file mode 100644 index 000000000..16463e006 Binary files /dev/null and b/Assignments/Small Bookstore Logical Model.employee_shifts.png differ diff --git a/Assignments/assignment-two.sql b/Assignments/assignment-two.sql new file mode 100644 index 000000000..4f426d6f1 --- /dev/null +++ b/Assignments/assignment-two.sql @@ -0,0 +1,252 @@ +BEGIN TRANSACTION; +CREATE TABLE IF NOT EXISTS "assignment2" ( + "field1" TEXT, + "field2" TEXT +); +-- /* ASSIGNMENT 2 */ +-- --Please write responses between the QUERY # and END QUERY blocks +-- /* SECTION 2 */ +-- -- COALESCE +-- /* 1. Our favourite manager wants a detailed long list of products','but is afraid of tables!'); +-- We tell them','no problem! We can produce a list with all of the appropriate details.'); +-- Using the following syntax you create our super cool and not at all needy manager a list: +-- SELECT +-- product_name || ''',''' || product_size|| '' ('' || product_qty_type || '')'''); +-- FROM product +-- But wait! The product table has some bad data (a few NULL values). +-- Find the NULLs and then using COALESCE','replace the NULL with a blank for the first column with'); +-- nulls','and ''unit'' for the second column with nulls.'); +-- **HINT**: keep the syntax the same','but edited the correct components with the string.'); +-- 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. */ +-- --QUERY 1 +SELECT + product_name || '', '' || COALESCE(product_size, '''') || '' ('' || COALESCE(product_qty_type, ''unit'') || '')'' +FROM product +; +-- --END QUERY +-- --Windowed Functions +-- /* 1. Write a query that selects from the customer_purchases table and numbers each customer’s +-- visits to the farmer’s market (labeling each market date with a different number). +-- Each customer’s first visit is labeled 1','second visit is labeled 2'); +-- You can either display all rows in the customer_purchases table','with the counter changing on'); +-- each new market date for each customer','or select only the unique market dates per customer'); +-- (without purchase details) and number those visits. +-- HINT: One of these approaches uses ROW_NUMBER() and one uses DENSE_RANK(). +-- Filter the visits to dates before April 29','2022. */'); +-- --QUERY 2 +SELECT + customer_id, + market_date, + product_id, + quantity, + DENSE_RANK() OVER (PARTITION BY customer_id ORDER BY market_date ASC) as visit_number +FROM customer_purchases +WHERE market_date < ''2022-04-29'' +; +-- --END QUERY +-- /* 2. Reverse the numbering of the query 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. +-- HINT: Do not use the previous visit dates filter. */ +-- --QUERY 3 +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 + WHERE market_date < ''2022-04-29'' +) +WHERE visit_number = 1 +; +-- --END QUERY +-- /* 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. +-- You can make this a running count by including an ORDER BY within the PARTITION BY if desired. +-- Filter the visits to dates before April 29','2022. */'); +-- --QUERY 4 +SELECT + customer_id, + market_date, + product_id, + quantity, + COUNT(*) OVER ( + PARTITION BY customer_id, product_id + ORDER BY market_date + ) as times_bought +FROM customer_purchases +WHERE market_date < ''2022-04-29'' +; +-- --END QUERY +-- -- String manipulations +-- /* 1. Some product names in the product table have descriptions like Jar or Organic. +-- These are separated from the product name with a hyphen. +-- Create a column using SUBSTR (and a couple of other commands) that captures these','but is otherwise NULL.'); +-- Remove any trailing or leading whitespaces. Don''t just use a case statement for each product! +-- | product_name | description | +-- |----------------------------|-------------| +-- | Habanero Peppers - Organic | Organic | +-- Hint: you might need to use INSTR(product_name','''-'') to find the hyphens. INSTR will help split the column. */'); +-- --QUERY 5 +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 +; +-- --END QUERY +-- /* 2. Filter the query to show any product_size value that contain a number with REGEXP. */ +-- --QUERY 6 +SELECT + product_name, + product_size, + CASE + WHEN INSTR(product_name, ''-'') > 0 + THEN TRIM(SUBSTR(product_name, INSTR(product_name, ''-'') + 1)) + ELSE NULL + END as description +FROM product +WHERE product_size REGEXP ''[0-9]'' +; +-- --END QUERY +-- -- UNION +-- /* 1. Using a UNION','write a query that displays the market dates with the highest and lowest total sales.'); +-- HINT: There are a possibly a few ways to do this query','but if you''re struggling'); +-- 1) Create a CTE/Temp Table to find sales values grouped dates; +-- 2) Create another CTE/Temp table with a rank windowed function on the previous query to create +-- best day and worst day; +-- 3) Query the second temp table twice','once for the best day'); +-- with a UNION binding them. */ +-- --QUERY 7 +SELECT * FROM ( + SELECT + market_date, + sum(quantity * cost_per_quantity) as total_sales, + ''Best Day'' as label + FROM customer_purchases + GROUP by market_date + ORDER by total_sales DESC + LIMIT 1 +) +UNION +SELECT * FROM ( + SELECT + market_date, + sum(quantity * cost_per_quantity) as total_sales, + ''Worst Day'' as label + FROM customer_purchases + GROUP by market_date + ORDER by total_sales ASC + LIMIT 1 +); + +-- --END QUERY +-- /* SECTION 3 */ +-- -- Cross Join +-- /*1. Suppose every vendor in the `vendor_inventory` table had 5 of each of their products to sell to **every** +-- customer on record. How much money would each vendor make per product? +-- Show this by vendor_name and product name','rather than using the IDs.'); +-- HINT: Be sure you select only relevant columns and rows. +-- Remember','CROSS JOIN will explode your table rows'); +-- Think a bit about the row counts: how many distinct vendors','product names are there (x)?'); +-- How many customers are there (y). +-- Before your final group by you should have the product of those two queries (x*y). */ +-- --QUERY 8 +SELECT + v.vendor_name, + p.product_name, + COUNT(DISTINCT c.customer_id) * 5 * vi.original_price AS total_revenue +FROM vendor_inventory vi +JOIN vendor v ON vi.vendor_id = v.vendor_id +JOIN product p ON vi.product_id = p.product_id +CROSS JOIN customer c +GROUP BY v.vendor_name, p.product_name, vi.original_price +; +-- --END QUERY +-- -- 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`. */ +-- --QUERY 9 +CREATE TABLE product_units AS +SELECT + product_id, + product_name, + product_size, + product_category_id, + product_qty_type, + CURRENT_TIMESTAMP as snapshot_timestamp +FROM product +WHERE product_qty_type = ''unit'' +; +-- --END QUERY +-- /*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). */ +-- --QUERY 10 +INSERT INTO product_units ( + product_id, + product_name, + product_size, + product_category_id, + product_qty_type, + snapshot_timestamp +) +VALUES ( + 24, + ''Apple Pie'', + ''10 inch'', + 3, + ''unit'', + CURRENT_TIMESTAMP +) +; +-- --END QUERY +-- -- DELETE +-- /* 1. Delete the older record for whatever product you added. +-- HINT: If you don''t specify a WHERE clause','you are going to have a bad time.*/'); +-- --QUERY 11 +DELETE FROM product_units +WHERE product_name = ''Apple Pie'' + AND snapshot_timestamp < ( + SELECT MAX(snapshot_timestamp) + FROM product_units + WHERE product_name = ''Apple Pie'' + ) + ; +-- --END QUERY +-- -- UPDATE +-- /* 1.We want to add the current_quantity to the product_units table. +-- First','add a new column'); +-- ALTER TABLE product_units +-- ADD current_quantity INT; +-- Then','using UPDATE'); +-- HINT: This one is pretty hard. +-- First','determine how to get the last quantity per product.'); +-- Second','coalesce null values to 0 (if you don''t have null values'); +-- Third','SET current_quantity = (...your select statement...)'); +-- 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. */'); +-- --QUERY 12 +UPDATE product_units +SET current_quantity = COALESCE(( + SELECT vi.quantity + FROM vendor_inventory vi + WHERE vi.product_id = product_units.product_id + ORDER BY vi.market_date DESC + LIMIT 1 +), 0) +; +-- +-- --END QUERY +COMMIT;