From 2e1651ab924ea74d53aac5f0b91238374305f472 Mon Sep 17 00:00:00 2001 From: yarishukla Date: Sun, 9 Aug 2026 15:12:23 -0400 Subject: [PATCH 1/2] complete assignment 2 --- .../assignments/DC_Cohort/Assignment2.md | 416 ++++++++++++++---- .../assignments/DC_Cohort/prompt 1.drawio.png | Bin 0 -> 73114 bytes .../assignments/DC_Cohort/prompt_2.drawio | 90 ++++ 3 files changed, 414 insertions(+), 92 deletions(-) create mode 100644 02_activities/assignments/DC_Cohort/prompt 1.drawio.png create mode 100644 02_activities/assignments/DC_Cohort/prompt_2.drawio diff --git a/02_activities/assignments/DC_Cohort/Assignment2.md b/02_activities/assignments/DC_Cohort/Assignment2.md index be0eff3a5..28be1dc08 100644 --- a/02_activities/assignments/DC_Cohort/Assignment2.md +++ b/02_activities/assignments/DC_Cohort/Assignment2.md @@ -1,195 +1,427 @@ # DC Assignment 2: Design a Logical Model and Advanced SQL -🚨 **Please review our [Assignment Submission Guide](https://github.com/UofT-DSI/onboarding/blob/main/onboarding_documents/submissions.md)** 🚨 for detailed instructions on how to format, branch, and submit your work. Following these guidelines is crucial for your submissions to be evaluated correctly. +🚨 Please review our [Assignment Submission Guide](https://github.com/UofT-DSI/onboarding/blob/main/onboarding_documents/submissions.md) 🚨 for detailed instructions on how to format, branch, and submit your work. Following these guidelines is crucial for your submissions to be evaluated correctly. #### Submission Parameters: + * Submission Due Date: `April 07, 2026` * Weight: 70% of total grade * The branch name for your repo should be: `assignment-two` * What to submit for this assignment: - * This markdown (Assignment2.md) with written responses in Section 1 and 4 - * Two Entity-Relationship Diagrams (preferably in a pdf, jpeg, png format). - * One .sql file + + * This markdown (Assignment2.md) with written responses in Section 1 and 4 + * Two Entity-Relationship Diagrams (preferably in a pdf, jpeg, png format). + * One .sql file * What the pull request link should look like for this assignment: `https://github.com//sql/pulls/` - * Open a private window in your browser. Copy and paste the link to your pull request into the address bar. Make sure you can see your pull request properly. This helps the technical facilitator and learning support staff review your submission easily. + + * Open a private window in your browser. Copy and paste the link to your pull request into the address bar. Make sure you can see your pull request properly. This helps the technical facilitator and learning support staff review your submission easily. Checklist: -- [ ] Create a branch called `assignment-two`. -- [ ] Ensure that the repository is public. -- [ ] Review [the PR description guidelines](https://github.com/UofT-DSI/onboarding/blob/main/onboarding_documents/submissions.md#guidelines-for-pull-request-descriptions) and adhere to them. -- [ ] Verify that the link is accessible in a private browser window. -If you encounter any difficulties or have questions, please don't hesitate to reach out to our team via our Slack. Our Technical Facilitators and Learning Support staff are here to help you navigate any challenges. +* [ ] Create a branch called `assignment-two`. +* [ ] Ensure that the repository is public. +* [ ] Review the PR description guidelines and adhere to them. +* [ ] Verify that the link is accessible in a private browser window. + +If you encounter any difficulties or have questions, please don't hesitate to reach out to our team via our Slack. Our Technical Facilitators and Learning Support staff are here to help you navigate this assignment. -*** +--- ## Section 1: -You can start this section following *session 1*, but you may want to wait until you feel comfortable wtih basic SQL query writing. + +You can start this section following session 1, but you may want to wait until you feel comfortable with basic SQL query writing. Steps to complete this part of the assignment: -- Design a logical data model -- Duplicate the logical data model and add another table to it following the instructions -- Write, within this markdown file, an answer to Prompt 3 +* Design a logical data model +* Duplicate the logical data model and add another table to it following the instructions +* Write, within this markdown file, an answer to Prompt 3 -### Design a Logical Model +### Design a Logical Model #### Prompt 1 + Design a logical model for a small bookstore. 📚 -At the minimum it should have employee, order, sales, customer, and book entities (tables). Determine sensible column and table design based on what you know about these concepts. Keep it simple, but work out sensible relationships to keep tables reasonably sized. +At the minimum it should have employee, order, sales, customer, and book entities (tables). Determine sensible column and table design based on what you know about these concepts. Keep it simple, but work out sensible relationships to keep tables reasonably sized. -Additionally, include a date table. -A date table (also called a calendar table) is a permanent table containing a list of dates and various components of those dates. Some theory, tips, and commentary can be found [here](https://www.sqlshack.com/designing-a-calendar-table/), [here](https://www.mssqltips.com/sqlservertip/4054/creating-a-date-dimension-or-calendar-table-in-sql-server/) and [here](https://sqlgeekspro.com/creating-calendar-table-sql-server/). -Remember, you don't actually need to run any of the queries in these articles, but instead understand *why* date tables in SQL make sense, and how to situate them within your logical models. +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/). +A date table (also called a calendar table) is a permanent table containing a list of dates and various components of those dates. -**HINT:** You do not need to create any data for this prompt. This is a conceptual model only. +ERD: See the submitted ERD image for the logical model. #### Prompt 2 + We want to create employee shifts, splitting up the day into morning and evening. Add this to the ERD. +ERD: See the submitted ERD image for the updated logical model with employee shifts. + #### 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. +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? -``` -Your answer... -``` +There are two possible architectures for the CUSTOMER_ADDRESS table, depending on whether the bookstore wants to keep a history of address changes. + +Type 1 — Overwrite changes + +In a Type 1 design, the customer's existing address is updated when they provide a new address. The table could contain columns such as customer_address_id, customer_id, street, city, province, postal_code, and country. If a customer moves, their old address is replaced with the new address. This approach is simple and only keeps the customer's current address, but it does not preserve any history of previous addresses. -*** +Type 2 — Retain changes + +In a Type 2 design, a new record is created whenever a customer's address changes, while the previous address remains in the table. The table could contain customer_address_id, customer_id, street, city, province, postal_code, and country, as well as start_date, end_date, and is_current. When the customer moves, the previous record can be given an end date and marked as no longer current, while a new record is created with the new address and a new start date. This allows the bookstore to maintain a complete history of the customer's addresses. + +Therefore, Type 1 overwrites the existing address and does not retain historical changes, while Type 2 creates new records and retains the history of address changes. Type 2 is useful when the bookstore needs to know where a customer lived at a particular point in time, while Type 1 is sufficient when only the customer's current address is important. + +--- ## Section 2: -You can start this section following *session 4*. + +You can start this section following session 4. Steps to complete this part of the assignment: -- Open the assignment2.sql file in DB Browser for SQLite: - - from [Github](./02_activities/assignments/assignment2.sql) - - or, from your local forked repository -- Complete each question, by writing responses between the QUERY # and END QUERY blocks +* Open the assignment2.sql file in DB Browser for SQLite. +* Complete each question by writing responses between the QUERY and END QUERY blocks. ### Write SQL #### 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. + +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 + +```sql +-- QUERY + +SELECT + product_name || ', ' || + COALESCE(product_size, '') || ' (' || + COALESCE(product_qty_type, 'unit') || ')' +FROM product; + +-- END QUERY ``` -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. +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. +```sql +-- QUERY -
-
+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, etc. -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. +### 1. Number each customer's visits -**HINT**: One of these approaches uses ROW_NUMBER() and one uses DENSE_RANK(). +```sql +-- QUERY -Filter the visits to dates before April 29, 2022. +SELECT + customer_id, + market_date, + DENSE_RANK() OVER ( + PARTITION BY customer_id + ORDER BY market_date + ) AS visit_number +FROM customer_purchases +WHERE market_date < '2022-04-29'; -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. +-- END QUERY +``` + +### 2. Reverse the numbering and find the most recent visit + +```sql +-- QUERY + +SELECT * +FROM ( + SELECT + customer_id, + market_date, + DENSE_RANK() OVER ( + PARTITION BY customer_id + ORDER BY market_date DESC + ) AS visit_number + FROM customer_purchases +) +WHERE visit_number = 1; + +-- END QUERY +``` + +### 3. Count how many times each customer purchased each product -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. +```sql +-- QUERY -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. +SELECT + customer_id, + product_id, + market_date, + COUNT(*) OVER ( + PARTITION BY customer_id, product_id + ) AS purchase_count +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 | +### 1. Extract the product description + +```sql +-- QUERY + +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 product sizes containing a number -**HINT**: you might need to use INSTR(product_name,'-') to find the hyphens. INSTR will help split the column. +```sql +-- QUERY -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]'; -
-
+-- 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, try the following: 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, once for the worst day, with a UNION binding them. +### 1. Market dates with the highest and lowest total sales + +```sql +-- QUERY + +WITH daily_sales AS ( + SELECT + market_date, + SUM(quantity * cost_per_quantity) 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 best_day, + RANK() OVER ( + ORDER BY total_sales ASC + ) AS worst_day + FROM daily_sales +) +SELECT + market_date, + total_sales +FROM ranked_sales +WHERE best_day = 1 + +UNION + +SELECT + market_date, + total_sales +FROM ranked_sales +WHERE worst_day = 1; + +-- END QUERY +``` -*** +--- ## Section 3: -You can start this section following *session 5*. + +You can start this section following session 5. Steps to complete this part of the assignment: -- Open the assignment2.sql file in DB Browser for SQLite: - - from [Github](./02_activities/assignments/assignment2.sql) - - or, from your local forked repository -- Complete each question, by writing responses between the QUERY # and END QUERY blocks + +* Open the assignment2.sql file in DB Browser for SQLite. +* Complete each question by writing responses between the QUERY and END QUERY blocks. ### Write SQL #### 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, so CROSS JOIN should likely be a subquery. 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). - -
-
+### 1. Calculate vendor earnings per product + +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. + +```sql +-- QUERY + +SELECT + v.vendor_name, + p.product_name, + SUM(5 * vi.cost_per_quantity) AS total_sales +FROM ( + SELECT DISTINCT + vendor_id, + product_id, + cost_per_quantity + 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 ( + SELECT DISTINCT + customer_id + FROM customer +) c +GROUP BY + v.vendor_name, + p.product_name; + +-- 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`. -2. Using `INSERT`, add a new row to the product_unit table (with an updated timestamp). This can be any product you desire (e.g. add another record for Apple Pie). +### 1. Create the product_units table + +```sql +-- QUERY + +CREATE TABLE product_units AS +SELECT + *, + CURRENT_TIMESTAMP AS snapshot_timestamp +FROM product +WHERE product_qty_type = 'unit'; + +-- END QUERY +``` + +### 2. Insert another product record + +```sql +-- QUERY + +INSERT INTO product_units ( + product_id, + product_name, + product_size, + product_category_id, + product_qty_type, + snapshot_timestamp +) +SELECT + product_id, + product_name, + product_size, + product_category_id, + product_qty_type, + CURRENT_TIMESTAMP +FROM product +WHERE product_name = 'Apple Pie'; + +-- END QUERY +``` -
-
+#### DELETE -#### DELETE -1. Delete the older record for whatever product you added. +### 1. Delete the older Apple Pie record -**HINT**: If you don't specify a WHERE clause, [you are going to have a bad time](https://imgflip.com/i/8iq872). +```sql +-- QUERY -
-
+DELETE FROM product_units +WHERE product_name = 'Apple Pie' +AND snapshot_timestamp = ( + SELECT MIN(snapshot_timestamp) + FROM product_units + WHERE product_name = 'Apple Pie' +); -#### UPDATE -1. We want to add the current_quantity to the product_units table. First, add a new column, `current_quantity` to the table using the following syntax. +-- END QUERY ``` + +#### UPDATE + +### 1. Add the current_quantity column + +```sql +-- QUERY + ALTER TABLE product_units ADD current_quantity INT; + +-- END QUERY ``` -Then, using `UPDATE`, change the current_quantity equal to the **last** `quantity` value from the vendor_inventory details. +### 2. Update current_quantity using the latest inventory quantity + +```sql +-- QUERY + +UPDATE product_units +SET current_quantity = ( + SELECT COALESCE(vi.quantity, 0) + FROM vendor_inventory vi + WHERE vi.product_id = product_units.product_id + ORDER BY vi.market_date DESC + LIMIT 1 +) +WHERE product_units.product_id IS NOT NULL; -**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, figure out how to rearrange your query so you do.) Third, `SET current_quantity = (...your select statement...)`, remembering that WHERE can only accommodate one column. 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. -*** +-- END QUERY +``` + +--- ## Section 4: + You can start this section anytime. Steps to complete this part of the assignment: -- Read the article -- Write, within this markdown file, between 250 and 1000 words. No additional citations/sources are required. + +* Read the article +* Write, within this markdown file, between 250 and 1000 words. No additional citations/sources are required. ### Ethics -Read: Boykis, V. (2019, October 16). _Neural nets are just people all the way down._ Normcore Tech.
- https://vicki.substack.com/p/neural-nets-are-just-people-all-the +Read: Boykis, V. (2019, October 16). Neural nets are just people all the way down. Normcore Tech. -**What are some of the ethical issues important to this story?** +What are some of the ethical issues important to this story? -Consider, for example, concepts of labour, bias, LLM proliferation, moderating content, intersection of technology and society, ect. +The article “Neural nets are just people all the way down” shows that machine learning systems are not completely independent or objective. Even though we often think of AI as something that is powered mainly by computers and algorithms, the article explains that humans play an important role in creating the data used to train these systems. People manually label images, organize categories, and make decisions about how information should be classified. These human decisions can introduce bias into the data and, as a result, into the machine learning system. +One important ethical issue is that biased or inappropriate training data can lead to unfair outcomes. For example, the article discusses ImageNet and how some categories used to label people were considered offensive or sensitive. If an AI system learns from these labels, it may reproduce the same biases when making predictions about new people. This demonstrates that even if an algorithm is technically working as designed, the results can still be harmful if the data or categories used to train it are problematic. -``` -Your thoughts... -``` +Another important issue is the human labour involved in creating AI systems. People are needed to collect, clean, label, and moderate data before it can be used to train machine learning models. This work can be repetitive and sometimes involve exposure to disturbing or inappropriate content. It is important to recognize this labour and consider whether workers are being treated fairly and provided with appropriate working conditions. + +I think this means that people who create and use machine learning systems have a responsibility to think carefully about the data they use. They should check training data for bias, remove harmful or inappropriate categories where appropriate, and consider how different groups of people could be affected by the system. It is also important to have diverse people involved in designing and reviewing these systems because different perspectives can help identify problems that one group might overlook. + +Overall, the article changed the way I think about AI because it shows that technology is not automatically neutral just because a computer is making the decision. Human choices exist throughout the process, from collecting and labeling data to designing the model and deciding how its results are used. Therefore, building responsible AI requires not only good technology but also careful human judgment, fairness, transparency, and accountability. diff --git a/02_activities/assignments/DC_Cohort/prompt 1.drawio.png b/02_activities/assignments/DC_Cohort/prompt 1.drawio.png new file mode 100644 index 0000000000000000000000000000000000000000..109312228df2e8ffd19951fced34527356f62708 GIT binary patch literal 73114 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 08440b991d76bc80ccb4e85a1684cd0c00bd4940 Mon Sep 17 00:00:00 2001 From: yarishukla Date: Sun, 9 Aug 2026 15:49:47 -0400 Subject: [PATCH 2/2] add assignment 2 SQL responses --- .../assignments/DC_Cohort/Assignment2.md | 411 +++++------------- .../assignments/DC_Cohort/assignment2.sql | 378 ++++++++++------ 2 files changed, 353 insertions(+), 436 deletions(-) diff --git a/02_activities/assignments/DC_Cohort/Assignment2.md b/02_activities/assignments/DC_Cohort/Assignment2.md index 28be1dc08..526158c62 100644 --- a/02_activities/assignments/DC_Cohort/Assignment2.md +++ b/02_activities/assignments/DC_Cohort/Assignment2.md @@ -1,427 +1,214 @@ # DC Assignment 2: Design a Logical Model and Advanced SQL -🚨 Please review our [Assignment Submission Guide](https://github.com/UofT-DSI/onboarding/blob/main/onboarding_documents/submissions.md) 🚨 for detailed instructions on how to format, branch, and submit your work. Following these guidelines is crucial for your submissions to be evaluated correctly. +🚨 **Please review our [Assignment Submission Guide](https://github.com/UofT-DSI/onboarding/blob/main/onboarding_documents/submissions.md)** 🚨 for detailed instructions on how to format, branch, and submit your work. Following these guidelines is crucial for your submissions to be evaluated correctly. #### Submission Parameters: - * Submission Due Date: `April 07, 2026` * Weight: 70% of total grade * The branch name for your repo should be: `assignment-two` * What to submit for this assignment: - - * This markdown (Assignment2.md) with written responses in Section 1 and 4 - * Two Entity-Relationship Diagrams (preferably in a pdf, jpeg, png format). - * One .sql file + * This markdown (Assignment2.md) with written responses in Section 1 and 4 + * Two Entity-Relationship Diagrams (preferably in a pdf, jpeg, png format). + * One .sql file * What the pull request link should look like for this assignment: `https://github.com//sql/pulls/` - - * Open a private window in your browser. Copy and paste the link to your pull request into the address bar. Make sure you can see your pull request properly. This helps the technical facilitator and learning support staff review your submission easily. + * Open a private window in your browser. Copy and paste the link to your pull request into the address bar. Make sure you can see your pull request properly. This helps the technical facilitator and learning support staff review your submission easily. Checklist: +- [ ] Create a branch called `assignment-two`. +- [ ] Ensure that the repository is public. +- [ ] Review [the PR description guidelines](https://github.com/UofT-DSI/onboarding/blob/main/onboarding_documents/submissions.md#guidelines-for-pull-request-descriptions) and adhere to them. +- [ ] Verify that the link is accessible in a private browser window. -* [ ] Create a branch called `assignment-two`. -* [ ] Ensure that the repository is public. -* [ ] Review the PR description guidelines and adhere to them. -* [ ] Verify that the link is accessible in a private browser window. - -If you encounter any difficulties or have questions, please don't hesitate to reach out to our team via our Slack. Our Technical Facilitators and Learning Support staff are here to help you navigate this assignment. +If you encounter any difficulties or have questions, please don't hesitate to reach out to our team via our Slack. Our Technical Facilitators and Learning Support staff are here to help you navigate any challenges. ---- +*** ## Section 1: - -You can start this section following session 1, but you may want to wait until you feel comfortable with basic SQL query writing. +You can start this section following *session 1*, but you may want to wait until you feel comfortable wtih basic SQL query writing. Steps to complete this part of the assignment: +- Design a logical data model +- Duplicate the logical data model and add another table to it following the instructions +- Write, within this markdown file, an answer to Prompt 3 -* Design a logical data model -* Duplicate the logical data model and add another table to it following the instructions -* Write, within this markdown file, an answer to Prompt 3 -### Design a Logical Model +### Design a Logical Model #### Prompt 1 - Design a logical model for a small bookstore. 📚 -At the minimum it should have employee, order, sales, customer, and book entities (tables). Determine sensible column and table design based on what you know about these concepts. Keep it simple, but work out sensible relationships to keep tables reasonably sized. +At the minimum it should have employee, order, sales, customer, and book entities (tables). Determine sensible column and table design based on what you know about these concepts. Keep it simple, but work out sensible relationships to keep tables reasonably sized. -Additionally, include a date table. +Additionally, include a date table. +A date table (also called a calendar table) is a permanent table containing a list of dates and various components of those dates. Some theory, tips, and commentary can be found [here](https://www.sqlshack.com/designing-a-calendar-table/), [here](https://www.mssqltips.com/sqlservertip/4054/creating-a-date-dimension-or-calendar-table-in-sql-server/) and [here](https://sqlgeekspro.com/creating-calendar-table-sql-server/). +Remember, you don't actually need to run any of the queries in these articles, but instead understand *why* date tables in SQL make sense, and how to situate them within your logical models. -A date table (also called a calendar table) is a permanent table containing a list of dates and various components of those dates. +There are several tools online you can use, I'd recommend [Draw.io](https://www.drawio.com/) or [LucidChart](https://www.lucidchart.com/pages/). -ERD: See the submitted ERD image for the logical model. +**HINT:** You do not need to create any data for this prompt. This is a conceptual model only. #### Prompt 2 - We want to create employee shifts, splitting up the day into morning and evening. Add this to the ERD. -ERD: See the submitted ERD image for the updated logical model with employee shifts. - #### 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? -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. -There are two possible architectures for the CUSTOMER_ADDRESS table, depending on whether the bookstore wants to keep a history of address changes. +``` +There are two possible architectures for the `CUSTOMER_ADDRESS` table, depending on whether the bookstore wants to keep a history of address changes. Type 1 — Overwrite changes -In a Type 1 design, the customer's existing address is updated when they provide a new address. The table could contain columns such as customer_address_id, customer_id, street, city, province, postal_code, and country. If a customer moves, their old address is replaced with the new address. This approach is simple and only keeps the customer's current address, but it does not preserve any history of previous addresses. +In a Type 1 design, the customer's existing address is updated when they provide a new address. The table could contain columns such as `customer_address_id`, `customer_id`, `street`, `city`, `province`, `postal_code`, and `country`. If a customer moves, their old address is replaced with the new address. This approach is simple and only keeps the customer's current address, but it does not preserve any history of previous addresses. Type 2 — Retain changes -In a Type 2 design, a new record is created whenever a customer's address changes, while the previous address remains in the table. The table could contain customer_address_id, customer_id, street, city, province, postal_code, and country, as well as start_date, end_date, and is_current. When the customer moves, the previous record can be given an end date and marked as no longer current, while a new record is created with the new address and a new start date. This allows the bookstore to maintain a complete history of the customer's addresses. +In a Type 2 design, a new record is created whenever a customer's address changes, while the previous address remains in the table. The table could contain `customer_address_id`, `customer_id`, `street`, `city`, `province`, `postal_code`, `country`, `start_date`, `end_date`, and `is_current`. When the customer moves, the previous record can be given an end date and marked as no longer current, while a new record is created with the new address and a new start date. This allows the bookstore to maintain a complete history of the customer's addresses. Therefore, Type 1 overwrites the existing address and does not retain historical changes, while Type 2 creates new records and retains the history of address changes. Type 2 is useful when the bookstore needs to know where a customer lived at a particular point in time, while Type 1 is sufficient when only the customer's current address is important. ---- +``` + +*** ## Section 2: - -You can start this section following session 4. +You can start this section following *session 4*. Steps to complete this part of the assignment: +- Open the assignment2.sql file in DB Browser for SQLite: + - from [Github](./02_activities/assignments/assignment2.sql) + - or, from your local forked repository +- Complete each question, by writing responses between the QUERY # and END QUERY blocks -* Open the assignment2.sql file in DB Browser for SQLite. -* Complete each question by writing responses between the QUERY and END QUERY blocks. ### Write SQL #### 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. +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: - -```sql --- QUERY - -SELECT - product_name || ', ' || - COALESCE(product_size, '') || ' (' || - COALESCE(product_qty_type, 'unit') || ')' -FROM product; - --- END QUERY +``` +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. - -```sql --- QUERY +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. -SELECT - product_name || ', ' || - COALESCE(product_size, '') || ' (' || - COALESCE(product_qty_type, 'unit') || ')' -FROM product; +**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. --- 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, etc. -### 1. Number each customer's visits - -```sql --- QUERY +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. -SELECT - customer_id, - market_date, - DENSE_RANK() OVER ( - PARTITION BY customer_id - ORDER BY market_date - ) AS visit_number -FROM customer_purchases -WHERE market_date < '2022-04-29'; +**HINT**: One of these approaches uses ROW_NUMBER() and one uses DENSE_RANK(). --- END QUERY -``` +Filter the visits to dates before April 29, 2022. -### 2. Reverse the numbering and find the most recent visit - -```sql --- QUERY - -SELECT * -FROM ( - SELECT - customer_id, - market_date, - DENSE_RANK() OVER ( - PARTITION BY customer_id - ORDER BY market_date DESC - ) AS visit_number - FROM customer_purchases -) -WHERE visit_number = 1; - --- 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. -### 3. Count how many times each customer purchased each product +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. -```sql --- QUERY +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. -SELECT - customer_id, - product_id, - market_date, - COUNT(*) OVER ( - PARTITION BY customer_id, product_id - ) AS purchase_count -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! -### 1. Extract the product description - -```sql --- QUERY - -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 -``` +| product_name | description | +|----------------------------|-------------| +| Habanero Peppers - Organic | Organic | -### 2. Filter product sizes containing a number +**HINT**: you might need to use INSTR(product_name,'-') to find the hyphens. INSTR will help split the column. -```sql --- QUERY +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]'; - --- END QUERY -``` +
-
#### UNION +1. Using a UNION, write a query that displays the market dates with the highest and lowest total sales. -### 1. Market dates with the highest and lowest total sales - -```sql --- QUERY - -WITH daily_sales AS ( - SELECT - market_date, - SUM(quantity * cost_per_quantity) 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 best_day, - RANK() OVER ( - ORDER BY total_sales ASC - ) AS worst_day - FROM daily_sales -) -SELECT - market_date, - total_sales -FROM ranked_sales -WHERE best_day = 1 - -UNION - -SELECT - market_date, - total_sales -FROM ranked_sales -WHERE worst_day = 1; - --- END QUERY -``` +**HINT**: There are a possibly a few ways to do this query, but if you're struggling, try the following: 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, once for the worst day, with a UNION binding them. ---- +*** ## Section 3: - -You can start this section following session 5. +You can start this section following *session 5*. Steps to complete this part of the assignment: - -* Open the assignment2.sql file in DB Browser for SQLite. -* Complete each question by writing responses between the QUERY and END QUERY blocks. +- Open the assignment2.sql file in DB Browser for SQLite: + - from [Github](./02_activities/assignments/assignment2.sql) + - or, from your local forked repository +- Complete each question, by writing responses between the QUERY # and END QUERY blocks ### Write SQL #### 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. -### 1. Calculate vendor earnings per product - -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. - -```sql --- QUERY - -SELECT - v.vendor_name, - p.product_name, - SUM(5 * vi.cost_per_quantity) AS total_sales -FROM ( - SELECT DISTINCT - vendor_id, - product_id, - cost_per_quantity - 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 ( - SELECT DISTINCT - customer_id - FROM customer -) c -GROUP BY - v.vendor_name, - p.product_name; - --- END QUERY -``` - -#### INSERT - -### 1. Create the product_units table +**HINT**: Be sure you select only relevant columns and rows. Remember, CROSS JOIN will explode your table rows, so CROSS JOIN should likely be a subquery. 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). -```sql --- QUERY - -CREATE TABLE product_units AS -SELECT - *, - CURRENT_TIMESTAMP AS snapshot_timestamp -FROM product -WHERE product_qty_type = 'unit'; +
-
--- END QUERY -``` - -### 2. Insert another product record - -```sql --- QUERY - -INSERT INTO product_units ( - product_id, - product_name, - product_size, - product_category_id, - product_qty_type, - snapshot_timestamp -) -SELECT - product_id, - product_name, - product_size, - product_category_id, - product_qty_type, - CURRENT_TIMESTAMP -FROM product -WHERE product_name = 'Apple Pie'; - --- 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`. -#### DELETE +2. Using `INSERT`, add a new row to the product_unit table (with an updated timestamp). This can be any product you desire (e.g. add another record for Apple Pie). -### 1. Delete the older Apple Pie record +
-
-```sql --- QUERY +#### DELETE +1. Delete the older record for whatever product you added. -DELETE FROM product_units -WHERE product_name = 'Apple Pie' -AND snapshot_timestamp = ( - SELECT MIN(snapshot_timestamp) - FROM product_units - WHERE product_name = 'Apple Pie' -); +**HINT**: If you don't specify a WHERE clause, [you are going to have a bad time](https://imgflip.com/i/8iq872). --- END QUERY -``` +
-
#### UPDATE - -### 1. Add the current_quantity column - -```sql --- QUERY - +1. We want to add the current_quantity to the product_units table. First, add a new column, `current_quantity` to the table using the following syntax. +``` ALTER TABLE product_units ADD current_quantity INT; - --- END QUERY ``` -### 2. Update current_quantity using the latest inventory quantity - -```sql --- QUERY +Then, using `UPDATE`, change the current_quantity equal to the **last** `quantity` value from the vendor_inventory details. -UPDATE product_units -SET current_quantity = ( - SELECT COALESCE(vi.quantity, 0) - FROM vendor_inventory vi - WHERE vi.product_id = product_units.product_id - ORDER BY vi.market_date DESC - LIMIT 1 -) -WHERE product_units.product_id IS NOT NULL; - --- END QUERY -``` - ---- +**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, figure out how to rearrange your query so you do.) Third, `SET current_quantity = (...your select statement...)`, remembering that WHERE can only accommodate one column. 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. +*** ## Section 4: - You can start this section anytime. Steps to complete this part of the assignment: - -* Read the article -* Write, within this markdown file, between 250 and 1000 words. No additional citations/sources are required. +- Read the article +- Write, within this markdown file, between 250 and 1000 words. No additional citations/sources are required. ### Ethics -Read: Boykis, V. (2019, October 16). Neural nets are just people all the way down. Normcore Tech. +Read: Boykis, V. (2019, October 16). _Neural nets are just people all the way down._ Normcore Tech.
+ https://vicki.substack.com/p/neural-nets-are-just-people-all-the + +**What are some of the ethical issues important to this story?** -What are some of the ethical issues important to this story? +Consider, for example, concepts of labour, bias, LLM proliferation, moderating content, intersection of technology and society, ect. + +``` The article “Neural nets are just people all the way down” shows that machine learning systems are not completely independent or objective. Even though we often think of AI as something that is powered mainly by computers and algorithms, the article explains that humans play an important role in creating the data used to train these systems. People manually label images, organize categories, and make decisions about how information should be classified. These human decisions can introduce bias into the data and, as a result, into the machine learning system. One important ethical issue is that biased or inappropriate training data can lead to unfair outcomes. For example, the article discusses ImageNet and how some categories used to label people were considered offensive or sensitive. If an AI system learns from these labels, it may reproduce the same biases when making predictions about new people. This demonstrates that even if an algorithm is technically working as designed, the results can still be harmful if the data or categories used to train it are problematic. -Another important issue is the human labour involved in creating AI systems. People are needed to collect, clean, label, and moderate data before it can be used to train machine learning models. This work can be repetitive and sometimes involve exposure to disturbing or inappropriate content. It is important to recognize this labour and consider whether workers are being treated fairly and provided with appropriate working conditions. +Another ethical issue is the labour involved in creating AI systems. Much of the work behind machine learning happens through people collecting, cleaning, labeling, and moderating data. This work can be hidden from the people who use AI systems, even though it is essential for making those systems function. This shows that AI is not simply created by computers; many human workers contribute to the development of these technologies. -I think this means that people who create and use machine learning systems have a responsibility to think carefully about the data they use. They should check training data for bias, remove harmful or inappropriate categories where appropriate, and consider how different groups of people could be affected by the system. It is also important to have diverse people involved in designing and reviewing these systems because different perspectives can help identify problems that one group might overlook. +I think this means that people who create and use machine learning systems have a responsibility to think carefully about the data they use. They should check training data for bias, remove harmful or inappropriate categories, and consider how different groups of people could be affected by the system. It is also important to have diverse people involved in designing and reviewing these systems because different perspectives can help identify problems that one group might overlook. -Overall, the article changed the way I think about AI because it shows that technology is not automatically neutral just because a computer is making the decision. Human choices exist throughout the process, from collecting and labeling data to designing the model and deciding how its results are used. Therefore, building responsible AI requires not only good technology but also careful human judgment, fairness, transparency, and accountability. +Overall, the article changed the way I think about AI because it shows that technology is not automatically neutral just because a computer is making the decision. Human choices exist throughout the process, from collecting and labeling data to designing the model and deciding how its results are used. Therefore, building responsible AI requires not only good technology but also careful human judgment, fairness, and accountability. +``` diff --git a/02_activities/assignments/DC_Cohort/assignment2.sql b/02_activities/assignments/DC_Cohort/assignment2.sql index 4079c18ae..7a8baba0d 100644 --- a/02_activities/assignments/DC_Cohort/assignment2.sql +++ b/02_activities/assignments/DC_Cohort/assignment2.sql @@ -1,180 +1,298 @@ /* ASSIGNMENT 2 */ ---Please write responses between the QUERY # and END QUERY blocks +/* 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. +/* 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 +SELECT product_name || ', ' || product_size|| ' (' || product_qty_type || ')' FROM product - -But wait! The product table has some bad data (a few NULL values). +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. +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. +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 +/* QUERY 1 */ +SELECT +COALESCE(product_name, '') || ', ' || +COALESCE(product_size, '') || ' (' || +COALESCE(product_qty_type, 'unit') || ')' +FROM product; +/* END QUERY */ ---END QUERY +/* Windowed Functions */ - ---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, etc. +/* 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, etc. 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(). +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 - - +/* QUERY 2 */ ---END QUERY +SELECT +customer_id, +market_date, +DENSE_RANK() OVER ( +PARTITION BY customer_id +ORDER BY market_date +) 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 +/* 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 +/* QUERY 3 */ +SELECT * +FROM ( +SELECT +customer_id, +market_date, +DENSE_RANK() OVER ( +PARTITION BY customer_id +ORDER BY market_date DESC +) AS visit_number +FROM customer_purchases +) +WHERE visit_number = 1; +/* END QUERY */ ---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. +/* 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 - - - - ---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 - - - - ---END QUERY +/* QUERY 4 */ + +SELECT +customer_id, +product_id, +market_date, +COUNT(*) OVER ( +PARTITION BY customer_id, product_id +) AS purchase_count +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! */ + +/* 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 - +/* QUERY 6 */ +SELECT +product_name, +product_size +FROM product +WHERE product_size REGEXP '[0-9]'; ---END QUERY +/* END QUERY */ +/* UNION */ --- 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, try the following: -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, once for the worst day, -with a UNION binding them. */ ---QUERY 7 - - - - ---END QUERY - - +HINT: There are a possibly a few ways to do this query, but if you're struggling, try the following: + +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, once for the worst day, + with a UNION binding them. */ + +/* QUERY 7 */ + +WITH daily_sales AS ( +SELECT +market_date, +SUM(quantity * cost_per_quantity) 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 best_day, +RANK() OVER ( +ORDER BY total_sales ASC +) AS worst_day +FROM daily_sales +) +SELECT +market_date, +total_sales +FROM ranked_sales +WHERE best_day = 1 + +UNION + +SELECT +market_date, +total_sales +FROM ranked_sales +WHERE worst_day = 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? +/* 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, so CROSS JOIN should likely be a subquery. +HINT: Be sure you select only relevant columns and rows. +Remember, CROSS JOIN will explode your table rows, so CROSS JOIN should likely be a subquery. 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 - - - - ---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 - - - - ---END QUERY +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, +SUM(5 * vi.cost_per_quantity) AS total_sales +FROM ( +SELECT DISTINCT +vendor_id, +product_id, +cost_per_quantity +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 ( +SELECT DISTINCT +customer_id +FROM customer +) c +GROUP BY +v.vendor_name, +p.product_name; + +/* 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 +*, +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). +/* 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 - - +/* QUERY 10 */ + +INSERT INTO product_units ( +product_id, +product_name, +product_size, +product_category_id, +product_qty_type, +snapshot_timestamp +) +SELECT +product_id, +product_name, +product_size, +product_category_id, +product_qty_type, +CURRENT_TIMESTAMP +FROM product +WHERE product_name = 'Apple Pie'; ---END QUERY - +/* END QUERY */ --- DELETE -/* 1. Delete the older record for whatever product you added. +/* DELETE */ -HINT: If you don't specify a WHERE clause, you are going to have a bad time.*/ ---QUERY 11 +/* 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 MIN(snapshot_timestamp) +FROM product_units +WHERE product_name = 'Apple Pie' +); ---END QUERY +/* END QUERY */ +/* UPDATE */ --- UPDATE -/* 1.We want to add the current_quantity to the product_units table. +/* 1. We want to add the current_quantity to the product_units table. First, add a new column, current_quantity to the table using the following syntax. ALTER TABLE product_units @@ -182,19 +300,31 @@ ADD current_quantity INT; Then, using UPDATE, change the current_quantity equal to the last quantity value from the vendor_inventory details. -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, figure out how to rearrange your query so you do.) -Third, SET current_quantity = (...your select statement...), remembering that WHERE can only accommodate one column. -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. +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, figure out how to rearrange your query so you do.) +Third, SET current_quantity = (...your select statement...), remembering that WHERE can only accommodate one column. +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 - +/* QUERY 12 */ +ALTER TABLE product_units +ADD current_quantity INT; ---END QUERY +/* END QUERY */ +/* QUERY 13 */ +UPDATE product_units +SET current_quantity = ( +SELECT COALESCE(vi.quantity, 0) +FROM vendor_inventory vi +WHERE vi.product_id = product_units.product_id +ORDER BY vi.market_date DESC +LIMIT 1 +) +WHERE product_units.product_id IS NOT NULL; +/* END QUERY */