Switch values in MySQL table - mysql

I have a table in MySQL with a field "Ordering" These are just auto incremented numbers. Now I wonder if there is a query to change the values from the last to the first...
So the entry with ordering 205 should become 1, 204 -> 2 and so on...
It's actually not an auto-increment. The problem is I started adding projects from the current website. From page 1 to page 20, but the first item on page 1 is the latest. The way I added the new projects, the newest is on the last page..
If the ordering field is switched, the new items added will be correctly numbered again and added to the front page. It's just a wrong way I started adding old projects...
Structure
Examples of the content

I can't comment due to limitations, but i really agree with #Abhik Chakraborty.
You don't want to do this. Just use the order by as he suggested.
Example:
SELECT * FROM tableName
ORDER BY columnName DESC;
Just in case you would like to know more about it: http://www.w3schools.com/sql/sql_orderby.asp

Try this as one statement call:
SET #MaxSort := (SELECT MAX(Ordering) FROM MyTable);
UPDATE MyTable t set t.Ordering = (#MaxSort + 1 - t.Ordering);
This will work if field doesn't have unique constraint.
But this field, should not be an auto_increment field at first place. Auto increment is increasing NOT decreasing counter. Except if you just try to fix existing data and the new records will be increasing.
Additional explanation
Thanks for pointing it out. Multiple query inside single query statement doesn't work with php_mysqli and it is not used because of potential MySQL injection attack if servers allows it. Maybe you can setup PHPMyAdmin to use PHP PDO.
I can use multiple queries, but I'm using PHP PDO or DBeaver database manager.
I can only suggest to supply MaxSort manually (since this is one time job anyway):
UPDATE
MyTable t
set
t.Ordering = 254 - t.Ordering + 1;

Related

Leasing jobs (atomic update and get) from a MySQL database

I have a MySQL table that manages jobs that worker-clients can lease for processing. Apart from the columns that describe the job, the table has a unique primary key column id, a time-stamp-column lease, a boolean-column complete, and an int-column priority.
I'm trying to write a (set of) SQL statement(s) that will manage the leasing-process. My current plan is to find the first incomplete job that has a lease-date that is at least 8 hours in the past (no job should take more than one hour, so an incomplete lease that is that old probably means that the client died and the job needs to be restarted), set its lease-date to the current time-stamp, and return its info. All of this, of course, needs to happen atomically.
I found a neat trick here on SO and a variation of it in the discussion of the MySQL documentation (see post on 7-29-04 here) that uses user-defined variables to return the leased job from an UPDATE statement.
And, indeed, this works fine:
UPDATE jobs SET lease=NOW() WHERE TIMESTAMPDIFF(HOUR,lease,NOW())>=8 AND NOT complete AND #id:=id LIMIT 1;
SELECT * FROM jobs WHERE id=#id;
The problem comes in when I try to add priorities to the jobs and add ORDER BY priority into the UPDATE statement right before LIMIT. The UPDATE still works as expected, but the SELECT always returns the same row back (either the first or the last, but not the one that was actually updated). I'm a little confused by this, since LIMIT 1 should make sure that the first update that actually happens will terminate the UPDATE process, leaving #id set to the correct value of that updated row, no? For some reason it seems to keep evaluating the condition #id:=id for all rows anyways, even after it's done with its update (or maybe it evaluates it first for all rows before even figuring out which one to update, I don't know...).
To fix this, I tried rewriting the statement to make sure the variable really only gets set for the matching row:
UPDATE jobs SET lease=NOW(),#id:=id WHERE TIMESTAMPDIFF(HOUR,lease,NOW())>=8 AND NOT complete ORDER BY priority LIMIT 1;
But for some reason, this gives me the following error:
Error Code : 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
'#id:=id WHERE TIMESTAMPDIFF(HOUR,lease,NOW())>=8 AND NOT complete ORDER BY prior'
at line 1
So, it seems that I can't assign the variable in the SET-part of the UPDATE (although this was the way it was suggested in the SO-answer linked above).
Can this approach be salvaged somehow or is there a better one altogether?
PS: I'm using MySQL server v5.5.44-0+deb8u1
My solution with a little trick:
first: you must use a subselect so that UPDATE not nows thats the same table an
second: you must initialize the #id with "(SELECT #id:=0)" else if the found no row they returns the last set value. Here you can also specify if they return 0 or '' when no result is found.
UPDATE jobs SET lease=NOW() WHERE id =
( SELECT * FROM
( SELECT #id:=id FROM jobs,(SELECT #id:=0) AS tmp_id
WHERE TIMESTAMPDIFF(HOUR,lease,NOW())>=8
AND NOT complete ORDER BY priority LIMIT 1
) AS tmp
);
It is OK that you found a solution.
If this must be quite stable, I would go for a different solution. I would not use atomicity, but "commit"- like workflows. You should identify your worker-client with a unique key, either in it's own table or with a secure hash key. You add two fields to your jobs-table: worker and state. So if you look for a job for worker W345, you assign worker to that job.
First part would be
update jobs set worker='W345', state='planning', lease=now()
where TIMESTAMPDIFF(HOUR,lease,NOW())>=8
AND NOT complete
ORDER BY priority LIMIT 1;
Next part (could be even from different part of application)
select * from jobs where worker='W345' and state='planning';
get id and data, update:
update jobs set state='sending', lease=now() where id=...;
Maybe you even can commit the sending of the job, otherwise you guess that it started after sending.
update jobs set state='working', lease=now() where id = ...;
You find all jobs that are dead before being sent to worker by their state and some short minutes old lease. You can find out where the process got into trouble. You can find out which workers get most trouble, and so on.
Maybe the real details differ, but as long as you have some status column you should be quite flexible and find your solution.
I was able to fix things with the following hack:
UPDATE jobs SET lease=IF(#id:=id,NOW(),0) WHERE TIMESTAMPDIFF(HOUR,lease,NOW())>=8 AND NOT complete ORDER BY priority LIMIT 1;
Seems like it's simply not allowed to set a local variable within the SET section of UPDATE.
Note:Since the id column is an auto-increment primary key, it is never 0 or NULL. Thus, the assignment #id:=id inside the IF-statement should always evaluate to TRUE and therefore lease should be set correctly (correct me if I'm wrong on this, please!).
One thing to keep in mind:The variable #id by default is scoped to the MySQL connection (not any Java Statement-object, for example, or similar), so if one connection is to be used for multiple job-leases, one needs to ensure that the different UPDATE/SELECT-pairs never get interleaved. Or one could add an increasing number to the variable-name (#id1, #id2, #id3, ...) to guarantee correct results, but I don't know what performance (or memory-use) impact this will have on the MySQL-server. Or, the whole thing could be packaged up into a stored procedure and the variable declared as local.

Increment a value in mysql table column dynamically

I have a mysql table column (runs) which holds an integer, I want to increment it by another dynamic value (1,2,3,4,5) using an update query on click of a button. How should I do that?
(Without retrieving the original value from that table column, is there any direct way to increment the value, not like AUTO_INCREMENT since it does it using a static value)
I've checked the documentation on MySql here and it seems you can do it with an update query like this:
UPDATE t1 SET col1 = col1 + 1;
where you can change 1 to any numeric value as long as col1 is also numeric.
MySql will get the initial value of col1 and add your number to it.
Even if MySql can do this, I'm guessing this also has an impact on speed, it might be faster then 2 queries, but probably needs testing to determin the differences.
When confronted with software speeds issues saving a select query for only one value it's not the key modification that you can do to optimize your app. As Digvijay Yadav suggested, you should also check other solutions, and other optimizations on to make your app faster.
another example on that page that also shows that what your are trying to do can work is :
UPDATE t SET id = id + 1 ORDER BY id DESC;
please try it and let me know if it doesn't work.
If DB interaction is the main problem then in this case I would suggest not to read/write to DB on every user click. Instead you should retrieve the value once and store them in a DS (may be an array or list). On every click you perform operations on this list and after sometime you update the DB with updated values.
Also, if you are creating and destroying the DB connections on every read/write operation then I would not suggest that. A better solution IMO is to create a DB connection when your webapp starts up and store the connection object in the ServletContext as an attribute as you are using servlets and JSPs.
Further if your web app is too big and has thousand of users then there are several caching techniques for improving the performance of DB.

SQL Trigger for comparing on insert

I'd like to write a trigger that checks top value in column (table consists of 4 double columns and one of them acts as a primary key (value within that field should always be bigger then previous entry)). Now is there a way to compare a top value of id column with new value that should be inserted, and rollback transaction if value of new id is equal or lower then previous top value(by using sql triggers of course).
Thank you in advance.
One and most easy way I found is to check if you get any result. I mean this:
if (SELECT COUNT(*) FROM tbl_name WHERE id = id_you_want_check >= 1) {
rollback;
}
If you know to use triggers in mysql or whatever DBMS you are using (look at the documentation of your DBMS).
PS: As said Colin, next time post your SQL DBMS to easily find an appropiate solution for you. ;)

Inserting data in MYSQL is placed after a recently deleted record

I have designed a page using PHP, JavaScript and MYSQL, where it holds id(Auto incremented) images, names and so on. every thing works fine, i can insert, update or delete the records with click on respective buttons. what i have noticed that once a record deleted and when i try to insert a new record, it gets placed exactly after recently deleted record. for example. if i have 18 records, i delete record # 14 and insert a new record which obviously will be record # 19, it will be place after deleted record which was #14. is there anyway to force the insert to place the new record at the end of the table (after last record #19)? i don't want to get into phpMyAdmin and use Alter table order by...
when using select i have no problem as i'm using ORDER BY.. so that it displays every thing as i want.
Appreciating all your help.
Fardin
By definition, SQL tables have no natural order. Relying on the natural order that some MySQL table managers provide is going to lead to very fragile code. Please consider sticking with the SQL standard and ordering your rows on retrieval.
You cannot control how MySQL controls the recycling of deleted-record space.
Here is a comment from MySQL that says, essentially, the same thing: http://forums.mysql.com/read.php?108,41767,41836.

In Ruby on Rails, how to create a record with ID 3 when it was destroyed earlier?

I am trying an experimental Rails project and destroyed record 3 (ID == 3), so now there is record 1, 2, 4, and 5.
Is there a way to repopulate this table with record ID from 1 to 5? (this is using mysql)
I can use
if Item.exists?(i)
item = Item.find(i)
else
item = Item.new
end
# set some values
item.name = "haha" + i
item.save
Item.new() is to create new records with new autoincrement record IDs (6 in this case), but what about the ones that were deleted earlier?
You can set the ID using:
item = Item.new
item.id = i
You cannot pass in :id => i to Item.new because id is a special attribute, and is automatically protected from mass-assignment.
You can also tell MySQL to start counting from 1 again, which will automatically fill in all missing IDs:
Item.connection.execute("ALTER TABLE items AUTO_INCREMENT = 1")
(Actually, MySQL docs say that this it will reset it to MAX(id) + 1, so it won't end up using 3 in this case.)
MySQL won't do this for you, as you likely know. On every single item creation, you can go back and see if there are any unused ID numbers, and use those instead. (I bet you can set id explicitly, though I haven't tried.) There are two reasons why I wouldn't recommend this:
It will significantly slow down record creation when you get to a large number of records.
An item's ID generally is meant to refer to it forever. Especially if you plan to use the ID in URLs, you shouldn't reuse an ID number if it was ever used before, to maintain data consistency. A deleted item should have all traces removed.
That is, if you're talking about using unused ID numbers on each item creation once you reach production. If you're talking about just a one-time deal since not having item #3 bugs you... your time is better spent working on something else.
You can do it as #wuputah said. And more over normally we shouldn't delete records from a table unless otherwise its not a master table (as it might affect the other table relationships). Instead we can inactive records (say having a table column called active)
And make things more simple you can use a plugin like acts_as_paranoid to achieve this
http://github.com/technoweenie/acts_as_paranoid
cheers
sameera