diff --git a/AI_ASSIST.md b/AI_ASSIST.md index 07efd00..1e6fc64 100644 --- a/AI_ASSIST.md +++ b/AI_ASSIST.md @@ -5,17 +5,30 @@ Document one session where you used an LLM to help with a query or a design deci > ⚠️ Never paste real customer data or PII into an LLM. The NYC taxi dataset used here is public, so sample rows are safe to share. ## The problem +didnt know how to get the day of the week or the hour for verification +## The prompt -TODO: What were you trying to solve? Paste the relevant SQL or schema fragment. +how do i get day of the week from timestamp date in postgresql +## The response -## The prompt +In PostgreSQL, you can easily get the day of the week from a timestamp using either EXTRACT() / DATE_PART() or TO_CHAR().The best method depends on whether you want the result as a number (e.g., 0-6 or 1-7) or as a text name (e.g., "Monday").Here is how to do both.1. Get the Day of the Week as a NumberYou can use EXTRACT(DOW FROM timestamp) or EXTRACT(ISODOW FROM timestamp). Pay close attention to how they count the days:Standard Day of Week (DOW)Range: 0 (Sunday) to 6 (Saturday).SQLSELECT EXTRACT(DOW FROM TIMESTAMP '2026-07-01'); +-- Returns 3 (Since July 1, 2026 is a Wednesday) +ISO 8601 Day of Week (ISODOW)Range: 1 (Monday) to 7 (Sunday). This is highly recommended if your weeks strictly start on Monday.SQLSELECT EXTRACT(ISODOW FROM TIMESTAMP '2026-07-01'); +-- Returns 3 (Wednesday) +💡 Note: You can also use DATE_PART('dow', timestamp) which achieves the exact same result as EXTRACT.2. Get the Day of the Week as text (Name)If you need the actual name of the day, use the TO_CHAR() function with the appropriate template pattern.Full Day Name (Day)SQLSELECT TO_CHAR(TIMESTAMP '2026-07-01', 'Day'); +-- Returns 'Wednesday ' (padded with spaces) + +SELECT TO_CHAR(TIMESTAMP '2026-07-01', 'FMDay'); +-- Returns 'Wednesday' (FM removes trailing whitespace padding) +Abbreviated Day Name (Dy)SQLSELECT TO_CHAR(TIMESTAMP '2026-07-01', 'FMDy'); +-- Returns 'Wed' +Quick Reference TableFunction / PatternResult for a WednesdayFormat TypeRange/NotesEXTRACT(DOW FROM ...)3Numeric0 (Sun) - 6 (Sat)EXTRACT(ISODOW FROM ...)3Numeric1 (Mon) - 7 (Sun)TO_CHAR(..., 'FMDay')"Wednesday"TextFull name, capitalizedTO_CHAR(..., 'fmday')"wednesday"TextFull name, lowercaseTO_CHAR(..., 'FMDy')"Wed"Text3-letter abbreviation -TODO: What did you ask the AI? Include the context you provided. -## The response -TODO: What did it suggest? Did it work first try? +it didnt work first try since i used extract first then to_char (t.pickup_datetime, 'D') which gave me only the index of the day so i switched to (t.pickup_datetime, 'Day') which showed the day name +also did the same with the hour ## Reflection -TODO: Did you understand *why* the suggestion worked, or did you accept it blindly? +i did understand why and learned few more things to keep in mind considering timestamps and datetime and the use of Day/'HH12 AM' \ No newline at end of file diff --git a/assets/borough_count.png b/assets/borough_count.png new file mode 100644 index 0000000..62575c2 Binary files /dev/null and b/assets/borough_count.png differ diff --git a/data_dictionary.md b/data_dictionary.md index 5e44612..e0b2b82 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 taxi trip) +- **Primary key:** (none) +- **Foreign keys:** (pickup_location_id, dropoff_location_id) both linking to vw_dim_zones.location_id +- **Measures:** (fare_amount, trip_distance, tip_amount, passenger_count) ## vw_dim_zones -- **Grain:** TODO -- **Primary key:** TODO -- **Foreign keys:** TODO (or "none") -- **Measures:** TODO (or "none, descriptive attributes only") +- **Grain:** (one zone per row) +- **Primary key:** (location_id) +- **Foreign keys:** (none) +- **Measures:** (none, "descriptive attributes only") diff --git a/schema_setup.sql b/schema_setup.sql index a7ae1ad..0f44a13 100644 --- a/schema_setup.sql +++ b/schema_setup.sql @@ -3,24 +3,35 @@ -- Dimension: one row per location_id. Treat location_id as the primary key. -- TODO: complete the SELECT (location_id, zone, borough). -CREATE OR REPLACE VIEW vw_dim_zones AS +CREATE OR REPLACE VIEW dev_bader.vw_dim_zones AS SELECT - -- TODO -FROM nyc_taxi.raw_zones; + z.location_id, + z.zone, + z.borough +FROM nyc_taxi.raw_zones z; -- Fact: one row per taxi trip. -- - Exclude rows where fare_amount is less than 0. -- - Cast pickup_datetime to TIMESTAMP. -- - Keep the location IDs so the view can join to vw_dim_zones. -- TODO: complete the SELECT and the WHERE. -CREATE OR REPLACE VIEW vw_fact_trips AS +CREATE OR REPLACE VIEW dev_bader.vw_fact_trips AS SELECT - -- TODO + vendor_id, + pickup_datetime::timestamp AS pickup_datetime, + dropoff_datetime, + passenger_count, + pickup_location_id, + dropoff_location_id, + trip_distance, + fare_amount, + tip_amount, + payment_type 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 dev_bader.vw_fact_trips f JOIN dev_bader.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..e8979f1 100644 --- a/validation_queries.sql +++ b/validation_queries.sql @@ -5,16 +5,40 @@ -- 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 + t.vendor_id, + t.pickup_datetime, + t.dropoff_datetime, + COUNT(*) AS duplicate_count +FROM nyc_taxi.raw_trips t +GROUP BY t.vendor_id, t.pickup_datetime, t.dropoff_datetime +HAVING COUNT(*) > 1 +ORDER BY duplicate_count 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(*) AS total_rows, + COUNT(*) FILTER (WHERE t.pickup_location_id IS NULL) AS null_pickup, + COUNT(*) FILTER (WHERE t.dropoff_location_id IS NULL) AS null_dropoff +FROM nyc_taxi.raw_trips t; -- 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(t.fare_amount) AS min_fare, + MAX(t.fare_amount) AS max_fare, + COUNT(*) FILTER (WHERE t.fare_amount < 0) AS negative_fares +FROM nyc_taxi.raw_trips t; -- 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 + t.pickup_location_id +FROM nyc_taxi.raw_trips t +LEFT JOIN nyc_taxi.raw_zones z ON t.pickup_location_id = z.location_id +WHERE z.location_id IS NULL +ORDER BY t.pickup_location_id; \ No newline at end of file diff --git a/verification_results.sql b/verification_results.sql index 68a8d26..bce7217 100644 --- a/verification_results.sql +++ b/verification_results.sql @@ -4,19 +4,75 @@ -- 1. Volume: how many total rows in vw_fact_trips? How many rows per borough? -- What is the most common pickup/dropoff location combination? --- TODO +SELECT COUNT(*) AS total_rows +FROM dev_bader.vw_fact_trips t; + + +SELECT z.borough, COUNT(*) AS borough_count +FROM dev_bader.vw_fact_trips t +JOIN dev_bader.vw_dim_zones z ON t.pickup_location_id = z.location_id +GROUP BY z.borough +ORDER BY borough_count DESC; + +SELECT + z1.zone AS pickup_zone, + z2.zone AS dropoff_zone, + COUNT(*) AS trip_count +FROM dev_bader.vw_fact_trips t +JOIN dev_bader.vw_dim_zones z1 ON t.pickup_location_id = z1.location_id +JOIN dev_bader.vw_dim_zones z2 ON t.dropoff_location_id = z2.location_id +GROUP BY z1.zone, z2.zone +ORDER BY trip_count DESC +LIMIT 1; -- (Take a screenshot of the per-borough counts and save it as assets/borough_count.png.) -- 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 + z.zone AS pickup_zone, + SUM(f.fare_amount) AS total_fare +FROM dev_bader.vw_fact_trips t +JOIN dev_bader.vw_dim_zones z ON t.pickup_location_id = z.location_id +GROUP BY z.zone +ORDER BY total_fare DESC +LIMIT 1; +SELECT + z.zone AS pickup_zone, + t.pickup_datetime::date AS trip_date, + SUM(t.fare_amount) AS total_fare +FROM dev_bader.vw_fact_trips t +JOIN dev_bader.vw_dim_zones z ON t.pickup_location_id = z.location_id +GROUP BY z.zone, t.pickup_datetime::date +ORDER BY total_fare DESC +LIMIT 1; -- 3. Geospatial: total number of trips and average trip_distance for each borough. --- TODO +SELECT + z.borough, + COUNT(*) AS total_trips, + AVG(t.trip_distance) AS avg_trip_distance +FROM dev_bader.vw_fact_trips t +JOIN dev_bader.vw_dim_zones z ON t.pickup_location_id = z.location_id +GROUP BY z.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 + TO_CHAR(t.pickup_datetime, 'Day') AS day_of_week, + SUM(t.tip_amount) AS total_tip +FROM dev_bader.vw_fact_trips t +GROUP BY day_of_week +ORDER BY total_tip DESC +LIMIT 1; + +SELECT + TO_CHAR(t.pickup_datetime, 'HH12 AM') AS pickup_hour, + AVG(t.tip_amount) AS avg_tip +FROM dev_bader.vw_fact_trips t +GROUP BY pickup_hour +ORDER BY avg_tip DESC +LIMIT 1; \ No newline at end of file