Everyone familiar with php's mysql_query command, knows that it doesn't allow us to perform more than one query (separated by the ; delimiter) in one call...
My problem is that I wan't to be able to define a user variable in my UPDATE query so that I increment it for each row (kinda like auto increment).
It should look like something as this:
SET #t1=0;
UPDATE `mytable` SET `order` = (#t1:=(#t1+1)) ORDER BY `order` ASC;
My problem is that, since I can't define my variable and then make the update, I can't find a way to set the variable inside the query.
I've tried to define it if it was NULL:
... `order` = (IFNULL( #t1 := ( #t1 + 1 ) , #t1 := 0 )) ...
but it didn't worked since the variable resets at each row it works on.
Anyone familiar with mysql that see's a solution?
Thanks in advance.
Old question but here's an answer anyway:
UPDATE `mytable` SET `order` = (#t1 := IFNULL(#t1, 0) + 1) ORDER BY `order` ASC;
IFNULL(#t1, 0) returns 0 if #t1 doesn't have a value or returns the value of #t1 if it has a value.
So on the first row #t1 is not set and it updates as order = (#t1 := 0 + 1) and on the following rows #t1 already has a value and adds +1 each row.
You could use the mysqli library, it allows for multiple querys in one query using the
mysqli->multiple_query( string $querys);
http://us.php.net/mysqli_multi_query
Going out on a limb, how about...
... `order` = (SELECT `order`+1 FROM `mytable` ORDER BY `order` DESC LIMIT 1)
or something like that as a subquery?... I'm not sure whether the subquery re-runs after each update, but if it does, it should select the previously highest order value and increment it?
Related
I have a long list from database which i want to update a field by name group. i want to update the first 30 to value '1' next 30 to value '2' and so on until the end
Any idea on how to go about it
$sql = mysqli_query($conn,
"UPDATE `tablename` SET `group` = '$value' WHERE id IN (SELECT id FROM tablename ORDER BY id desc LIMIT 0, 30)");
You have run into this limitation of mysql
You cannot update a table and select from the same table in a
subquery.
You could use an update join, but since limits are involved, you would once again run into the same issue. Since only a small number of records are involved, you could do a simple query to get the ids.
SELECT GROUP_CONCAT(id) FROM tablename ORDER BY id desc LIMIT 0, 30
Then use that in the next query
UPDATE `tablename` SET `group` = '$value' WHERE id IN (ids_from_above_query)
If you are worried about concurrent updates, start a transaction.
If your update is a one-time thing (and don't need to optimize it, e.g. do it all in one query), you can simply repeat the following query until you are finished:
update `tablename`
SET `group` = '$value'
where `group` is null
order by id desc
limit 30;
All group-values have to be null (or some other unused value) to begin with, so e.g. use update tablename SET group = null first, if they aren't.
And you probably want to do it all in a transaction.
I've a classic pagination system using LIMIT startrecord, endrecord and I want to figure out in what page number an X record is located.
The only idea I've right now is to seek recursively all the records to find it out. But I'm looking for a much more "economic" method!
Any ideas ?
You could use a sub query to create a table with the results and their position, then query that for the specific entry you are looking at:
SET #rank=0;
SELECT rank, record
FROM (
SELECT
#rank:=#rank+1 AS rank,
record
FROM table
) as subquery
WHERE record = x;
The returned table would show the record an the rank it appeared in the original query. You can the divide the rank by the number of results per page... Or build it into the query. Hope this helps.
Cout the number of records that are prior to the one you are looking for. This requires you to assume an order for your query which is natural.
SELECT COUNT(id) AS c
FROM tbl
WHERE sort_field < ((SELECT sort_field FROM tbl WHERE id = 18))
OR (sort_field = ((SELECT sort_field FROM tbl WHERE id = 18)) AND id < 18);
Then just retrieve the c and calculate ceilling(c/page_size). This will give you the page number that your record will fall in. The only important thing to remember is that you need to sort the records in the same order as you would in your query with limit.
To describe what the query does, it counts the number records that stand before the record with id 18. The only tricky part is with records with the same value as for their sort_field in which MySQL will make use of primary key and in our case the id. And that's why we have the OR part in our condition. In my answer I'm assuming you are sorting your original query (with limit statement in it) ascending, but if you are sorting descending then you need to change all of < to >.
Use something like this with your query as part of the s subselect
SELECT s.row, s.RECORD, YOUR_OTHER_FIELDS...
FROM (SELECT #row := 0) cnt
JOIN (SELECT #row := #row + 1 row, RECORD, ...YOUR QUERY WITH ORDER BY ...) s
WHERE s.RECORD = <desired record number>
and divide row by the pagesize from your pagination.
Concrete but nonsensical example:
SELECT p.row, p.id
FROM (SELECT #row := 0) cnt
JOIN (SELECT #row := #row + 1 row, id FROM products ORDER BY id desc) p
WHERE p.id = 485166
As intended, the value of row changes with the order you use in the subselect.
It folds the variable initialization into the query so this is only one statement.
It also does not depend on a natural order or distribution of rows - as long as the order they ARE returned in stays the same for whatever ORDER you specify (or leave out).
if this is something that you will use often, i think it is a good idea to create an stored procedure or a function. We can use a cursor inside, to iterate through the results and get the position of the desired item. I think this will be faster, it wont have to iterate to all the records, and dont need a subquery (for all this i would say that it is more economic) and you can use order, join, and whatever you need.
DELIMITER $$
CREATE FUNCTION position ( looking_for INT )
RETURNS INT
READS SQL DATA
BEGIN
-- First we declare all the variables we will need
DECLARE id INT;
DECLARE pos INT;
SET pos=0;
-- flag which will be set to true, when cursor reaches end of table
DECLARE exit_loop BOOLEAN;
-- Declare the sql for the cursor
DECLARE pos_cursor CURSOR FOR
SELECT id
FROM your_table
--you can use where, join, group by, order and whatever you need
--end of query
-- Let mysql set exit_loop to true, if there are no more rows to iterate
DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;
-- open the cursor
OPEN example_cursor;
-- marks the beginning of the loop
example_loop: LOOP
-- read the id from next row into the variable id
FETCH pos_cursor INTO id;
-- increment the pos var
SET pos=pos+1;
-- check if we found the desired item,
-- if it has been set we close the cursor and exit
-- the loop
IF id=looking_for THEN
CLOSE example_cursor;
LEAVE example_loop;
END IF;
-- check if the exit_loop flag has been set by mysql,
-- if it has been set we close the cursor and exit
-- the loop
IF exit_loop THEN
CLOSE example_cursor;
LEAVE example_loop;
END IF;
END LOOP example_loop;
RETURN pos;
END $$
DELIMITER ;
You create the function just once, and for using it, you just need to use this sql:
CALL position(ID_OF_THE_ITEM_YOU_ARE_LOOKING_FOR);
and it returns the position of the item, in the position [0][0] of the returned rowset.
Of course instead of the id you can create a function that compares the name, or any other field, or even more than one.
If the query is always diferent, then you cannot use a function, but you can still use the cursor (the syntax will be the same). You can build the cursor in your PHP, let pos be a System variable (using #pos), and in any case just add the specific sql of the query (the part between DECLARE pos_cursor CURSOR FOR and --end of query)
You can't really create an "economic" way. You have to get the full list of records from the DB since there is no way to know the position of a record from MySQL.
Depending on your sorting, the frequency at which the data changes, you could assign the record its position in a column: add column position to the table you are querying. That might not be feasible in all cases.
I have tried to set the max value for the particular column but that is not working for me. I do not know where i'm going wrong.
UPDATE `upload_video`
SET order_id ='select max(order_id)+1
FROM upload_video'
WHERE `video_id` = 22
This is my query i run the select max(order_id)+1 from upload_video query separately which is giving the result. But if i use this query in update query, the query is executing without error. But the order_id is not updating properly. please help me
Your query is almost correct in standard SQL, you only need to use brackets () instead of apostrophe ':
SET order_id = (SELECT MAX(...) ...)
but MySQL doesn't allow you to update a table while selecting from the same table, a workaround is to use a subquery that calculates the value that you need, and to join your subquery with the table you need to update:
UPDATE
upload_video JOIN (SELECT COALESCE(MAX(order_id),0)+1 max_id
FROM upload_video) s
SET
upload_video.order_id=s.max_id
WHERE
video_id=22
Please see fiddle here.
You have a typo in the statement, you used UPADTE instead of UPDATE.
One problem is, don't quote the subquery. You have used single quotes, which means the expression select max(order_id)+1... was interpreted as a text literal (a varchar). But you clearly don't want that (I guess order_id is a number). What you want instead is to evaluate the subquery. However, if you try:
UPDATE `upload_video`
SET order_id =(select max(order_id)+1
FROM upload_video)
WHERE `video_id` = 22
then MySQL doesn't allow it (I didn't know about that). Other databases such as PostgreSQL allow it. So you might need two statements:
select #id = coalesce(max(order_id), 0) + 1 FROM upload_video;
UPDATE `upload_video` SET order_id = #id WHERE `video_id` = 22;
Please note this works in MySQL but not in other databases.
Try this:
UPDATE `upload_video`
SET order_id =(select COALESCE(max(U2.order_id),0)+1
FROM upload_video U2)
WHERE `video_id` = 22
Peraphs this query goes in error because MySql doesn't want to use the same table in UPDATE and in subquery.
If your case please write two queries.
The first get the maximum value, the second does update
I am trying to update fields in my DB, but got stuck with such a simple problem: I want to update just one row in the table with the biggest id number. I would do something like that:
UPDATE table SET name='test_name' WHERE id = max(id)
Unfortunatelly it doesnt work. Any ideas?
Table Structure
id | name
---|------
1 | ghost
2 | fox
3 | ghost
I want to update only last row because ID number is the greatest one.
The use of MAX() is not possible at this position. But you can do this:
UPDATE table SET name='test_name' ORDER BY id DESC LIMIT 1;
For multiple table, as #Euthyphro question, use table.column.
The error indicates that column id is ambiguous.
Example :
UPDATE table1 as t1
LEFT JOIN table2 as t2
ON t2.id = t1.colref_t2
SET t1.name = nameref_t2
ORDER BY t1.id DESC
LIMIT 1
UPDATE table SET name='test_name' WHERE id = (SELECT max(id) FROM table)
This query will return an error as you can not do a SELECT subquery from the same table you're updating.
Try using this:
UPDATE table SET name='test_name' WHERE id = (
SELECT uid FROM (
SELECT MAX(id) FROM table AS t
) AS tmp
)
This creates a temporary table, which allows using same table for UPDATE and SELECT, but at the cost of performance.
I think iblue's method is probably your best bet; but another solution might be to set the result as a variable, then use that variable in your UPDATE statement.
SET #max = (SELECT max(`id`) FROM `table`);
UPDATE `table` SET `name` = "FOO" WHERE `id` = #max;
This could come in handy if you're expecting to be running multiple queries with the same ID, but its not really ideal to run two queries if you're only performing one update operation.
UPDATE table_NAME
SET COLUMN_NAME='COLUMN_VALUE'
ORDER BY ID
DESC LIMIT 1;
Because you can't use SELECT IN DELETE OR UPDATE CLAUSE.ORDER BY ID DESC LIMIT 1. This gives you ID's which have maximum value MAX(ID) like you tried to do. But MAX(ID) will not work.
Old Question, but for anyone coming across this you might also be able to do this:
UPDATE
`table_name` a
JOIN (SELECT MAX(`id`) AS `maxid` FROM `table_name`) b ON (b.`maxid` = a.`id`)
SET a.`name` = 'test_name';
We can update the record using max() function and maybe it will help for you.
UPDATE MainTable
SET [Date] = GETDATE()
where [ID] = (SELECT MAX([ID]) FROM MainTable)
It will work the perfect for me.
I have to update a table with consecutive numbers.
This is how i do.
UPDATE pos_facturaciondian fdu
SET fdu.idfacturacompra = '".$resultado["afectados"]."',
fdu.fechacreacion = '".$fechacreacion."'
WHERE idfacturaciondian =
(
SELECT min(idfacturaciondian) FROM
(
SELECT *
FROM pos_facturaciondian fds
WHERE fds.idfacturacompra = ''
ORDER BY fds.idfacturaciondian
) as idfacturaciondian
)
Using PHP I tend to do run a mysqli_num_rows then put the result into a variable, then do an UPDATE statement saying where ID = the newly created variable. Some people have posted there is no need to use LIMIT 1 on the end however I like to do this as it doesn't cause any trivial delay but could prevent any unforeseen actions from being taken.
If you have only just inserted the row you can use PHP's mysqli_insert_id function to return this id automatically to you without needing to run the mysqli_num_rows query.
Select the max id first, then update.
UPDATE table SET name='test_name' WHERE id = (SELECT max(id) FROM table)
How do I get the ID of the last updated row in MySQL using PHP?
I've found an answer to this problem :)
SET #update_id := 0;
UPDATE some_table SET column_name = 'value', id = (SELECT #update_id := id)
WHERE some_other_column = 'blah' LIMIT 1;
SELECT #update_id;
EDIT by aefxx
This technique can be further expanded to retrieve the ID of every row affected by an update statement:
SET #uids := null;
UPDATE footable
SET foo = 'bar'
WHERE fooid > 5
AND ( SELECT #uids := CONCAT_WS(',', fooid, #uids) );
SELECT #uids;
This will return a string with all the IDs concatenated by a comma.
Hm, I am surprised that among the answers I do not see the easiest solution.
Suppose, item_id is an integer identity column in items table and you update rows with the following statement:
UPDATE items
SET qwe = 'qwe'
WHERE asd = 'asd';
Then, to know the latest affected row right after the statement, you should slightly update the statement into the following:
UPDATE items
SET qwe = 'qwe',
item_id=LAST_INSERT_ID(item_id)
WHERE asd = 'asd';
SELECT LAST_INSERT_ID();
If you need to update only really changed row, you would need to add a conditional update of the item_id through the LAST_INSERT_ID checking if the data is going to change in the row.
This is officially simple but remarkably counter-intuitive. If you're doing:
update users set status = 'processing' where status = 'pending'
limit 1
Change it to this:
update users set status = 'processing' where status = 'pending'
and last_insert_id(user_id)
limit 1
The addition of last_insert_id(user_id) in the where clause is telling MySQL to set its internal variable to the ID of the found row. When you pass a value to last_insert_id(expr) like this, it ends up returning that value, which in the case of IDs like here is always a positive integer and therefore always evaluates to true, never interfering with the where clause. This only works if some row was actually found, so remember to check affected rows. You can then get the ID in multiple ways.
MySQL last_insert_id()
You can generate sequences without calling LAST_INSERT_ID(), but the
utility of using the function this way is that the ID value is
maintained in the server as the last automatically generated value. It
is multi-user safe because multiple clients can issue the UPDATE
statement and get their own sequence value with the SELECT statement
(or mysql_insert_id()), without affecting or being affected by other
clients that generate their own sequence values.
MySQL mysql_insert_id()
Returns the value generated for an AUTO_INCREMENT column by the
previous INSERT or UPDATE statement. Use this function after you have
performed an INSERT statement into a table that contains an
AUTO_INCREMENT field, or have used INSERT or UPDATE to set a column
value with LAST_INSERT_ID(expr).
The reason for the differences between LAST_INSERT_ID() and
mysql_insert_id() is that LAST_INSERT_ID() is made easy to use in
scripts while mysql_insert_id() tries to provide more exact
information about what happens to the AUTO_INCREMENT column.
PHP mysqli_insert_id()
Performing an INSERT or UPDATE statement using the LAST_INSERT_ID()
function will also modify the value returned by the mysqli_insert_id()
function.
Putting it all together:
$affected_rows = DB::getAffectedRows("
update users set status = 'processing'
where status = 'pending' and last_insert_id(user_id)
limit 1"
);
if ($affected_rows) {
$user_id = DB::getInsertId();
}
(FYI that DB class is here.)
This is the same method as Salman A's answer, but here's the code you actually need to do it.
First, edit your table so that it will automatically keep track of whenever a row is modified. Remove the last line if you only want to know when a row was initially inserted.
ALTER TABLE mytable
ADD lastmodified TIMESTAMP
DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP;
Then, to find out the last updated row, you can use this code.
SELECT id FROM mytable ORDER BY lastmodified DESC LIMIT 1;
This code is all lifted from MySQL vs PostgreSQL: Adding a 'Last Modified Time' Column to a Table and MySQL Manual: Sorting Rows. I just assembled it.
Query :
$sqlQuery = "UPDATE
update_table
SET
set_name = 'value'
WHERE
where_name = 'name'
LIMIT 1;";
PHP function:
function updateAndGetId($sqlQuery)
{
mysql_query(str_replace("SET", "SET id = LAST_INSERT_ID(id),", $sqlQuery));
return mysql_insert_id();
}
It's work for me ;)
SET #uids := "";
UPDATE myf___ingtable
SET id = id
WHERE id < 5
AND ( SELECT #uids := CONCAT_WS(',', CAST(id AS CHAR CHARACTER SET utf8), #uids) );
SELECT #uids;
I had to CAST the id (dunno why)... or I cannot get the #uids content (it was a blob)
Btw many thanks for Pomyk answer!
Hey, I just needed such a trick - I solved it in a different way, maybe it'll work for you. Note this is not a scalable solution and will be very bad for large data sets.
Split your query into two parts -
first, select the ids of the rows you want to update and store them in a temporary table.
secondly, do the original update with the condition in the update statement changed to where id in temp_table.
And to ensure concurrency, you need to lock the table before this two steps and then release the lock at the end.
Again, this works for me, for a query which ends with limit 1, so I don't even use a temp table, but instead simply a variable to store the result of the first select.
I prefer this method since I know I will always update only one row, and the code is straightforward.
ID of the last updated row is the same ID that you use in the 'updateQuery' to found & update that row. So, just save(call) that ID on anyway you want.
last_insert_id() depends of the AUTO_INCREMENT, but the last updated ID not.
My solution is , first decide the "id" ( #uids ) with select command and after update this id with #uids .
SET #uids := (SELECT id FROM table WHERE some = 0 LIMIT 1);
UPDATE table SET col = 1 WHERE id = #uids;SELECT #uids;
it worked on my project.
Further more to the Above Accepted Answer
For those who were wondering about := & =
Significant difference between := and =, and that is that := works as a variable-assignment operator everywhere, while = only works that way in SET statements, and is a comparison operator everywhere else.
So SELECT #var = 1 + 1; will leave #var unchanged and return a boolean (1 or 0 depending on the current value of #var), while SELECT #var := 1 + 1; will change #var to 2, and return 2.
[Source]
If you are only doing insertions, and want one from the same session, do as per peirix's answer. If you are doing modifications, you will need to modify your database schema to store which entry was most recently updated.
If you want the id from the last modification, which may have been from a different session (i.e. not the one that was just done by the PHP code running at present, but one done in response to a different request), you can add a TIMESTAMP column to your table called last_modified (see http://dev.mysql.com/doc/refman/5.1/en/datetime.html for information), and then when you update, set last_modified=CURRENT_TIME.
Having set this, you can then use a query like:
SELECT id FROM table ORDER BY last_modified DESC LIMIT 1;
to get the most recently modified row.
No need for so long Mysql code. In PHP, query should look something like this:
$updateQuery = mysql_query("UPDATE table_name SET row='value' WHERE id='$id'") or die ('Error');
$lastUpdatedId = mysql_insert_id();