开发者

A multitude of the same id in an WHERE id IN () statement

开发者 https://www.devze.com 2023-02-19 21:11 出处:网络
I have a simple query that increases the value开发者_C百科 of a field by 1. Now I used to loop over all id\'s and fire a query for each of them, but now that things are getting a bit resource heavy I

I have a simple query that increases the value开发者_C百科 of a field by 1. Now I used to loop over all id's and fire a query for each of them, but now that things are getting a bit resource heavy I wanted to optimize this. Normally I would just do

UPDATE table SET field = field + 1 WHERE id IN (all the ids here)

but now I have the problem that there are id's that occur twice (or more, I can't know that on forehand). Is there a way to have the query run twice for id 4 if the query looks like this:

UPDATE table SET field = field + 1 WHERE id IN (1, 2, 3, 4, 4, 5)

Thanks,

lordstyx

Edit: sorry for not being clear enough. The id here is an auto inc field, so it are all unique ID's. the id's that have to be updated are indirectly comming from users, so I can't predict which id is going to occur how often. If there are the ID's (1, 2, 3, 4, 4, 5) I need the field of row with id 4 to be incremented with 2, and all the rest with 1.


If (1, 2, 3, 4, 4, 5) comes from a SELECT id ... query, then you can do something like this:

UPDATE yourTable 
  JOIN 
    ( SELECT id
           , COUNT(id) AS counter 
      ....
      GROUP BY id
    ) AS data
    ON yourTable.id = data.id
SET yourTable.field = yourTable.field + data.counter 
;

Since the input comes from users, perhaps you can manipulate it a bit. Change (1, 2, 3, 4, 4, 5) to (1), (2), (3), (4), (4), (5).

Then (having created a temporary table):

CREATE TABLE tempUpdate
( id INT )
;

Do the following procedure:

  1. add the values in the temporary table,
  2. run the update and
  3. delete the values.

Code:

INSERT INTO TempUpdate
VALUES (1), (2), (3), (4), (4), (5)
;

UPDATE yourTable 
  JOIN 
    ( SELECT id
           , COUNT(id) AS counter 
      FROM TempUpdate
      GROUP BY id
    ) AS data
    ON yourTable.id = data.id
SET yourTable.field = yourTable.field + data.counter 
;

DELETE FROM TempUpdate
;


No. But you could perform something like

UPDATE table
   SET field = field + (LENGTH(',1,2,3,4,4,5,') - LENGTH(REPLACE(',1,2,3,4,4,5,', CONCAT(',', id, ','), ''))) / LENGTH(CONCAT(',', id, ','))
 WHERE id IN (1, 2, 3, 4, 4, 5)

if you need row with id = 4 specifically to be incremented twice


Here is solution you wanted, but I'm not sure this is what you need.

Let's say that your talbe is called test. You want to increase id. I've added a field idwas to easily show what was the id before the query:

CREATE TABLE `test` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `idwas` int(8) unsigned default NULL,
  PRIMARY KEY  (`id`)
) ;

Let's fill it with data:

truncate table test;
insert into test(id) VALUES(1),(3),(15);
update test set idwas = id;

Now let's say that you have user input 1,3,5,3, so:

  • id 1 should be increased by 1
  • id 3 should be increased by 2
  • id 5 is missing, nothing to increase.
  • row with id 15 should not be changed because not in user input

We'll put the user input in a variable to be easier to use it:

SET @userInput = '1,3,5,3';

then do the magic:

SET @helperTable = CONCAT(
  'SELECT us.id, count(us.id) as i FROM  ',
  '(SELECT ',REPLACE(@userInput, ',',' AS `id` UNION ALL SELECT '),
  ') AS us GROUP BY us.id');

SET @stmtText = CONCAT(
  ' UPDATE  ',
  '(',@helperTable,') AS h INNER JOIN test as t ON t.id = h.id',
  ' SET t.id = t.id + h.i');

PREPARE stmt FROM @stmtText;

EXECUTE stmt;

And this is the result:

mysql> SELECT * FROM test;
+----+-------+
| id | idwas |
+----+-------+
|  2 |     1 |
|  5 |     3 |
| 15 |    15 |
+----+-------+
3 rows in set (0.00 sec)


If it's reasonable, you could try doing a combination of what you had before and what you have now.

In whatever is creating this list, separate it into (depending on the language's constructs) some type of array. Follow this by sorting it,finding how many multiples of each there are, and doing whatever else you need to to get the following: an array with (increment-number => list of ids), so you do one query for each increment amount. Thus, your example becomes

UPDATE table SET field = field + 1 WHERE id IN (1, 2, 3, 5)
UPDATE table SET field = field + 2 WHERE id IN (4)

In php, for example, I would take the array, sort the array, use the content of the array as the keys for another array of the form (id => count), and then fold that over into the (count => list of ids) array.

It's not that efficient, but is definitely better than one query per id. It's also probably better than using iteration and string manipulation in SQL. Unless you're forced to use SQL to do everything (which it sounds like you're not), I wouldn't use it to do everything, when it's overly awkward to do so.


You could use the following:

create temporary table temp1 (id integer);
insert into temp1 (id) values (1),(2),(3),(4),(4),(5);
update your_table set your_field = your_field + (select count(*) from temp1 where id = your_table.id)

This solution requires you to format the id list like (1),(2),(3),(4),(4),(5) but I don't think that is a problem, right? This worked on my test database, hope it works for you too!

Regards, Arthur

0

精彩评论

暂无评论...
验证码 换一张
取 消