Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 56 additions & 6 deletions AI_ASSIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Binary file added assets/borough_count.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 8 additions & 8 deletions data_dictionary.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
13 changes: 9 additions & 4 deletions schema_setup.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;
22 changes: 18 additions & 4 deletions validation_queries.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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;
65 changes: 64 additions & 1 deletion verification_results.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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;