diff --git a/02_activities/assignments/DC_Cohort/Assignment1.md b/02_activities/assignments/DC_Cohort/Assignment1.md index f650c9752..af102b044 100644 --- a/02_activities/assignments/DC_Cohort/Assignment1.md +++ b/02_activities/assignments/DC_Cohort/Assignment1.md @@ -210,4 +210,5 @@ Consider, for example, concepts of fariness, inequality, social structures, marg ``` Your thoughts... +From a literary scholar’s perspective, the databases and data systems I encounter every day are never neutral containers of information. They are closer to narrative forms: they classify, arrange, and give legibility to the world according to particular assumptions about what counts as normal, valid, and valuable. As someone trained in literary analysis, I am especially attentive to how systems produce inclusion and exclusion through representation. A database does not simply record identity; it actively shapes which identities are recognizable. In this sense, data systems resemble realist narratives: they claim to describe the world as it is, while in fact producing a world ordered by specific ideological assumptions (as media scholar Wendy Chun and many others have argued). This insight applies more broadly to many data systems in everyday life. University portals, healthcare forms, banking apps, and social media platforms all embed value systems through the categories they require us to inhabit. They often privilege efficiency, standardization, traceability, and administrative control over ambiguity, fluidity, or lived complexity. Like literary genres, databases rely on conventions, but unlike novels, they often hide their constructedness and present their categories as self-evident truths. A recent example in China (this is my scholarly field) would be the health code systems used during the pandemic. These systems translated ideas such as “risk,” “safety,” and “mobility” into calculable data categories, making a person’s ability to move through public space dependent on digital verification. This shows that data systems often embed values such as state surveillance, risk management, and social control under the language of efficiency and public safety. ``` diff --git a/02_activities/assignments/DC_Cohort/Assignment2.md b/02_activities/assignments/DC_Cohort/Assignment2.md index be0eff3a5..665535c0e 100644 --- a/02_activities/assignments/DC_Cohort/Assignment2.md +++ b/02_activities/assignments/DC_Cohort/Assignment2.md @@ -50,6 +50,8 @@ There are several tools online you can use, I'd recommend [Draw.io](https://www. #### Prompt 2 We want to create employee shifts, splitting up the day into morning and evening. Add this to the ERD. + + #### 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? @@ -57,6 +59,9 @@ The store wants to keep customer addresses. Propose two architectures for the CU ``` Your answer... + +The first architecture overwrites the existing address whenever a customer changes address. In this design, the CUSTOMER_ADDRESS table stores only the customer’s current address, with columns such as customer_address_id, customer_id, street, city, province, postal_code, and country. Each customer has one current address record, and updates replace the old values. This is a Type 1 slowly changing dimension because historical address information is not preserved. The second architecture retains address history. In this version, CUSTOMER_ADDRESS includes columns such as customer_address_id, customer_id, street, city, province, postal_code, country, effective_start_date, effective_end_date, and is_current. When a customer changes address, the old record is closed by setting an end date and marking it as no longer current, and a new row is inserted for the new address. This is a Type 2 slowly changing dimension because it preserves historical changes over time. + ``` *** @@ -191,5 +196,6 @@ Consider, for example, concepts of labour, bias, LLM proliferation, moderating c ``` -Your thoughts... +I think one of the most important ethical issues raised by this story is the hidden labour that underlies supposedly autonomous AI systems. The author shows that the so-called neural networks and other machine learning systems do not emerge from pure computation alone; they depend on large amounts of human work, including data labelling, moderation, cleaning, and evaluation. This matters ethically because the public often imagines AI as automated and objective, while the actual systems are built on forms of labour that are frequently underpaid, invisible, and psychologically harmful. As far as I know, much labour is outsourced to Asian countries such as India and the Philippines, which is a kind of reiteration of colonialism. A second major issue is bias. If machine learning models are trained on human-produced data, then they also absorb human prejudices, exclusions, and inequalities. Bias is therefore not an accidental glitch added onto otherwise neutral technology. It is often embedded in the social worlds, institutions, and assumptions that produce the data in the first place. This means that AI can reproduce racial, gendered, cultural, and class-based inequalities while appearing to operate scientifically or impartially. The article also points to the ethical problem of content moderation. Many AI systems and online platforms rely on human moderators to identify violent, abusive, or disturbing material. This work can expose people to repeated psychological harm while remaining poorly recognized. More broadly, the story challenges the myth that advanced technologies are separate from society, which I totally agree as someone working on science fiction and technology studies. AI systems are shaped by economic incentives, labour structures, corporate priorities, and social hierarchies. Discussions of LLM proliferation make this especially urgent: as such systems spread rapidly into education, search, writing, and administration, their errors and biases can scale just as quickly. This creates risks of misinformation, unfair decision-making, and a growing dependence on systems that appear intelligent while still relying on extensive human intervention. + ``` diff --git a/02_activities/assignments/DC_Cohort/assignment1.sql b/02_activities/assignments/DC_Cohort/assignment1.sql index 2ec561e2a..4d774003c 100644 --- a/02_activities/assignments/DC_Cohort/assignment1.sql +++ b/02_activities/assignments/DC_Cohort/assignment1.sql @@ -6,7 +6,8 @@ --SELECT /* 1. Write a query that returns everything in the customer table. */ --QUERY 1 - +SELECT * +FROM customer; @@ -16,7 +17,10 @@ /* 2. Write a query that displays all of the columns and 10 rows from the customer table, sorted by customer_last_name, then customer_first_ name. */ --QUERY 2 - +SELECT * +FROM customer +ORDER BY customer_last_name, customer_first_name +LIMIT 10; @@ -27,7 +31,10 @@ sorted by customer_last_name, then customer_first_ name. */ /* 1. Write a query that returns all customer purchases of product IDs 4 and 9. Limit to 25 rows of output. */ --QUERY 3 - +SELECT * +FROM customer_purchases +WHERE product_id IN (4, 9) +LIMIT 25; @@ -42,7 +49,11 @@ filtered by customer IDs between 8 and 10 (inclusive) using either: Limit to 25 rows of output. */ --QUERY 4 - +SELECT *, + quantity * cost_to_customer_per_qty AS price +FROM customer_purchases +WHERE customer_id BETWEEN 8 AND 10 +LIMIT 25; @@ -55,7 +66,13 @@ Using the product table, write a query that outputs the product_id and product_n columns and add a column called prod_qty_type_condensed that displays the word “unit” if the product_qty_type is “unit,” and otherwise displays the word “bulk.” */ --QUERY 5 - +SELECT product_id, + product_name, + CASE + WHEN product_qty_type = 'unit' THEN 'unit' + ELSE 'bulk' + END AS prod_qty_type_condensed +FROM product; @@ -66,7 +83,17 @@ if the product_qty_type is “unit,” and otherwise displays the word “bulk. add a column to the previous query called pepper_flag that outputs a 1 if the product_name contains the word “pepper” (regardless of capitalization), and otherwise outputs 0. */ --QUERY 6 - +SELECT product_id, + product_name, + CASE + WHEN product_qty_type = 'unit' THEN 'unit' + ELSE 'bulk' + END AS prod_qty_type_condensed, + CASE + WHEN LOWER(product_name) LIKE '%pepper%' THEN 1 + ELSE 0 + END AS pepper_flag +FROM product; @@ -78,7 +105,13 @@ contains the word “pepper” (regardless of capitalization), and otherwise out vendor_id field they both have in common, and sorts the result by market_date, then vendor_name. Limit to 24 rows of output. */ --QUERY 7 - +SELECT v.*, + vba.* +FROM vendor AS v +INNER JOIN vendor_booth_assignments AS vba + ON v.vendor_id = vba.vendor_id +ORDER BY vba.market_date, v.vendor_name +LIMIT 24; @@ -92,7 +125,10 @@ Limit to 24 rows of output. */ /* 1. Write a query that determines how many times each vendor has rented a booth at the farmer’s market by counting the vendor booth assignments per vendor_id. */ --QUERY 8 - +SELECT vendor_id, + COUNT(*) AS booth_rental_count +FROM vendor_booth_assignments +GROUP BY vendor_id; @@ -105,7 +141,16 @@ of customers for them to give stickers to, sorted by last name, then first name. HINT: This query requires you to join two tables, use an aggregate function, and use the HAVING keyword. */ --QUERY 9 - +SELECT c.customer_id, + c.customer_first_name, + c.customer_last_name, + SUM(cp.quantity * cp.cost_to_customer_per_qty) AS total_spent +FROM customer AS c +INNER JOIN customer_purchases AS cp + ON c.customer_id = cp.customer_id +GROUP BY c.customer_id, c.customer_first_name, c.customer_last_name +HAVING SUM(cp.quantity * cp.cost_to_customer_per_qty) > 2000 +ORDER BY c.customer_last_name, c.customer_first_name; @@ -124,7 +169,16 @@ When inserting the new vendor, you need to appropriately align the columns to be VALUES(col1,col2,col3,col4,col5) */ --QUERY 10 +DROP TABLE IF EXISTS temp.new_vendor; +CREATE TEMP TABLE new_vendor AS +SELECT * +FROM vendor; + +INSERT INTO temp.new_vendor + (vendor_id, vendor_name, vendor_type, vendor_owner_first_name, vendor_owner_last_name) +VALUES + (10, 'Thomass Superfood Store', 'Fresh Focused', 'Thomas', 'Rosenthal'); @@ -138,7 +192,11 @@ HINT: you might need to search for strfrtime modifers sqlite on the web to know and year are! Limit to 25 rows of output. */ --QUERY 11 - +SELECT customer_id, + STRFTIME('%m', market_date) AS month, + STRFTIME('%Y', market_date) AS year +FROM customer_purchases +LIMIT 25; @@ -152,7 +210,12 @@ HINTS: you will need to AGGREGATE, GROUP BY, and filter... but remember, STRFTIME returns a STRING for your WHERE statement... AND be sure you remove the LIMIT from the previous query before aggregating!! */ --QUERY 12 - +SELECT customer_id, + SUM(quantity * cost_to_customer_per_qty) AS total_spent_april_2022 +FROM customer_purchases +WHERE STRFTIME('%m', market_date) = '04' + AND STRFTIME('%Y', market_date) = '2022' +GROUP BY customer_id; diff --git a/02_activities/assignments/DC_Cohort/assignment2.sql b/02_activities/assignments/DC_Cohort/assignment2.sql index 4079c18ae..fdabafa4c 100644 --- a/02_activities/assignments/DC_Cohort/assignment2.sql +++ b/02_activities/assignments/DC_Cohort/assignment2.sql @@ -23,7 +23,9 @@ Edit the appropriate columns -- you're making two edits -- and the NULL rows wil 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 @@ -41,7 +43,18 @@ 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, + DENSE_RANK() OVER ( + PARTITION BY customer_id + ORDER BY market_date + ) AS visit_number +FROM ( + SELECT DISTINCT customer_id, market_date + FROM customer_purchases + WHERE market_date < '2022-04-29' +); --END QUERY @@ -53,7 +66,21 @@ only the customer’s most recent visit. HINT: Do not use the previous visit dates filter. */ --QUERY 3 - +SELECT * +FROM ( + SELECT + customer_id, + market_date, + DENSE_RANK() OVER ( + PARTITION BY customer_id + ORDER BY market_date DESC + ) AS recent_visit_number + FROM ( + SELECT DISTINCT customer_id, market_date + FROM customer_purchases + ) +) ranked_visits +WHERE recent_visit_number = 1; --END QUERY @@ -66,7 +93,16 @@ You can make this a running count by including an ORDER BY within the PARTITION 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 + ) AS times_customer_purchased_product +FROM customer_purchases +WHERE market_date < '2022-04-29'; --END QUERY @@ -85,7 +121,14 @@ 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. */ --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 @@ -94,6 +137,9 @@ Hint: you might need to use INSTR(product_name,'-') to find the hyphens. INSTR w /* 2. Filter the query to show any product_size value that contain a number with REGEXP. */ --QUERY 6 +SELECT * +FROM product +WHERE product_size REGEXP '[0-9]'; @@ -111,7 +157,36 @@ HINT: There are a possibly a few ways to do this query, but if you're struggling with a UNION binding them. */ --QUERY 7 - +WITH sales_by_date AS ( + SELECT + market_date, + SUM(quantity * cost_to_customer_per_qty) AS total_sales + FROM customer_purchases + GROUP BY market_date +), +ranked_sales AS ( + SELECT + market_date, + total_sales, + RANK() OVER (ORDER BY total_sales DESC) AS highest_rank, + RANK() OVER (ORDER BY total_sales ASC) AS lowest_rank + FROM sales_by_date +) +SELECT + market_date, + total_sales, + 'highest total sales' AS sales_label +FROM ranked_sales +WHERE highest_rank = 1 + +UNION + +SELECT + market_date, + total_sales, + 'lowest total sales' AS sales_label +FROM ranked_sales +WHERE lowest_rank = 1; --END QUERY @@ -132,7 +207,32 @@ How many customers are there (y). Before your final group by you should have the product of those two queries (x*y). */ --QUERY 8 - +WITH vendor_products AS ( + SELECT DISTINCT + vi.vendor_id, + v.vendor_name, + vi.product_id, + p.product_name, + vi.original_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 +) +SELECT + vp.vendor_name, + vp.product_name, + COUNT(c.customer_id) * 5 * vp.original_price AS potential_revenue +FROM vendor_products vp +CROSS JOIN customer c +GROUP BY + vp.vendor_name, + vp.product_name, + vp.original_price +ORDER BY + vp.vendor_name, + vp.product_name; --END QUERY @@ -145,7 +245,14 @@ It should use all of the columns from the product table, as well as a new column Name the timestamp column `snapshot_timestamp`. */ --QUERY 9 +DROP TABLE IF EXISTS product_units; +CREATE TABLE product_units AS +SELECT + *, + CURRENT_TIMESTAMP AS snapshot_timestamp +FROM product +WHERE product_qty_type = 'unit'; --END QUERY @@ -155,8 +262,13 @@ Name the timestamp column `snapshot_timestamp`. */ This can be any product you desire (e.g. add another record for Apple Pie). */ --QUERY 10 - - +INSERT INTO product_units +SELECT + *, + CURRENT_TIMESTAMP AS snapshot_timestamp +FROM product +WHERE product_name = 'Apple Pie' +LIMIT 1; --END QUERY @@ -167,6 +279,13 @@ This can be any product you desire (e.g. add another record for Apple Pie). */ 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 MIN(snapshot_timestamp) + FROM product_units + WHERE product_name = 'Apple Pie' + ); @@ -191,6 +310,18 @@ Finally, make sure you have a WHERE statement to update the right row, When you have all of these components, you can run the update statement. */ --QUERY 12 +ALTER TABLE product_units +ADD current_quantity INT; + +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) +WHERE product_units.product_id IS NOT NULL; diff --git a/02_activities/assignments/DC_Cohort/erd_merged_assignment_2.PNG b/02_activities/assignments/DC_Cohort/erd_merged_assignment_2.PNG new file mode 100644 index 000000000..fe6816307 Binary files /dev/null and b/02_activities/assignments/DC_Cohort/erd_merged_assignment_2.PNG differ diff --git a/02_activities/assignments/DC_Cohort/model.jpg b/02_activities/assignments/DC_Cohort/model.jpg new file mode 100644 index 000000000..198ae8201 Binary files /dev/null and b/02_activities/assignments/DC_Cohort/model.jpg differ