cswork / libraryOpen practice workspace

Coding questions · Meta

Write SQL for library analytics

This question evaluates proficiency in SQL-based data manipulation and analytical querying, focusing on relational schema navigation, joins, aggregation, filtering, and handling temporal and conditional metrics within a library dataset.

Given a library database, write SQL to answer the following:

  1. Count the number of books that are currently not returned (i.e., still checked out) and are in good condition.
  2. Among those currently not returned and in good condition, compute the percentage that have been renewed more than 2 times (use that subset as the denominator).
  3. Return the top 3 book titles or IDs that have more than 10 copies and the longest total lending time across all their loans (sum of loan durations).
  4. Find the member–referrer pair with the greatest difference in the number of reservations they have made, and return the pair and the difference.

Count active (not returned) loans for books in good condition

You are given a library database with books and loan transactions.

A book is considered "currently not returned" if its loan record has return_date IS NULL.

Write a SQL query to count how many loaned books are currently not returned AND the associated book is in 'Good' condition.

Return a single row with a single column named not_returned_good_count.

Table books

columntypeconstraints
book_idINTPRIMARY KEY, NOT NULL
titleVARCHAR(200)NOT NULL
total_copiesINTNOT NULL
conditionVARCHAR(20)NOT NULL

Sample data:

book_idconditiontitletotal_copies
1GoodSQL Basics12
2GoodData Science 10120
3GoodHistory of Rome15
4GoodRare Manuscript2
5DamagedDamaged Book30
6GoodPython for Data11

Table members

columntypeconstraints
member_idINTPRIMARY KEY, NOT NULL
member_nameVARCHAR(100)NOT NULL
referrer_member_idINT

Sample data:

member_idmember_namereferrer_member_id
1AliceNULL
2Bob1
3Cara1
4Dan2
5Eva2
6Finn3

Table loans

columntypeconstraints
loan_idINTPRIMARY KEY, NOT NULL
book_idINTNOT NULL
member_idINTNOT NULL
checkout_dateDATENOT NULL
due_dateDATENOT NULL
return_dateDATE
renew_countINTNOT NULL

Sample data:

book_idcheckout_datedue_dateloan_idmember_idrenew_countreturn_date
12025-05-012025-05-15101112025-05-20
12025-05-252025-06-0810223NULL
22025-04-012025-04-15103322025-05-15
22025-05-202025-06-0310441NULL
32025-03-012025-03-15105502025-04-15
32025-05-102025-05-2410614NULL
52025-01-012025-01-15107212025-05-01
62025-05-282025-06-1110830NULL
62025-02-012025-02-15109412025-03-01
22025-02-102025-02-24110202025-02-20
12025-01-102025-01-24111502025-01-25

Table reservations

columntypeconstraints
reservation_idINTPRIMARY KEY, NOT NULL
member_idINTNOT NULL
book_idINTNOT NULL
reservation_dateDATENOT NULL

Sample data:

book_idmember_idreservation_datereservation_id
212025-01-05201
312025-02-07202
112025-03-10203
612025-04-12204
412025-05-01205
122025-03-03206
232025-01-15207
632025-02-16208
332025-05-20209
132025-05-25210
252025-01-20211
352025-01-22212
652025-02-10213
152025-03-08214
552025-04-01215
452025-05-10216
252025-05-30217
362025-02-25218
662025-03-18219

Expected output

One row with the count of loan records that are still checked out (return_date is NULL) where the book's condition is 'Good'.

not_returned_good_count
4

Hints

  1. A loan is active if return_date is NULL.
  2. Join loans to books and filter condition = 'Good'.

Percentage of active good-condition loans renewed more than 2 times

Using the same definition as in Question 1:

  • "currently not returned" means return_date IS NULL
  • book must be in 'Good' condition

Among those active good-condition loans, compute the percentage of loans that have been renewed more than 2 times (renew_count > 2).

Return a single row with a single column named pct_renewed_gt_2, rounded to 2 decimals.

Table books

columntypeconstraints
book_idINTPRIMARY KEY, NOT NULL
titleVARCHAR(200)NOT NULL
total_copiesINTNOT NULL
conditionVARCHAR(20)NOT NULL

Sample data:

