From f4e2d617d6828d7ef923ccd88e9c1f3d6a109f1e Mon Sep 17 00:00:00 2001 From: Mareh Date: Wed, 1 Jul 2026 00:00:08 +0200 Subject: [PATCH] completed week 9 assignment sql queries and dictionary --- AI_ASSIST.md | 62 +++++++++++++++++++++++++++++++++---- assets/borough_count.png | Bin 0 -> 34436 bytes data_dictionary.md | 16 +++++----- schema_setup.sql | 13 +++++--- validation_queries.sql | 22 ++++++++++--- verification_results.sql | 65 ++++++++++++++++++++++++++++++++++++++- 6 files changed, 155 insertions(+), 23 deletions(-) create mode 100644 assets/borough_count.png diff --git a/AI_ASSIST.md b/AI_ASSIST.md index 07efd00..9af13ef 100644 --- a/AI_ASSIST.md +++ b/AI_ASSIST.md @@ -6,16 +6,66 @@ Document one session where you used an LLM to help with a query or a design deci ## The problem -TODO: What were you trying to solve? Paste the relevant SQL or schema fragment. +-- 3. Range validation: what are the min and max fare_amount? Are there negative values? +-- TODO: SELECT MIN(fare_amount), MAX(fare_amount), and a count of rows where fare_amount < 0. +select + MIN(rt.fare_amount), + MAX(rt.fare_amount), + COUNT(*) +where rt.fare_amount < 0, +from nyc_taxi.raw_trips rt; +Syntax error: ## The prompt - -TODO: What did you ask the AI? Include the context you provided. +why i got syntax error here and should i apply filter (where) ? ## The response +You got a syntax error because of how the SQL engine processes queries and where components must be placed. -TODO: What did it suggest? Did it work first try? +Here is exactly why your query failed and why using a **`FILTER`** clause is the best approach here: -## Reflection +--- + +### 1. Why you got a Syntax Error + +There are two structural mistakes in your original query: + +* **Placement of `FROM` and `WHERE`:** In SQL, the `FROM` clause must always come *before* the `WHERE` clause. You placed a comma after your `WHERE` condition and then wrote `FROM`, which completely confuses the database parser. +* **The comma inside `WHERE`:** You wrote `WHERE rt.fare_amount < 0,`. A `WHERE` clause cannot end with a comma. + +--- + +### 2. Should you apply a regular `WHERE` at the bottom? + +**No**, you shouldn't use a regular `WHERE` clause at the bottom of this query. + +If you put `WHERE rt.fare_amount < 0` at the end of the query, **the database will filter the entire table first**. This means your `MIN()` and `MAX()` functions would *only* look at the negative rows. + +* Your `MIN` would show the worst negative number. +* Your `MAX` would show the highest negative number (something close to `0`), **not** the true maximum fare paid by a real customer (which could be hundreds of dollars). -TODO: Did you understand *why* the suggestion worked, or did you accept it blindly? +--- + +### 3. The Solution: Use the `FILTER` clause + +To get the true minimum, the true maximum, and the count of bad rows all in a single pass, you must use the `FILTER` clause inside the `COUNT(*)` function. This acts as a mini-conditional statement just for that specific counter. + +Here is the correct way to write it: + +```sql +SELECT + MIN(rt.fare_amount) AS min_fare, + MAX(rt.fare_amount) AS max_fare, + COUNT(*) FILTER (WHERE rt.fare_amount < 0) AS negative_fares_count +FROM nyc_taxi.raw_trips rt; + +``` + +### Why this works perfectly: + +* `MIN(rt.fare_amount)` and `MAX(rt.fare_amount)` scan the **entire table** to find the absolute lowest and highest values. +* `COUNT(*) FILTER (WHERE rt.fare_amount < 0)` looks at every row but only increments the counter when the fare is negative. + +Run this updated query in your editor, and it will return a clean, one-row report without any errors! +## Reflection +Yes, I understand the response, and I noticed that the order of "where" before "from" is wrong, and also the comma, and using "filter" is better. diff --git a/assets/borough_count.png b/assets/borough_count.png new file mode 100644 index 0000000000000000000000000000000000000000..bea86f784188e13780a81cfe7a09f42324b29f7e GIT binary patch literal 34436 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 literal 0 HcmV?d00001 diff --git a/data_dictionary.md b/data_dictionary.md index 5e44612..00ffbb9 100644 --- a/data_dictionary.md +++ b/data_dictionary.md @@ -4,14 +4,14 @@ Document both views. State the grain in one sentence, identify the keys, and lis ## vw_fact_trips -- **Grain:** TODO (one sentence, e.g. "One row per ...") -- **Primary key:** TODO -- **Foreign keys:** TODO -- **Measures:** TODO (columns you would SUM or AVG) +- **Grain:** One row per individual valid taxi trip with a non-negative fare amount. +- **Primary key:** The raw data doesn't have a single primary key column, but we can use vendor_id and pickup_datetime together as a team (a composite key) to identify each unique row. +- **Foreign keys:** pickup_location_id, dropoff_location_id (both link to vw_dim_zones) +- **Measures:** fare_amount, tip_amount, total_amount,trip_distance ## vw_dim_zones -- **Grain:** TODO -- **Primary key:** TODO -- **Foreign keys:** TODO (or "none") -- **Measures:** TODO (or "none, descriptive attributes only") +- **Grain:** One row per unique taxi zone location ID +- **Primary key:** location_id +- **Foreign keys:** none +- **Measures:** none, "descriptive attributes only" diff --git a/schema_setup.sql b/schema_setup.sql index a7ae1ad..a91f381 100644 --- a/schema_setup.sql +++ b/schema_setup.sql @@ -5,7 +5,9 @@ -- TODO: complete the SELECT (location_id, zone, borough). CREATE OR REPLACE VIEW vw_dim_zones AS SELECT - -- TODO + rz.location_id, + rz.zone, + rz.borough FROM nyc_taxi.raw_zones; -- Fact: one row per taxi trip. @@ -15,12 +17,15 @@ FROM nyc_taxi.raw_zones; -- TODO: complete the SELECT and the WHERE. CREATE OR REPLACE VIEW vw_fact_trips AS SELECT - -- TODO + rt.*, + rt.pickup_datetime::TIMESTAMP AS pickup_datetime_clean FROM nyc_taxi.raw_trips --- TODO: WHERE fare_amount >= 0 -; +WHERE fare_amount >= 0; -- Join-readiness test (run after creating the views; it must run without error -- and return a count close to the vw_fact_trips row count): -- SELECT COUNT(*) FROM vw_fact_trips f -- JOIN vw_dim_zones d ON f.pickup_location_id = d.location_id; +SELECT COUNT(*) +FROM vw_fact_trips f +JOIN vw_dim_zones d ON f.pickup_location_id = d.location_id; \ No newline at end of file diff --git a/validation_queries.sql b/validation_queries.sql index 301b194..6f6934f 100644 --- a/validation_queries.sql +++ b/validation_queries.sql @@ -5,16 +5,30 @@ -- 1. Duplicate check: are there rows with the same vendor_id, pickup_datetime, dropoff_datetime? -- TODO: GROUP BY the three columns and keep only groups with HAVING COUNT(*) > 1. - - +select rt.vendor_id , rt.pickup_datetime , rt.dropoff_datetime , COUNT(*) AS copies +from nyc_taxi.raw_trips rt +group by rt.vendor_id , rt.pickup_datetime , rt.dropoff_datetime +having count(*)>1 +ORDER BY copies DESC; -- 2. Null integrity: how many rows have a NULL pickup_location_id or dropoff_location_id? -- TODO: count the NULLs (COUNT(*) FILTER (WHERE ... IS NULL) is handy for several columns at once). - +select + COUNT(*) filter(where rt.pickup_location_id is null) as null_pickup_location, + COUNT(*) filter(where rt.dropoff_location_id is null)as null_dropoff_location +from nyc_taxi.raw_trips rt; -- 3. Range validation: what are the min and max fare_amount? Are there negative values? -- TODO: SELECT MIN(fare_amount), MAX(fare_amount), and a count of rows where fare_amount < 0. - +select + MIN(rt.fare_amount) as min_fare_amount, + MAX(rt.fare_amount) as max_fare_amount, + COUNT(*) FILTER (WHERE rt.fare_amount < 0) AS negative_fares_count +from nyc_taxi.raw_trips rt; -- 4. Relationship check: which pickup_location_id values in nyc_taxi.raw_trips do NOT exist in nyc_taxi.raw_zones? -- TODO: LEFT JOIN nyc_taxi.raw_zones ... WHERE z.location_id IS NULL (or NOT EXISTS). -- Do NOT use NOT IN: a single NULL in the subquery hides every orphan. +select rt.pickup_location_id +from nyc_taxi.raw_trips rt +left join nyc_taxi.raw_zones rz on rt.pickup_location_id = rz.location_id +where rz.location_id is null; \ No newline at end of file diff --git a/verification_results.sql b/verification_results.sql index 68a8d26..527b4b0 100644 --- a/verification_results.sql +++ b/verification_results.sql @@ -6,17 +6,80 @@ -- What is the most common pickup/dropoff location combination? -- TODO -- (Take a screenshot of the per-borough counts and save it as assets/borough_count.png.) +SELECT COUNT(*) AS total_trips +FROM vw_fact_trips; + +SELECT + d.borough, + COUNT(*) AS trip_count +FROM vw_fact_trips f +JOIN vw_dim_zones d ON f.pickup_location_id = d.location_id +GROUP BY d.borough +ORDER BY trip_count DESC; + +SELECT + p.zone AS pickup_zone, + d.zone AS dropoff_zone, + COUNT(*) AS combination +FROM vw_fact_trips f +JOIN vw_dim_zones p ON f.pickup_location_id = p.location_id +JOIN vw_dim_zones d ON f.dropoff_location_id = d.location_id +GROUP BY p.zone, d.zone +ORDER BY combination DESC +LIMIT 1; -- 2. Revenue: which pickup zone (name, not ID) generated the highest total fare_amount? -- Which pickup zone collected the highest total fare_amount on any single day? -- TODO +SELECT + d.zone AS zone_name, + SUM(f.fare_amount) AS total_revenue +FROM vw_fact_trips f +INNER JOIN vw_dim_zones d + ON f.pickup_location_id = d.location_id +GROUP BY d.zone +ORDER BY total_revenue DESC +LIMIT 1; + +SELECT + d.zone AS zone_name, + DATE(f.pickup_datetime_clean) AS trip_date, + SUM(f.fare_amount) AS total_money +FROM vw_fact_trips f +JOIN vw_dim_zones d + ON f.pickup_location_id = d.location_id +GROUP BY d.zone, DATE(f.pickup_datetime_clean) +ORDER BY total_money DESC +LIMIT 1; -- 3. Geospatial: total number of trips and average trip_distance for each borough. -- TODO - +SELECT + d.borough, + COUNT(*) AS total_trips, + AVG(f.trip_distance) AS avg_distance +FROM vw_fact_trips f +JOIN vw_dim_zones d ON f.pickup_location_id = d.location_id +GROUP BY d.borough +ORDER BY total_trips DESC; -- 4. Time patterns: which day of the week had the highest total tip_amount? -- What hour of the day has the highest average tip? -- TODO +SELECT + EXTRACT(DOW FROM f.pickup_datetime_clean) AS day_number, + SUM(f.tip_amount) AS total_tips +FROM vw_fact_trips f +GROUP BY day_number +ORDER BY total_tips DESC +LIMIT 1; + +SELECT + EXTRACT(HOUR FROM f.pickup_datetime_clean) AS trip_hour, + ROUND(AVG(f.tip_amount)::numeric, 2) AS avg_tip +FROM vw_fact_trips f +GROUP BY trip_hour +ORDER BY avg_tip DESC +LIMIT 1;