Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions .user.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
id: 65fed7dc-cfb1-40ec-af82-fb56180cafce
29 changes: 17 additions & 12 deletions AI_ASSIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,29 @@ Document one place you used an LLM during this assignment.

## The problem

<!-- TODO: describe the specific problem you asked an LLM about.
Example: "My safe_divide macro compiled but returned NULL for all rows even
when tip_amount and fare_amount were both non-zero." -->

TODO

While building the stg_trips staging model, i had an SQL syntax issue because i placed the "WHERE" clause before the "FROM" clause.
## The prompt

<!-- TODO: paste the exact prompt you sent to the LLM. -->

TODO

Im building a dbt staging model and my SQL query is failing. what is the problem here?
my query was :
SELECT
pickup_datetime,
pickup_location_id,
fare_amount,
tip_amount,
trip_distance,

{{ safe_divide('tip_amount', 'fare_amount') }} as tip_pct

where pickup_location_id is not null
FROM {{ source('nyc_taxi', 'raw_trips') }}
and fare_amount >= 0
## The response

<!-- TODO: summarise or paste what the LLM returned. -->

TODO
the llm explained that SQL clauses follow a fixed order. The FROM clause must come before the WHERE clause because SQL needs to know the source table before applying filters.

## Reflection

Expand All @@ -29,8 +35,7 @@ TODO
'ratio' to 'tip_pct' to match the assignment schema." -->

TODO

---
i changed the query order so that "FROM" comes immediately after the SELECT columns, followed by the WHERE filters.

> Remember: never paste real connection strings, passwords, or PII into an LLM.
> The NYC TLC dataset is public so sample rows are safe here, but practise the habit.
Binary file added docs/Lineage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 5 additions & 4 deletions macros/safe_divide.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
-- Use for tip_pct = tip_amount / fare_amount and similar ratio columns.

{% macro safe_divide(numerator, denominator) %}
-- TODO: implement the macro body.
-- Use NULLIF(denominator, 0) to avoid division-by-zero errors.
-- Return only the SQL expression (no SELECT, no semicolon).
NULL

{{ numerator }} / nullif({{ denominator }}, 0)

{% endmacro %}


32 changes: 23 additions & 9 deletions models/marts/_fct_daily_borough_stats.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,36 @@ version: 2
models:
- name: fct_daily_borough_stats
description: >
TODO: state the grain (one row per ___), the source lineage
(built from ___ and ___), and at least one known caveat
(e.g. rows dropped in staging, any WARN-severity tests).
Daily borough-level taxi statistics with a grain of one row per
(pickup_borough, pickup_date).
Built from stg_trips and stg_zones.
Trips with missing or invalid pickup locations are removed during staging
or excluded by the INNER JOIN.
The avg_tip_pct warning test may return some rows where
tips are higher than the fare amount.

tests:
- dbt_utils.unique_combination_of_columns:
combination_of_columns:
- pickup_borough
- pickup_date
# TODO: Task 5 -- add the compound uniqueness test on the mart's primary
# key (pickup_borough, pickup_date). You need the dbt_utils package for
# this: declare it in packages.yml and run `dbt deps` first.
columns:
- name: pickup_borough
description: "TODO: explain what this column contains and where it comes from"
description: "The TLC borough where trips were picked up, sourced from stg_zones.borough"
tests:
- not_null
- name: pickup_date
description: "TODO: explain units and how it is derived"
description: "The calendar date of the trip pickup, derived from pickup_datetime."
tests:
- not_null
- name: trip_count
description: "TODO: explain what is counted (unit: number of trips)"
description: "The number of taxi trips picked up in the borough on that dat."
- name: total_fare
description: "TODO: explain units (USD) and what fare_amount represents"
description: "The total fare amount collected in US dollars, calculated as the sum of fare_amount"
- name: avg_tip_pct
description: "TODO: explain the ratio (tip_amount / fare_amount)"
description: "The average tipping ratio calculated as tip_amount divided by fare_amount."
- name: avg_trip_distance
description: "TODO: explain units (miles, from TLC source data)"
description: "The average trip distance in miles based on the TLC trip_distance field"
27 changes: 21 additions & 6 deletions models/marts/fct_daily_borough_stats.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
-- Grain: one row per (pickup_borough, pickup_date).
-- Used to answer: trip volume, revenue, tipping behaviour, and distance profile
-- per borough per day for January 2024.
{{ config(materialized='table') }}

