Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Deleting Multiple MySQL Rows With DBI

by Red Neckerson (Novice)
on May 22, 2001 at 23:21 UTC ( [id://82353]=perlquestion: print w/replies, xml ) Need Help??

Red Neckerson has asked for the wisdom of the Perl Monks concerning the following question:

I would like to delete multiple rows in an mySQL table based on message "ids" I have in an array, @MESSAGE_IDS. The MESSAGE_ID array would have message id numbers (13, 14, 15, 19, 25, 44, etc...), each of which identify messages I would like to delete.

I would like to delete all of them with one Select and prepare statement, in "one fell swooop" if I can. My first reaction was to use a while loop like this:

while $i (@MESSAGE_IDS){ my $sth = $dbh->prepare("DELETE * FROM table WHERE message_id = '$message_id'"); $sth->execute(); # and so on ... }

But apparently calling prepare and execute over and over is not a good way of deleting multiple rows. Is there a better dbi function or function to accomplish what I want without calling prepare and execute in a loop? Red------

Replies are listed 'Best First'.
Re: Deleting Multiple MySQL Rows With DBI
by lhoward (Vicar) on May 22, 2001 at 23:27 UTC
    The most efficient way is probably to so something like this:
    my $sth = $dbh->prepare("DELETE FROM table WHERE message_id = ?"); foreach $i (@MESSAGE_IDS){ $sth->execute($i); }
    Since the query is always the same, you only need to prepare it once. I suspect that doing it in one query whith an "or" or "in" using the entire list of numbers at once (depending on how many you're deleting) may not be well optomized by the server, and hence slower.
      I don't see why using 'or' or 'in' would be slower, unless there's something about MySQL in particular I don't know about (which is entirely possible). It should be quicker since there's only one call to the database instead of many, and it should still be able to take advantage of any existing indexes.
        In general DB servers aren't good at optomizing "in" or "or" queries. The server may end up doing a table scan to locate all the items to delete, which could be really slow. By doing each ID individualy you're guaranteed that each one will go directly to that item (assuming there's an index on the ID col) and delete it. True you are executing more statments, but I suspect that in many situations it could be quicker than the poorly optomized "in" statment.

        Benchmarks would be interesting ;)

      All of these worked, thanks. I had to take out the DELETE *, apparently, you just say DELETE FROM TABLE, and you have to use the WHERE clause to narrow down the columns. Red------
Re: Deleting Multiple MySQL Rows With DBI
by Beatnik (Parson) on May 22, 2001 at 23:34 UTC
    Purely SQL speaking :
    $elements = join('","',@MESSAGE_IDS); $elements = '"'.$elements.'"'; my $sth = $dbh->prepare("DELETE * FROM table WHERE message_id in ($ele +ments)"); $sth->execute();
    Greetz
    Beatnik
    ... Quidquid perl dictum sit, altum viditur.
Re: Deleting Multiple MySQL Rows With DBI
by mr.nick (Chaplain) on May 22, 2001 at 23:28 UTC
    Depending on the size of @MESSAGE_IDS: if it's a relatively low number, you could always do something like:
    my $sth=$dbh->prepare("DELETE FROM table WHERE ". join(" or ",map { "message_id = $_" } @MESSAGE_IDS));
    If @MESSAGE_IDS is really large, consider breaking it up into smaller arrays when doing the above on each slice. Something like:
    while (@MESSAGE_IDS) { my @todel=splice @MESSAGE_IDS,0,10; my $sth=$dbh->prepare("DELETE FROM table WHERE ". join(" or ",map { "message_id = $_" } @todel)); $sth->execute; }
    Update: Or listen to lhoward :) He's got the best answer, I think. I also didn't realize you could do what beatnik suggested. I guess my SQL is off.
Re: Deleting Multiple MySQL Rows With DBI
by runrig (Abbot) on May 22, 2001 at 23:29 UTC
Re: Deleting Multiple MySQL Rows With DBI
by Hero Zzyzzx (Curate) on May 22, 2001 at 23:45 UTC

    Why not just glom all your ids into one DBI statement, so that it looks like:

    delete * from table where (id=1 or id=2 or id=3)

    Example, extremely untested code:

    $query = "delete from table where id=("; for(@MESSAGE_IDS){ # you may need a "chomp" in here $query.=$_." or id="; } #Delete "or" off the back $query=substr($query,0,-7)); #close the query $query.=")"; my $sth= $dbh->prepare($query); $sth->execute;

    I wouldn't use this for huge deletes, but it should serve your purposes. If the "id" column is indexed, this should be pretty damn fast.

Re: Deleting Multiple MySQL Rows With DBI
by Odud (Pilgrim) on May 22, 2001 at 23:42 UTC
    You should prepare the statement once outside the loop and then bind and execute it inside the loop
    my $sth = $dbh->prepare("DELETE * FROM table WHERE message_id = ?"); while $i (@MESSAGE_IDS){ $sth->bind($i) $sth->execute(); # and so on ... }
    Check the perldoc for DBI - this is off the top of my head.
(dkubb) Re: (2) Deleting Multiple MySQL Rows With DBI
by dkubb (Deacon) on May 24, 2001 at 05:35 UTC

    Remember, you only need to prepare a statement handle if you are going to re-use it for more than 1 query. Otherwise, you can use the Database Handle Methods for one-time queries. They encapsulate a prepare, execute, and sometimes a fetch-type method in a single call. For INSERT or UPDATE statements use DBI::do.

    The following statement does a batch DELETE without using any 'Synthetic' Code, and is tested to work with MySQL:

    $dbh->do( sprintf( 'DELETE FROM table WHERE message_id IN(%s)', join ',', ('?') x @MESSAGE_IDS ), {}, @MESSAGE_IDS, );

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://82353]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (4)
As of 2024-04-25 19:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found