book_idconditiontitletotal_copies
1GoodSQL Basics12
2GoodData Science 10120
3GoodHistory of Rome15
4GoodRare Manuscript2
5DamagedDamaged Book30
6GoodPython for Data11

Table members

columntypeconstraints
member_idINTPRIMARY KEY, NOT NULL
member_nameVARCHAR(100)NOT NULL
referrer_member_idINT

Sample data:

member_idmember_namereferrer_member_id
1AliceNULL
2Bob1
3Cara1
4Dan2
5Eva2
6Finn3

Table loans

columntypeconstraints
loan_idINTPRIMARY KEY, NOT NULL
book_idINTNOT NULL
member_idINTNOT NULL
checkout_dateDATENOT NULL
due_dateDATENOT NULL
return_dateDATE
renew_countINTNOT NULL

Sample data:

book_idcheckout_datedue_dateloan_idmember_idrenew_countreturn_date
12025-05-012025-05-15101112025-05-20
12025-05-252025-06-0810223NULL
22025-04-012025-04-15103322025-05-15
22025-05-202025-06-0310441NULL
32025-03-012025-03-15105502025-04-15
32025-05-102025-05-2410614NULL
52025-01-012025-01-15107212025-05-01
62025-05-282025-06-1110830NULL
62025-02-012025-02-15109412025-03-01
22025-02-102025-02-24110202025-02-20
12025-01-102025-01-24111502025-01-25

Table reservations

columntypeconstraints
reservation_idINTPRIMARY KEY, NOT NULL
member_idINTNOT NULL
book_idINTNOT NULL
reservation_dateDATENOT NULL

Sample data:

book_idmember_idreservation_datereservation_id
212025-01-05201
312025-02-07202
112025-03-10203
612025-04-12204
412025-05-01205
122025-03-03206
232025-01-15207
632025-02-16208
332025-05-20209
132025-05-25210
252025-01-20211
352025-01-22212
652025-02-10213
152025-03-08214
552025-04-01215
452025-05-10216
252025-05-30217
362025-02-25218
662025-03-18219

Expected output

One row with the percentage (0-100) of active good-condition loans that have renew_count > 2, rounded to 2 decimals.

pct_renewed_gt_2
50

Hints

  1. Use the same filter as Question 1 to define the denominator.
  2. Compute numerator with a CASE expression and divide by COUNT(*).

Top 3 books with >10 copies by total lending time

Find the top 3 books (by book_id and title) that:

  • have more than 10 total copies (books.total_copies > 10)
  • have the longest total lending time across all their loans

Define a loan's lending time in days as: (COALESCE(return_date, DATE '2025-06-01') - checkout_date) So if a loan has not been returned yet, assume it is lent through 2025-06-01.

Return book_id, title, and total_lend_days (sum of lending time across all loans for that book), ordered by total_lend_days descending and limit to 3 rows.

Table books

columntypeconstraints
book_idINTPRIMARY KEY, NOT NULL
titleVARCHAR(200)NOT NULL
total_copiesINTNOT NULL
conditionVARCHAR(20)NOT NULL

Sample data:

book_idconditiontitletotal_copies
1GoodSQL Basics12
2GoodData Science 10120
3GoodHistory of Rome15
4GoodRare Manuscript2
5DamagedDamaged Book30
6GoodPython for Data11

Table members

columntypeconstraints
member_idINTPRIMARY KEY, NOT NULL
member_nameVARCHAR(100)NOT NULL
referrer_member_idINT

Sample data:

member_idmember_namereferrer_member_id
1AliceNULL
2Bob1
3Cara1
4Dan2
5Eva2
6Finn3

Table loans

columntypeconstraints
loan_idINTPRIMARY KEY, NOT NULL
book_idINTNOT NULL
member_idINTNOT NULL
checkout_dateDATENOT NULL
due_dateDATENOT NULL
return_dateDATE
renew_countINTNOT NULL

Sample data:

book_idcheckout_datedue_dateloan_idmember_idrenew_countreturn_date
12025-05-012025-05-15101112025-05-20
12025-05-252025-06-0810223NULL
22025-04-012025-04-15103322025-05-15
22025-05-202025-06-0310441NULL
32025-03-012025-03-15105502025-04-15
32025-05-102025-05-2410614NULL
52025-01-012025-01-15107212025-05-01
62025-05-282025-06-1110830NULL
62025-02-012025-02-15109412025-03-01
22025-02-102025-02-24110202025-02-20
12025-01-102025-01-24111502025-01-25

