LLM SQL Debugging: When AI Gets Logic Wrong

Core Concept

Myth: "LLMs Will Replace SQL Skills"

The Sweet Spot: LLM generates boilerplate → Verify the logic using Debug Tables → Ship faster

Real-World Performance Data

The BIRD benchmark (https://bird-bench.github.io/) tests LLMs on real SQL generation:

Why you get paid the big bucks Focus on semantics. Deeply understand both SQL and business requirements!


Query Comparison: "Users who don't listen to Taylor Swift"

Query A: LEFT JOIN Approach

-- Find users who listened to non-Taylor songs (or no songs)
SELECT u.user_id, u.name
FROM Users u
LEFT JOIN Listens l ON u.user_id = l.user_id
LEFT JOIN Songs s ON l.song_id = s.song_id
WHERE s.artist != 'Taylor Swift' OR s.artist IS NULL
GROUP BY u.user_id, u.name

Query B: NOT IN Subquery Approach

-- Find users who never listened to any Taylor Swift song
SELECT u.user_id, u.name
FROM Users u
WHERE u.user_id NOT IN (
    SELECT DISTINCT l.user_id
    FROM Listens l
    JOIN Songs s ON l.song_id = s.song_id
    WHERE s.artist = 'Taylor Swift'
)
Reminder: Beware the NULL in NOT IN death trap! Here we're safe since l.user_id can't be NULL per our Spotify schema. Adding WHERE l.user_id IS NOT NULL is still fine defensive practice.

Step-by-Step Debug Tables

Query A Execution Trace

Step 1: LEFT JOIN Users with Listens

user_idnamelisten_idsong_idratingNotes
1Mickey114.5
1Mickey224.2
1Mickey363.9
2Minnie424.7
2Minnie574.6
2Minnie683.9
3Daffy712.9
3Daffy824.9
3Daffy96NULL
4PlutoNULLNULLNULLNo listens

Step 2: LEFT JOIN with Songs (Add artist)

user_idnamesong_idtitleartistNotes
1Mickey1EvermoreTaylor Swift
1Mickey2WillowTaylor Swift
1Mickey6YesterdayBeatles
2Minnie2WillowTaylor Swift
2Minnie7Yellow SubmarineBeatles
2Minnie8Hey JudeBeatles
3Daffy1EvermoreTaylor Swift
3Daffy2WillowTaylor Swift
3Daffy6YesterdayBeatles
4PlutoNULLNULLNULLNo songs

Step 3: WHERE filter (artist != 'Taylor Swift' OR artist IS NULL)

user_idnamesong_idtitleartistNotes
1Mickey6YesterdayBeatlesKept: artist != 'Taylor Swift'
2Minnie7Yellow SubmarineBeatlesKept: artist != 'Taylor Swift'
2Minnie8Hey JudeBeatlesKept: artist != 'Taylor Swift'
3Daffy6YesterdayBeatlesKept: artist != 'Taylor Swift'
4PlutoNULLNULLNULLKept: artist IS NULL

Step 4: GROUP BY and Final Output

user_idname
1Mickey
2Minnie
3Daffy
4Pluto

Query B Execution Trace

Step 1: Find Taylor Swift listeners

user_idNotes
1Listened to songs 1, 2 (Taylor Swift)
2Listened to song 2 (Taylor Swift)
3Listened to songs 1, 2 (Taylor Swift)

Step 2: NOT IN filter

user_idnameNotes
1MickeyExcluded: user_id IN {1,2,3}
2MinnieExcluded: user_id IN {1,2,3}
3DaffyExcluded: user_id IN {1,2,3}
4PlutoIncluded: user_id NOT IN {1,2,3}

Final Output

user_idname
4Pluto

The Semantic Gap

Same Question, Different Answers: English is ambiguous. SQL is not.

Debug Queries

  1. Decompose: Break query into steps

  2. Trace: Follow data through each step

  3. Compare: Multiple queries = spot differences

  4. Edge Cases: NULL, NOT IN, LEFT JOINs, ...