1

I have a place ('place') where I can have some boxes ('box'). In each box, I can have items ('item'). I can also have items in the place outside of a box. I want to delete a place, and by so, to delete each boxes in the place, including items in each boxes as well as the items in the place but outside any box. I got it working with the following separated statements:

1: select the place

SELECT placeid, placename 
FROM place 
WHERE userid = (SELECT id 
                FROM user 
                WHERE username = 'username' AND password ='password') 
  AND placename = 'placename'

2: select the boxes in that place

SELECT boxid, boxname 
FROM box 
WHERE placeid = 'id'

3a: for each box, delete the items in it

DELETE FROM item 
WHERE boxid = 'boxid'

3b: then delete the box

DELETE FROM box 
WHERE id = 'boxid'

4: delete the items in the place not in a box

DELETE FROM item 
WHERE placeid = 'placeid' AND boxid = 0

5: delete the place

DELETE FROM place 
WHERE id = placeid

This is working but of course this is slow. The error management is also not that nice to work with.

Is there a way of doing all this with fewer SQL statements or even only one?

I got of course the same question for deleting a box in one statement!

edit: mysql with php

Thanks a lot.

pm200107
  • 303
  • 1
  • 11
  • 1
    Which [DBMS](https://en.wikipedia.org/wiki/DBMS) product are you using? Postgres? Oracle? "SQL" is just a query language, not the name of a specific database product. –  Dec 07 '17 at 20:25
  • 1
    You could incorporate the initial selects into each delete statement, but you can only delete from one table at a time. If you wanted to add referential integrity, you could delete the parent and have the system cascade the delete to the child records, but that might not change the performance very much. – D Stanley Dec 07 '17 at 20:26
  • Good reference for delete cascade https://stackoverflow.com/questions/2914936/mysql-foreign-key-constraints-cascade-delete – Aaron Dietz Dec 07 '17 at 20:30
  • performance is one thing, error management is another one, for each single delete. no? – pm200107 Dec 07 '17 at 20:30
  • @pm200107 Not much error management to worry about if you let the system do the work. `ON DELETE CASCADE` accomplishes that. – Aaron Dietz Dec 07 '17 at 20:36

2 Answers2

1

Because item and box both have a placeid (per the info in your question) you can simplify this task a bit, but you still need 3 DELETE statements:

SELECT p.placeid
INTO #PlacesToDelete
FROM place p 
INNER JOIN [user] u ON p.userid = u.id
WHERE u.username = 'username'
    AND u.[password] = 'password'
    AND p.placename = 'placename'

DELETE i
FROM item i
INNER JOIN #PlacesToDelete ptd ON i.placeid = ptd.placeid

DELETE b
FROM box b
INNER JOIN #PlacesToDelete ptd ON b.placeid = ptd.placeid

DELETE p
FROM place p
INNER JOIN #PlacesToDelete ptd ON p.placeid = ptd.placeid

Or, if this is related to MySQL, you should be able to further simplify and use a single statement:

DELETE p, b, i
FROM place p 
INNER JOIN [user] u ON p.userid = u.id
INNER JOIN box b ON b.placeid = p.placeid
INNER JOIN item i ON i.placeid = p.placeid
WHERE u.username = 'username'
    AND u.[password] = 'password'
    AND p.placename = 'placename'

Edit: Based on the comment if an item is in a box, placeid is equal to 0 because it belongs to a box, not a place, here's an updated query that will delete everything you want:

DELETE i1, i2, b, p
FROM place p 
INNER JOIN [user] u ON p.userid = u.id
INNER JOIN box b ON b.placeid = p.placeid
INNER JOIN item i1 ON i1.placeid = p.placeid
INNER JOIN item i2 ON i2.boxid = b.boxid
WHERE u.username = 'username'
    AND u.[password] = 'password'
    AND p.placename = 'placename'
digital.aaron
  • 5,435
  • 2
  • 24
  • 43
  • No problem, @pm200107. If my answer worked for you, I'd appreciate it if you could mark my answer as "accepted". – digital.aaron Dec 07 '17 at 21:26
  • this one in fact doesnt work as it will cancel the place, the boxes for that place and the items for that place, BUT not the items inside the boxes for that place – pm200107 Dec 07 '17 at 23:40
  • The item table has a placeid. When the item.placeid matches the place.placeid, it doesn't matter what boxid the item has. – digital.aaron Dec 07 '17 at 23:47
  • sure, but it doesnt delete items in boxes. if an item is in a box, placeid is equal to 0 because it belongs to a box, not a place. So this answer is incomplete – pm200107 Dec 08 '17 at 20:49
1

You should try ON DELETE CASCADE, it would look like this:

ALTER TABLE box ADD CONSTRAINT fk_box_place_id 
                FOREIGN KEY (placeid) 
                REFERENCES place(id) 
                ON DELETE CASCADE;

ALTER TABLE item ADD CONSTRAINT fk_item_place_id 
                 FOREIGN KEY (placeid) 
                 REFERENCES place(id) 
                 ON DELETE CASCADE;

I don't think you need any other cascades, since placeid is available in all tables and will cover items both in and out of boxes, but maybe I'm overlooking something.

Once set up, all you do is delete the place you want to delete and the rest is handled for you:

DELETE 
FROM place
WHERE placeID = 'deletedPlaceID';
Aaron Dietz
  • 10,137
  • 1
  • 14
  • 26
  • thanks. As I am not familiar with the ON DELETE CASCADE: I alter the table in MySQL first to set it up. Then once the tables are altered, in my script, I can only delete the place table? or do I have to alter the table every once I need to delete the table place? – pm200107 Dec 07 '17 at 21:18
  • against the solution provided by digital.aaron (@digital.aaron thanks for that), which solution would be best for performance? – pm200107 Dec 07 '17 at 21:19
  • @pm200107 You just need to alter the table once. You probably already have FKs set up for `placeid` but if not it is a good idea to have them anyway. It will prevent `placeid`s being inserted into other tables unless they exist in `place`. Then yes, all you do is delete the `placeid` from `place` and the system will automatically remove any rows that reference that `placeid` from `box` and `item`. You should check that my FKs make sense to you first, since you know your system best, but I believe the able will do the trick. – Aaron Dietz Dec 07 '17 at 21:21
  • @pm200107 Performance wise they are likely identical. Using cascaded deletes just takes a bit of the possibility for user error out of the equation. – Aaron Dietz Dec 07 '17 at 21:22
  • ok. Thanks. For 'readiness' I used placeid as the primary key in this post. In reality, the i have 'id' in the place table (as well as 'id' and 'placeid' in the box table and 'id' and 'placeid' in the item table). Does it change something (sorry for the dumb question) – pm200107 Dec 07 '17 at 21:24
  • @pm200107 The syntax is `FOREIGN KEY (local table column) REFERENCES foreign table (foreign table column) ON DELETE CASCADE` .... It sounds like you want `FOREIGN KEY (placeid) REFERENCES place (id) ON DELETE CASCADE` .. You can also optionally name the index created by the key. Read this http://www.mysqltutorial.org/mysql-on-delete-cascade/ – Aaron Dietz Dec 07 '17 at 21:29
  • is there a limitation based on the mySQL version? #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(columns, FOREIGN KEY (placeid) REFERENCES place (id) ON DELETE CASCADE)' at line 1 – pm200107 Dec 07 '17 at 21:45
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/160749/discussion-between-aaron-dietz-and-pm200107). – Aaron Dietz Dec 07 '17 at 21:46
  • my mysql server does not allow on delete cascade. I cant use this answer. I'd like to thank Aaron for the time he spent with me. – pm200107 Dec 07 '17 at 23:15