Table reservations

columntypeconstraints
reservation_idINTPRIMARY KEY, NOT NULL
member_idINTNOT NULL
book_idINTNOT NULL
reservation_dateDATENOT NULL

Sample data:

book_idmember_idreservation_datereservation_id
212025-01-05201
312025-02-07202
112025-03-10203
612025-04-12204
412025-05-01205
122025-03-03206
232025-01-15207
632025-02-16208
332025-05-20209
132025-05-25210
252025-01-20211
352025-01-22212
652025-02-10213
152025-03-08214
552025-04-01215
452025-05-10216
252025-05-30217
362025-02-25218
662025-03-18219

Expected output

Three rows: the books with total_copies > 10 having the highest total_lend_days across all loans (including ongoing loans counted through 2025-06-01).

book_idtitletotal_lend_days
5Damaged Book120
3History of Rome67
2Data Science 10166

Hints

  1. Use COALESCE to treat unreturned loans as ending on 2025-06-01.
  2. Aggregate lending time per book and then ORDER BY the sum.

Member–referrer pair with the largest reservation-count difference

Each member may have been referred by another member (members.referrer_member_id).

For every member that has a referrer, compute:

  • reservations made by the member
  • reservations made by the referrer
  • the absolute difference between those two counts

Return the single member–referrer pair with the greatest difference, along with the difference.

Output columns: member_id, member_name, referrer_member_id, referrer_name, reservation_diff

If multiple pairs tie for greatest difference, return the one with the smallest member_id.

Table books

columntypeconstraints
book_idINTPRIMARY KEY, NOT NULL
titleVARCHAR(200)NOT NULL
total_copiesINTNOT NULL
conditionVARCHAR(20)NOT NULL

Sample data:

book_idconditiontitletotal_copies
1GoodSQL Basics12
2GoodData Science 10120
3GoodHistory of Rome15
4GoodRare Manuscript2
5DamagedDamaged Book30
6GoodPython for Data11

Table members

columntypeconstraints
member_idINTPRIMARY KEY, NOT NULL
member_nameVARCHAR(100)NOT NULL
referrer_member_idINT

Sample data:

member_idmember_namereferrer_member_id
1AliceNULL
2Bob1
3Cara1
4Dan2
5Eva2
6Finn3

Table loans

columntypeconstraints
loan_idINTPRIMARY KEY, NOT NULL
book_idINTNOT NULL
member_idINTNOT NULL
checkout_dateDATENOT NULL
due_dateDATENOT NULL
return_dateDATE
renew_countINTNOT NULL

Sample data:

book_idcheckout_datedue_dateloan_idmember_idrenew_countreturn_date
12025-05-012025-05-15101112025-05-20
12025-05-252025-06-0810223NULL
22025-04-012025-04-15103322025-05-15
22025-05-202025-06-0310441NULL
32025-03-012025-03-15105502025-04-15
32025-05-102025-05-2410614NULL
52025-01-012025-01-15107212025-05-01
62025-05-282025-06-1110830NULL
62025-02-012025-02-15109412025-03-01
22025-02-102025-02-24110202025-02-20
12025-01-102025-01-24111502025-01-25

Table reservations

columntypeconstraints
reservation_idINTPRIMARY KEY, NOT NULL
member_idINTNOT NULL
book_idINTNOT NULL
reservation_dateDATENOT NULL

Sample data:

book_idmember_idreservation_datereservation_id
212025-01-05201
312025-02-07202
112025-03-10203
612025-04-12204
412025-05-01205
122025-03-03206
232025-01-15207
632025-02-16208
332025-05-20209
132025-05-25210
252025-01-20211
352025-01-22212
652025-02-10213
152025-03-08214
552025-04-01215
452025-05-10216
252025-05-30217
362025-02-25218
662025-03-18219

Expected output

One row showing the member and their referrer with the maximum absolute difference in reservation counts, plus that difference.

member_idmember_namereferrer_member_idreferrer_namereservation_diff
5Eva2Bob6

Hints

  1. Pre-aggregate reservation counts per member with a LEFT JOIN so members with 0 reservations still appear.
  2. Join each member to their referrer and take ABS of the two counts.