lesson 29 Dates, strings & subqueries · intermediate
Ask whether a row exists
Test whether matching rows exist without duplicating results.
the idea
EXISTS (subquery) is true when the subquery returns at least one row; NOT EXISTS is true when it returns none. Use it to ask whether related rows exist without multiplying the outer rows the way a join would.
Dataset: Healthcare
The tables the challenges use, with their first 6 rows. Under each column name: the kind of value it holds, and which table it links to.
patients9 columns · 3,200 rows
| patient_idnumber · unique id | first_nametext | last_nametext | sextext | birth_datedate | citytext | statetext | insurance_typetext | deceased_datetext |
|---|---|---|---|---|---|---|---|---|
| 1 | Sara | Foster | M | 1972-05-08 | Baltimore | MD | medicaid | NULL |
| 2 | Anika | Jackson | M | 1953-07-10 | Baltimore | MD | medicare | NULL |
| 3 | Jessica | Hughes | F | 2010-12-24 | Newark | NJ | medicaid | NULL |
| 4 | Rahul | Gonzalez | F | 2023-04-04 | Los Angeles | CA | commercial | NULL |
| 5 | Jasmine | Miller | M | 1996-08-07 | Manchester | NH | medicaid | NULL |
| 6 | Emma | Ibrahim | M | 1933-12-10 | New York | NY | medicare | NULL |
- patient_idnumber · unique id
- 1
- first_nametext
- Sara
- last_nametext
- Foster
- sextext
- M
- birth_datedate
- 1972-05-08
- citytext
- Baltimore
- statetext
- MD
- insurance_typetext
- medicaid
- deceased_datetext
- NULL
- patient_idnumber · unique id
- 2
- first_nametext
- Anika
- last_nametext
- Jackson
- sextext
- M
- birth_datedate
- 1953-07-10
- citytext
- Baltimore
- statetext
- MD
- insurance_typetext
- medicare
- deceased_datetext
- NULL
- patient_idnumber · unique id
- 3
- first_nametext
- Jessica
- last_nametext
- Hughes
- sextext
- F
- birth_datedate
- 2010-12-24
- citytext
- Newark
- statetext
- NJ
- insurance_typetext
- medicaid
- deceased_datetext
- NULL
- patient_idnumber · unique id
- 4
- first_nametext
- Rahul
- last_nametext
- Gonzalez
- sextext
- F
- birth_datedate
- 2023-04-04
- citytext
- Los Angeles
- statetext
- CA
- insurance_typetext
- commercial
- deceased_datetext
- NULL
- patient_idnumber · unique id
- 5
- first_nametext
- Jasmine
- last_nametext
- Miller
- sextext
- M
- birth_datedate
- 1996-08-07
- citytext
- Manchester
- statetext
- NH
- insurance_typetext
- medicaid
- deceased_datetext
- NULL
- patient_idnumber · unique id
- 6
- first_nametext
- Emma
- last_nametext
- Ibrahim
- sextext
- M
- birth_datedate
- 1933-12-10
- citytext
- New York
- statetext
- NY
- insurance_typetext
- medicare
- deceased_datetext
- NULL
first 6 of 3,200 rows · your queries use all of them
encounters9 columns · 20,447 rows
| encounter_idnumber · unique id | patient_idnumber · links to patients | provider_idnumber · links to providers | encounter_classtext | start_atdate & time | end_atdate & time | reason_codetext | total_costdecimal | payer_paiddecimal |
|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 4 | ambulatory | 2022-08-07 13:00:00 | 2022-08-07 13:24:45 | T78.40XA | 175.6 | 159.38 |
| 2 | 1 | 4 | telehealth | 2022-10-12 11:15:00 | 2022-10-12 11:59:48 | E11.9 | 110.02 | 95.53 |
| 3 | 1 | 74 | telehealth | 2023-05-12 11:30:00 | 2023-05-12 12:13:27 | E11.9 | 83.68 | 74.77 |
| 4 | 1 | 349 | ambulatory | 2023-12-17 08:15:00 | 2023-12-17 08:39:33 | E11.9 | 198.23 | 187.3 |
| 5 | 1 | 4 | telehealth | 2024-05-09 16:45:00 | 2024-05-09 17:29:01 | E66.9 | 115.08 | 98.4 |
| 6 | 1 | 4 | ambulatory | 2024-09-07 13:00:00 | 2024-09-07 13:27:30 | E66.9 | 153.61 | 142.99 |
- encounter_idnumber · unique id
- 1
- patient_idnumber · links to patients
- 1
- provider_idnumber · links to providers
- 4
- encounter_classtext
- ambulatory
- start_atdate & time
- 2022-08-07 13:00:00
- end_atdate & time
- 2022-08-07 13:24:45
- reason_codetext
- T78.40XA
- total_costdecimal
- 175.6
- payer_paiddecimal
- 159.38
- encounter_idnumber · unique id
- 2
- patient_idnumber · links to patients
- 1
- provider_idnumber · links to providers
- 4
- encounter_classtext
- telehealth
- start_atdate & time
- 2022-10-12 11:15:00
- end_atdate & time
- 2022-10-12 11:59:48
- reason_codetext
- E11.9
- total_costdecimal
- 110.02
- payer_paiddecimal
- 95.53
- encounter_idnumber · unique id
- 3
- patient_idnumber · links to patients
- 1
- provider_idnumber · links to providers
- 74
- encounter_classtext
- telehealth
- start_atdate & time
- 2023-05-12 11:30:00
- end_atdate & time
- 2023-05-12 12:13:27
- reason_codetext
- E11.9
- total_costdecimal
- 83.68
- payer_paiddecimal
- 74.77
- encounter_idnumber · unique id
- 4
- patient_idnumber · links to patients
- 1
- provider_idnumber · links to providers
- 349
- encounter_classtext
- ambulatory
- start_atdate & time
- 2023-12-17 08:15:00
- end_atdate & time
- 2023-12-17 08:39:33
- reason_codetext
- E11.9
- total_costdecimal
- 198.23
- payer_paiddecimal
- 187.3
- encounter_idnumber · unique id
- 5
- patient_idnumber · links to patients
- 1
- provider_idnumber · links to providers
- 4
- encounter_classtext
- telehealth
- start_atdate & time
- 2024-05-09 16:45:00
- end_atdate & time
- 2024-05-09 17:29:01
- reason_codetext
- E66.9
- total_costdecimal
- 115.08
- payer_paiddecimal
- 98.4
- encounter_idnumber · unique id
- 6
- patient_idnumber · links to patients
- 1
- provider_idnumber · links to providers
- 4
- encounter_classtext
- ambulatory
- start_atdate & time
- 2024-09-07 13:00:00
- end_atdate & time
- 2024-09-07 13:27:30
- reason_codetext
- E66.9
- total_costdecimal
- 153.61
- payer_paiddecimal
- 142.99
first 6 of 20,447 rows · your queries use all of them
appointments7 columns · 19,438 rows
| appointment_idnumber · unique id | patient_idnumber · links to patients | provider_idnumber · links to providers | booked_atdate & time | scheduled_startdate & time | statustext | encounter_idnumber · links to encounters |
|---|---|---|---|---|---|---|
| 1 | 1 | 4 | 2022-08-04 10:00:00 | 2022-08-04 13:00:00 | cancelled | NULL |
| 2 | 1 | 4 | 2022-08-04 10:00:00 | 2022-08-07 13:00:00 | completed | 1 |
| 3 | 1 | 4 | 2022-09-29 09:15:00 | 2022-10-12 11:15:00 | completed | 2 |
| 4 | 1 | 74 | 2023-04-10 06:30:00 | 2023-05-12 11:30:00 | completed | 3 |
| 5 | 1 | 349 | 2023-12-08 05:15:00 | 2023-12-17 08:15:00 | completed | 4 |
| 6 | 1 | 4 | 2024-04-22 12:45:00 | 2024-05-09 16:45:00 | completed | 5 |
- appointment_idnumber · unique id
- 1
- patient_idnumber · links to patients
- 1
- provider_idnumber · links to providers
- 4
- booked_atdate & time
- 2022-08-04 10:00:00
- scheduled_startdate & time
- 2022-08-04 13:00:00
- statustext
- cancelled
- encounter_idnumber · links to encounters
- NULL
- appointment_idnumber · unique id
- 2
- patient_idnumber · links to patients
- 1
- provider_idnumber · links to providers
- 4
- booked_atdate & time
- 2022-08-04 10:00:00
- scheduled_startdate & time
- 2022-08-07 13:00:00
- statustext
- completed
- encounter_idnumber · links to encounters
- 1
- appointment_idnumber · unique id
- 3
- patient_idnumber · links to patients
- 1
- provider_idnumber · links to providers
- 4
- booked_atdate & time
- 2022-09-29 09:15:00
- scheduled_startdate & time
- 2022-10-12 11:15:00
- statustext
- completed
- encounter_idnumber · links to encounters
- 2
- appointment_idnumber · unique id
- 4
- patient_idnumber · links to patients
- 1
- provider_idnumber · links to providers
- 74
- booked_atdate & time
- 2023-04-10 06:30:00
- scheduled_startdate & time
- 2023-05-12 11:30:00
- statustext
- completed
- encounter_idnumber · links to encounters
- 3
- appointment_idnumber · unique id
- 5
- patient_idnumber · links to patients
- 1
- provider_idnumber · links to providers
- 349
- booked_atdate & time
- 2023-12-08 05:15:00
- scheduled_startdate & time
- 2023-12-17 08:15:00
- statustext
- completed
- encounter_idnumber · links to encounters
- 4
- appointment_idnumber · unique id
- 6
- patient_idnumber · links to patients
- 1
- provider_idnumber · links to providers
- 4
- booked_atdate & time
- 2024-04-22 12:45:00
- scheduled_startdate & time
- 2024-05-09 16:45:00
- statustext
- completed
- encounter_idnumber · links to encounters
- 5
first 6 of 19,438 rows · your queries use all of them
worked example
Run these steps in order to see the idea before you write your own query. The step marked “the pattern” is what the challenges ask for. Running the example does not count toward progress.
Keep patients with a related row. Run this. EXISTS returns true when the subquery finds at least one inpatient encounter.
step-1.sqlSELECT p.patient_id FROM patients p WHERE EXISTS ( SELECT 1 FROM encounters e WHERE e.patient_id = p.patient_id AND e.encounter_class = 'inpatient' ) LIMIT 10;
See why join over-counts. Run this. Joining encounters repeats patients who have multiple inpatient stays.
step-2.sqlSELECT COUNT(*) AS joined_rows, COUNT(DISTINCT p.patient_id) AS patients FROM patients p JOIN encounters e ON e.patient_id = p.patient_id WHERE e.encounter_class = 'inpatient';
Put it together. The lesson's pattern in one query.the pattern
example.sqlSELECT p.patient_id FROM patients p WHERE EXISTS ( SELECT 1 FROM encounters e WHERE e.patient_id = p.patient_id AND e.encounter_class = 'inpatient' );
common mistake
Joining patients to encounters to find who had an emergency visit returns a patient once per emergency visit unless you add DISTINCT.