SQL Quest

Lessons, hints, explained solutions, the in-browser PostgreSQL editor and practice answer checks in your browser are available. Graded assessments, recorded progress from answer checks and new certificates aren't available yet.

Shifa Clinics operations

Shifa Clinics (fictional) · 10 tables · 47,764 rows · version 1

Open in the playground

Two years (2024–2025) of outpatient operations: clinics, service lines, physicians, about 2,500 synthetic patients, insurers, a service price list, ~15,000 appointments, the services delivered at each visit, invoices and insurance claim rejections. Built for operations analytics: no-show and cancellation rates, wait times, physician utilisation, revenue by service line and payer, and claim rejection analysis. Contains no diagnoses or medical records. Amounts are in SAR.

The documentation is open to everyone. Querying this dataset in the playground is included with Formula Quest membership; lessons that use it follow their own access.

Relationships

clinics6PKclinic_idclinic_namecityopened_onexam_roomsservice_lines7PKservice_line_idservice_line_codeservice_line_namephysicians59PKphysician_idFKclinic_idFKservice_line_idfirst_namelast_namegendertitlestart_dateend_date+ 1 more columnsinsurers6PKinsurer_idinsurer_namecoverage_percentis_activepatients2,444PKpatient_idFKinsurer_idpatient_numberfirst_namelast_namegenderbirth_yearcityregistered_onservices36PKservice_codeFKservice_line_idservice_namelist_pricestandard_minutesis_activeappointments14,645PKappointment_idFKpatient_idFKphysician_idFKclinic_idFKrescheduled_to_appointm…booked_atscheduled_atstatuscancelled_at+ 4 more columnsappointment_services18,578PKappointment_idPKline_noFKservice_codequantityunit_priceinvoices10,892PKinvoice_idFKappointment_idFKinsurer_idinvoice_dategross_amountinsurer_amountpatient_amountstatuspaid_onclaim_rejections1,091PKrejection_idFKinvoice_idrejected_onreason_coderesubmitted
Arrows point from a foreign key to the table it references. PK = primary key, FK = foreign key. Numbers are row counts.

Sample queries

Appointments by status and channel
SELECT channel, status, count(*) AS appointments
FROM appointments
GROUP BY channel, status
ORDER BY channel, appointments DESC;
No-show rate by clinic (booked appointments only)
SELECT c.clinic_name,
       count(*) AS booked,
       round(100.0 * count(*) FILTER (WHERE a.status = 'no_show') / count(*), 1) AS no_show_pct
FROM appointments a
JOIN clinics c ON c.clinic_id = a.clinic_id
WHERE a.channel <> 'walk_in' AND a.status IN ('completed', 'no_show')
GROUP BY c.clinic_name
ORDER BY no_show_pct DESC;
Median wait time by clinic (minutes)
SELECT c.clinic_name,
       percentile_cont(0.5) WITHIN GROUP (ORDER BY extract(epoch FROM a.started_at - a.check_in_at) / 60) AS median_wait_min
FROM appointments a
JOIN clinics c ON c.clinic_id = a.clinic_id
WHERE a.status = 'completed' AND a.check_in_at IS NOT NULL
GROUP BY c.clinic_name
ORDER BY median_wait_min DESC;
Monthly billed revenue by service line
SELECT date_trunc('month', a.scheduled_at)::date AS month, sl.service_line_name,
       sum(s.quantity * s.unit_price) AS revenue
FROM appointment_services s
JOIN appointments a ON a.appointment_id = s.appointment_id
JOIN services sv ON sv.service_code = s.service_code
JOIN service_lines sl ON sl.service_line_id = sv.service_line_id
GROUP BY 1, 2
ORDER BY 1, 3 DESC;
Claim rejections by insurer and reason
SELECT ins.insurer_name, r.reason_code, count(*) AS rejections
FROM claim_rejections r
JOIN invoices i ON i.invoice_id = r.invoice_id
JOIN insurers ins ON ins.insurer_id = i.insurer_id
GROUP BY 1, 2
ORDER BY 1, 3 DESC;

Data dictionary

clinics

6 rows · primary key (clinic_id)

Clinic branches.

ColumnTypeNull?KeyDescription
clinic_idintegerno PKUnique clinic number.
clinic_nametextnouniqueBranch name.
citytextnoCity.
opened_ondatenoDate the branch opened (Abha opened during the data period).
exam_roomsintegernoNumber of examination rooms.

service_lines

7 rows · primary key (service_line_id)

Medical specialties offered (service lines).

ColumnTypeNull?KeyDescription
service_line_idintegerno PKUnique service line number.
service_line_codetextnouniqueShort code (FM, PED, DEN, ...).
service_line_nametextnouniqueService line name.

physicians

59 rows · primary key (physician_id)

Physicians, one row per physician per clinic assignment (a transfer creates a new row).

ColumnTypeNull?KeyDescription
physician_idintegerno PKUnique physician record number.
clinic_idintegerno clinics.clinic_idClinic the physician works at.
service_line_idintegerno service_lines.service_line_idSpecialty.
first_nametextnoFirst name (fictional).
last_nametextnoFamily name (fictional).
genderchar(2)noF or M.
titletextnoConsultant, Specialist or General Practitioner.
start_datedatenoFirst working day at this clinic.
end_datedateyesLast working day. NULL = still working here.
is_activebooleannoTRUE while the physician still works at this clinic.