WITH trips AS (
SELECT *
Expand All @@ -26,13 +27,27 @@ SELECT
-- total_fare NUMERIC - sum(fare_amount)
-- avg_tip_pct NUMERIC - avg(tip_pct)
-- avg_trip_distance NUMERIC - avg(trip_distance)
NULL AS pickup_borough,
NULL AS pickup_date,
NULL AS trip_count,
NULL AS total_fare,
NULL AS avg_tip_pct,
NULL AS avg_trip_distance
z.borough AS pickup_borough,

t.pickup_datetime::date AS pickup_date,

count(*) AS trip_count,

sum(t.fare_amount) AS total_fare,

avg(t.tip_pct) AS avg_tip_pct,

avg(t.trip_distance) AS avg_trip_distance


FROM trips t
-- TODO: add JOIN to zones here
INNER JOIN zones z

ON t.pickup_location_id = z.location_id
-- TODO: add GROUP BY here
GROUP BY

z.borough,

t.pickup_datetime::date
4 changes: 2 additions & 2 deletions models/staging/_sources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ sources:
schema: nyc_taxi # TODO: confirm this matches the schema where raw_trips and raw_zones live
tables:
- name: raw_trips
description: "TODO: one sentence on what this table contains and its grain"
description: "Raw NYC green Taxi trip records, with one row representing one individual taxi trip."
- name: raw_zones
description: "TODO: one sentence on what this table contains"
description: "Raw TLC taxi zone lookup table containing location IDs and borough/zone information."
18 changes: 11 additions & 7 deletions models/staging/_stg_trips.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@ version: 2

models:
- name: stg_trips
description: "TODO: state the grain (one row per ___) and what source this reads from"
description: "One row per taxi trip from the raw_trips source after basic cleaning and filtering."
columns:
- name: pickup_datetime
description: "TODO"
description: "Timestamp when the trip started"
tests:
- not_null
# TODO: Task 5 -- add not_null tests on every column used as a join or
# group-by key. See the chapter's dbt Tests section for the syntax.
- name: pickup_location_id
description: "TODO"
description: "TLC location ID where the trip was picked up."
tests:
- not_null
- name: fare_amount
description: "TODO"
description: "Fare charged for the trip in US dollars"
- name: tip_amount
description: "TODO"
description: "Tip paid by the passenger in US dollars"
- name: trip_distance
description: "TODO"
description: "Distance travelled during the trip in miles"
- name: tip_pct
description: "TODO"
description: "Tip as a percentage of the fare amount"
10 changes: 7 additions & 3 deletions models/staging/_stg_zones.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ version: 2

models:
- name: stg_zones
description: "TODO: state the grain and what source this reads from"
description: "One row per TLC taxi zone from the raw_zones source"
columns:
- name: location_id
description: "TODO"
description: "Unique identifier for each TLC taxi zone."
tests:
- not_null
- unique

# TODO: Task 5 -- this column is the join key. Which two generic tests
# guarantee a clean one-to-many join from stg_trips?
- name: borough
description: "TODO"
description: "Borough that the taxi zone belongs to."
17 changes: 11 additions & 6 deletions models/staging/stg_trips.sql
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
-- Staging model: one row per NYC green taxi trip (January 2024).
-- Renames source columns, adds derived columns, and filters bad rows.
-- Downstream: fct_daily_borough_stats joins this to stg_zones.
{{ config(materialized='view') }}

SELECT
-- TODO: select the columns you need for the mart:
-- pickup_datetime, pickup_location_id, fare_amount, tip_amount, trip_distance
--
-- TODO: add tip_pct using {{ safe_divide('tip_amount', 'fare_amount') }}
--
-- TODO: filter out rows where pickup_location_id IS NULL or fare_amount < 0
pickup_datetime,
pickup_location_id,
fare_amount,
tip_amount,
trip_distance,

{{ safe_divide('tip_amount', 'fare_amount') }} as tip_pct

FROM {{ source('nyc_taxi', 'raw_trips') }}

where pickup_location_id is not null
and fare_amount >= 0
5 changes: 3 additions & 2 deletions models/staging/stg_zones.sql
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
-- Staging model: one row per TLC zone (265 zones).
-- Exposes location_id and borough for use as a lookup in the mart.
{{ config(materialized='view') }}

SELECT
-- TODO: select location_id and borough from {{ source('nyc_taxi', 'raw_zones') }}

location_id,
borough
FROM {{ source('nyc_taxi', 'raw_zones') }}
5 changes: 5 additions & 0 deletions package-lock.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
packages:
- name: dbt_utils
package: dbt-labs/dbt_utils
version: 1.3.0
sha1_hash: 226ae69cdfbc9367e2aa2c472b01f99dbce11de0
4 changes: 3 additions & 1 deletion packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
# `dbt deps` to install it. You need it for the compound uniqueness test
# on the mart. See https://hub.getdbt.com/dbt-labs/dbt_utils/latest/
# for the package block syntax.
packages: []
packages:
- package: dbt-labs/dbt_utils
version: 1.3.0
74 changes: 60 additions & 14 deletions reports/answers.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Business Question Answers

Queries run against `dev_<your_name>.fct_daily_borough_stats`.
Queries run against `dev_baraah.fct_daily_borough_stats`.

## Q1: Highest total `total_fare` across the whole loaded dataset

Expand All @@ -9,10 +9,20 @@ Queries run against `dev_<your_name>.fct_daily_borough_stats`.
```sql
-- TODO: query fct_daily_borough_stats grouped by pickup_borough, sum total_fare, order DESC
```

**Result:** TODO

**Interpretation:** TODO (one sentence)
SELECT
pickup_borough,
SUM(total_fare) AS total_revenue
FROM dev_baraah.fct_daily_borough_stats
GROUP BY pickup_borough
ORDER BY total_revenue DESC
LIMIT 1;
**Result:**
pickup_borough | total_revenue
----------------+---------------
Manhattan | 493955.62
(1 row)

**Interpretation:** The borough with the highest total fare revenue across the loaded dataset was Manhattan $493955.62.

---

Expand All @@ -23,10 +33,20 @@ Queries run against `dev_<your_name>.fct_daily_borough_stats`.
```sql
-- TODO: query fct_daily_borough_stats grouped by pickup_date, sum trip_count, order DESC LIMIT 1
```

**Result:** TODO

**Interpretation:** TODO (one sentence)
SELECT
pickup_date,
SUM(trip_count) AS total_trips
FROM dev_baraah.fct_daily_borough_stats
GROUP BY pickup_date
ORDER BY total_trips DESC
LIMIT 1;
**Result:**
pickup_date | total_trips
-------------+-------------
2024-01-17 | 2221
(1 row)

**Interpretation:** The busiest day in the dataset was 2024-01-17 with 2221 trips across all boroughs.

---

Expand All @@ -37,10 +57,25 @@ Queries run against `dev_<your_name>.fct_daily_borough_stats`.
```sql
-- TODO: query fct_daily_borough_stats order by avg_tip_pct DESC LIMIT 5
```

**Result:** TODO

**Interpretation:** TODO — note whether any avg_tip_pct > 1 rows appear and what causes them
SELECT
pickup_borough,
pickup_date,
avg_tip_pct
FROM dev_baraah.fct_daily_borough_stats
ORDER BY avg_tip_pct DESC
LIMIT 5;

**Result:**
pickup_borough | pickup_date | avg_tip_pct
----------------+-------------+--------------------
Unknown | 2024-01-30 | 2.500125
Unknown | 2024-01-07 | 1.3396296296296295
Unknown | 2024-01-11 | 1.1044956140350877
Unknown | 2024-01-16 | 1
Unknown | 2024-01-18 | 0.611111111111111
(5 rows)

**Interpretation:** The highest average tip percentage occurred in Unknown on 2024-01-30 .

---

Expand All @@ -51,7 +86,18 @@ Queries run against `dev_<your_name>.fct_daily_borough_stats`.
```sql
-- TODO: use percentile_cont(0.5) WITHIN GROUP (ORDER BY trip_count) filtered by borough
```
SELECT
pickup_borough,
percentile_cont(0.5) WITHIN GROUP (ORDER BY trip_count) AS median_trip_count
FROM dev_baraah.fct_daily_borough_stats
WHERE pickup_borough IN ('Manhattan', 'Brooklyn')
GROUP BY pickup_borough;

**Result:** TODO
pickup_borough | median_trip_count
----------------+-------------------
Brooklyn | 248
Manhattan | 1169.5
(2 rows)

**Interpretation:** TODO (one sentence on the ratio)
**Interpretation:** Manhattan had a higher median daily trip count than Brooklyn.
1 change: 1 addition & 0 deletions test-output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bash: test.sh: No such file or directory
11 changes: 9 additions & 2 deletions tests/assert_avg_tip_pct_within_bounds.sql
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@
-- unique_combination primary-key tests, which you want to stay at ERROR.
--
-- The test passes (no WARN) when zero rows are returned; any returned rows are flagged.
{{ config(severity='warn') }}

-- TODO: write the SELECT here.
-- Query {{ ref('fct_daily_borough_stats') }} and return rows where avg_tip_pct > 1.
SELECT NULL AS pickup_borough, NULL AS pickup_date, NULL AS avg_tip_pct
WHERE FALSE -- TODO: replace with the real query
SELECT
pickup_borough,
pickup_date,
avg_tip_pct

FROM {{ ref('fct_daily_borough_stats') }}

WHERE avg_tip_pct > 1
Loading