views:

30

answers:

2

Is there a way, when you have a list of records, to check if each of these records have foreign key references before you attempt to delete any of these records?

As an example, if I have a list of borrowers and a list of books, you should not be able to delete a borrower from the system if he still has books on loan. (My actual system is much more complicated than that however - a lot more tables.)

I would like to remove the delete option from any borrowers that have books on loan (in that example).

If I try to delete a record with foreign key references, I get an error to the effect of:

Database access failed: Cannot delete or update a parent row: a foreign key constraint fails (dbname.tablename, CONSTRAINT fkfieldid FOREIGN KEY (fieldid) REFERENCES tablename (fieldid))

One solution is to write a query to check if each record, in a list of records, has any foreign key references in any of the possible tables it could be referenced.

However, if I wish to display a list of 100 records from a table in my content management system, and I have to run 100 sub-queries in order to display that list, it is obviously very inefficient!

End users become confused when they try to delete a record but they can't because that data is 'in use' elsewhere, so I would rather remove the option to delete to avoid confusion.

Any ideas on what would be the best thing to do? Thanks.

+1  A: 

You can easily do this using either a sub-query or a join.

ie. sub-query

SELECT B.*, (SELECT COUNT(*) FROM loans L WHERE L.book_id = b.id) loan_count
FROM books B

or join (note that if you allow multiple simultaneous loans, such a book will appear more than once in the results):

SELECT B.*, L.book_id AS loaned_out_if_not_null
FROM books B
    LEFT JOIN loans L ON B.id = L.book_id

This can of course be shortened down with a GROUP BY:

SELECT B.id, B.name, COUNT(L.book_id) AS loan_count
FROM books B
    LEFT JOIN loans L ON B.id = L.book_id
GROUP BY B.id, B.name

If your database engine has support for CASE, you can avoid the multiple rows in the result by combining that with DISTINCT (of course, DISTINCT also has overhead):

SELECT DISTINCT B.*,
    CASE WHEN L.book_id IS NOT NULL THEN 1 ELSE 0 END AS loaned_out_if_one
FROM books B
    LEFT JOIN loans L ON B.id = L.book_id

Of these, I would go with the GROUP BY variant.

Lasse V. Karlsen
Joins risk duplicating records; EXISTS is a better choice
OMG Ponies
This is the type of solution I began looking at, but instead what I have done is to create queries that check each table that has a foreign key for the field in question. I then join those queries using UNION, and do a left outer join with the table containing the primary key. I am hoping that this solution will not be too inefficient.
Stevio
+1  A: 

What you maybe like are transactions.

1) Delete all entries.

2a) If any error occured -> Rollback 2b) If no error occured -> Commit

http://dev.mysql.com/doc/refman/5.0/en/commit.html

JochenJung