insurers

6 rows · primary key (insurer_id)

Health insurers the clinics bill. Patients without an insurer are self-pay.

ColumnTypeNull?KeyDescription
insurer_idintegerno PKUnique insurer number.
insurer_nametextnouniqueInsurer name (fictional).
coverage_percentnumeric(5,2)noShare of the gross amount the insurer pays; the patient pays the rest.
is_activebooleannoFALSE when the contract has ended (Basma Health, after 2025-06-30).

patients

2,444 rows · primary key (patient_id)

Registered patients (synthetic). Only minimal demographics are stored: no full date of birth and no medical information.

ColumnTypeNull?KeyDescription
patient_idintegerno PKUnique patient record number.
patient_numbertextnouniqueMedical record number shown on cards (SH-######), ascending with registration date.
first_nametextnoFirst name (fictional).
last_nametextnoFamily name (fictional).
genderchar(2)noF or M.
birth_yearsmallintyesYear of birth. NULL when not captured.
citytextyesCity of residence. NULL when not captured.
insurer_idintegeryes insurers.insurer_idCurrent insurer. NULL = self-pay.
registered_ondatenoDate the patient record was created.

services

36 rows · primary key (service_code)

Price list of billable services. list_price is the current (2025) price.

ColumnTypeNull?KeyDescription
service_codetextno PKService code, prefixed with the service line code.
service_nametextnoService name.
service_line_idintegerno service_lines.service_line_idService line that delivers it.
list_pricenumeric(12,2)noCurrent list price in SAR (2024 prices were about 5% lower).
standard_minutesintegernoPlanned duration in minutes.
is_activebooleannoFALSE for services no longer offered.

appointments

14,645 rows · primary key (appointment_id)

One row per booking or walk-in. A rescheduled booking keeps its row (status 'rescheduled') and points to the replacement appointment.

ColumnTypeNull?KeyDescription
appointment_idintegerno PKUnique appointment number (ascending with booking time).
patient_idintegerno patients.patient_idThe patient.
physician_idintegerno physicians.physician_idThe physician the appointment is with (determines the service line).
clinic_idintegerno clinics.clinic_idWhere the appointment takes place.
booked_attimestampnoWhen the booking was made. For walk-ins equal to scheduled_at.
scheduled_attimestampnoAppointment slot start (walk-ins: arrival time).
statustextnocompleted, no_show (did not come), cancelled (by the patient or clinic) or rescheduled (moved to another appointment).
cancelled_attimestampyesWhen the cancellation was recorded. NULL unless status = 'cancelled'.
check_in_attimestampyesReception check-in. NULL if the patient never arrived or reception skipped the check-in.
started_attimestampyesWhen the physician started the consultation. Only for completed visits.
ended_attimestampyesWhen the consultation ended. NULL if never arrived or not closed in the system.
channeltextnoHow it was booked: app, phone or walk_in.
rescheduled_to_appointment_idintegeryes appointments.appointment_idFor status 'rescheduled': the replacement appointment. NULL otherwise.

appointment_services

18,578 rows · primary key (appointment_id, line_no)

Services delivered and billed at a completed visit, one row per invoice line.

ColumnTypeNull?KeyDescription
appointment_idintegerno PK appointments.appointment_idThe visit.
line_nosmallintno PKLine number within the visit (1, 2, ...).
service_codetextno services.service_codeThe service delivered.
quantityintegernoUnits delivered (e.g. number of fillings).
unit_pricenumeric(12,2)noPrice charged per unit in SAR (the list price in force on the visit date).

invoices

10,892 rows · primary key (invoice_id)

One invoice per billed visit. For insured visits status tracks the insurance claim; for self-pay visits it tracks the patient's payment.

ColumnTypeNull?KeyDescription
invoice_idintegerno PKUnique invoice number.
appointment_idintegerno appointments.appointment_iduniqueThe visit billed.
insurer_idintegeryes insurers.insurer_idInsurer billed for this visit. NULL = self-pay (may differ from the patient's current insurer).
invoice_datedatenoDate the invoice was issued (0–3 days after the visit).
gross_amountnumeric(12,2)noTotal of the visit's service lines, SAR.
insurer_amountnumeric(12,2)noShare claimed from the insurer (gross × coverage_percent). 0 for self-pay.
patient_amountnumeric(12,2)noShare paid by the patient (co-payment, or everything for self-pay).
statustextnopaid, pending (awaiting payment), rejected (claim finally rejected) or written_off.
paid_ondateyesDate the invoice was settled. NULL unless status = 'paid'.

claim_rejections

1,091 rows · primary key (rejection_id)

Insurance claim rejections. An invoice can be rejected more than once; resubmitted claims may be paid later.

ColumnTypeNull?KeyDescription
rejection_idintegerno PKUnique rejection number.
invoice_idintegerno invoices.invoice_idThe rejected invoice (claim).
rejected_ondatenoDate the insurer rejected the claim.
reason_codetextnomissing_preauthorization, service_not_covered, coding_error, eligibility_expired, duplicate_claim or late_submission.
resubmittedbooleannoTRUE if the clinic corrected and resubmitted the claim after this rejection.