i have a column ID and something like 1000 items, some of then were removed like id=90, id=127, id=326
how can i make a query to look for those available ids, so i can reuse then for another item?
its like a min(ID) but i want to find only the ids that are NOT in my database, so if i remove a item with the ID = 90, next time i click on ADD ITEM i would insert it as id = 90
You can get the minimum available ID using this query:
SELECT MIN(t1.ID + 1) AS nextID
FROM tablename t1
LEFT JOIN tablename t2
ON t1.ID + 1 = t2.ID
WHERE t2.ID IS NULL
What it does is that it joins the table with itself and checks whether the min+1 ID is null or not. If it's null, then that ID is available. Suppose you have the table where ID are:
1
2
5
6
Then, this query will give you result as 3 which is what you want.
Do not reuse IDs. You usually have way enough available IDs so you don't have to care about fragmentation.
For example, if you re-use IDs, links from search engines might point to something completely unrelated from whatever is in the search index - showing a "not found" error is much better in such a case.
It's against the concept of surrogate keys to try to reuse IDs
The surrogate key is good because it idetifies the record itself, not some object in real life. If the record is gone, the ID is gone too.
Experienced DB developers are not afraid of running out of numbers because they know how many centuries it is needed to deplete, say, long integer numbers.
BTW, you may experience locking or violating uniqueness problems in a multithreaded environment with simultaneous transactions trying to find a gap in the ID sequence. The auto increment id generators provided by DB servers usually work outside the transactions scope and thus generate good surrogate keys.
Further reading: Surrogate keys
the query is like :
SELECT MIN(tableFoo.uniqueid + 1) AS nextID
FROM tableFoo
LEFT JOIN tableFoo tf1
ON tableFoo.uniqueid + 1 = tf1.uniqueid
WHERE tf1.uniqueid IS NULL
Note that the answers by shamittomar and Haim Evgi don't work if the lowest ID is free. To allow for the refilling the lowest ID, pre-check to see whether it is available:
SELECT TRUE FROM tablename WHERE ID = 1;
If this returns anything, then the ID of 1 is not free and you should use their answer. But if the ID of 1 is free, just use that.
In my personal opinion. Instead of removing the row from the auto increment it would be light years less expensive to have Boolean Column for "Removed" or "Deleted" and for extra security over right the row with blanks while you set the removed flag.
UPDATE table SET data=" ", removed = TRUE WHERE id = ##
(## is the actual id btw)
Then you can
SELECT * FROM table WHERE removed = TRUE ORDER BY id ASC
This will make your Database perform better and save you dough on servers. Not to mention ensure no nasty errors occur.
Given that your database is small enough, the correct answer is to not reuse your ids at all and just ensure its an auto incremented primary key. The table is a thousand records, so you can do this without any cost.
However, if you have a table of a few million records/longer id, you will find that the accepted answer wont finish in sensible time.
The accepted answer will give you the smallest of these values, correctly so, however, you are paying the price of not using an auto increment column, or if you have one, not using the auto increment column as the actual ID as it is intended (Like me, else I wouldn't be here). I'm at the mercy of a legacy application were the ID isn't the actual primary key is being used, and is randomly generated with a lolgorithm for no good reason, so I needed a means to replace that since upping the column range is now an extremely costly change.
Here, it is figuring out the entire
join between the entirety of t1 and t2 before reporting what the min of those joins is. In essence, you only care about the first NULL t1 that is found, regardless of whether it actually is the smallest or not.
So you'd take the MIN out and add a LIMIT of 1 instead.
edit : Since its not a primary key, you will also need to check for not null, since a primary key field cant be null
SELECT t1.ID + 1 AS nextID
FROM tablename t1
LEFT JOIN tablename t2
ON t1.ID + 1 = t2.ID
WHERE t2.ID IS NULL
AND t1.ID IS NOT NULL
LIMIT 1
This will always give you an id that you can use, its just not guaranteed to always be the smallest one.
Related
I have a SQL table with two columns:
'id' int Auto_Increment
instancename varchar
The current 114 rows are ordered alphabetically after instancename.
Now i want to insert a new row that fits into the order.
So say it starts with a 'B', it would be at around id 14 and therefore had to 'push down' all of the rows after id 14. How do i do this?
An SQL table is not inherently ordered! (It is just a set.) You would simply add the new row and view it using something like:
select instancename
from thetable
order by instancename;
I think you're going about this the wrong way. IDs shouldn't be changed. If you have tables that reference these IDs as foreign keys then the DBMS wouldn't let you change them, anyway.
Instead, if you need results from a specific query to be ordered alphabetically, tell SQL to order it for you:
SELECT * FROM table ORDER BY instancename
As an aside, sometimes you want something that can seemingly be a key (read- needs to be unique for each row) but does have to change from time to time (such as something like a SKU in a product table). This should not be the primary key for the same reason (there are undoubtedly other tables that may refer to these entries, each of which would also need to be updated).
Keeping this information distinct will help keep you and everyone else working on the project from going insane.
Try using an over and joining to self.
Update thetable
Set ID = r.ID
From thetable c Join
( Select instancename, Row_Number() Over(Order By instancename) As ID
From CollectionStatus) r On c.instancename= r.instancename
This should update the id column to the ordered number. You may have to disable it's identity first.
Just working with a database and some tests were done recently which checked the integrity of the setup.
As a result, a lot of test entries were added which were then deleted. However, when new entries are added, the ID number value continues from after the entries added.
What I want:
ID increases by one from where it left off before the additional rows were added:
4203, 4204, 4205, 4206 etc.
What is happening:
ID increases by one from after the additional rows ID:
4203, 4204, 6207, 6208 6209 etc.
Not sure where to fix this...whether in phpmyadmin or in the PHP code. Any help would be appreciated. Thanks!
I have ran into this before and I solve it easily with phpMyAdmin. Select the database, select the table, open the operations tab, and in the Table Options set the AUTO_INCREMENT to 1 then click GO. This will force mysql to look for the last auto incremented value and then set it to the value directly after that. I do this on a manually basis that way I know that when a row is skipped that it was not from testing but a deletion because when I test and delete the rows I fix the AI value.
I don't think there's a way to do this with an auto-incrementing ID key.
You could probably do it by assigning the ID to (select max(id) + 1 from the_table)
You could drop the primary key then recreate it, but this would reassign all the existing primary keys so could cause issues with relationships (although if you don't have any gaps in your primary key you may get away with it).
I would however say that you should accept (and your app should reflect) the possibility of missing IDs. For example in a web app if someone links to a missing ID you would want a 404 returned not a different record.
There should be no need to "reset" the id values; I concur with the other comments concerning this issue.
The behavior you observe with AUTO_INCREMENT is by design; it is described in the MySQL documentation.
With all that said, I will describe an approach you can use to change the id values of those rows "downwards", and make them all contiguous:
As a "stepping stone" first step, we will create a query that gets a list of the id values that we need changed, along with a proposed new id value we are going to change it to. This query makes use of a MySQL user variable.
Assuming that 4203 is the id value you want to leave as is, and you want the next higher id value to be reset to 4204, the next higher id to be reset to 4205, etc.
SELECT s.id
, #i := #i + 1 AS new_id
FROM mytable s
JOIN (SELECT #i := 4203) i
WHERE s.id > 4203
ORDER BY s.id
(Note: the constant value 4203 appears twice in the query above.)
Once we're satisfied that this query is working, and returning the old and new id values, we can use this query as an inline view (MySQL calls it a derived table), in a multi-table UPDATE statement. We just wrap that query in a set of parentheses, and give assign it an alias, so we can reference it like a regular table. (In an inline view, MySQL actually materializes the resultset returned by the query into a MyISAM table, which probably explains why MySQL refers to it as a "derived table".)
Here's an example UPDATE statement that references the derived table:
UPDATE ( SELECT s.id
, #i := #i + 1 AS new_id
FROM mytable s
JOIN (SELECT #i := 4203) i
WHERE s.id > 4203
ORDER BY s.id
) n
JOIN mytable t
ON t.id = n.id
SET t.id = n.new_id
ORDER BY t.id
Note that the old id value from the inline view is matched to the id value in the existing table (the ON clause), and the "new_id" value generated by the inline view is assigned to the id column (the SET clause.)
Once the id values are assigned, we can reset the AUTO_INCREMENT value on the table:
ALTER TABLE mytable AUTO_INCREMENT = 1;
NOTE: this is just an example, and is provided with the caveat that this should not be necessary to reassign id values. Ideally, primary key values should be IMMUTABLE i.e. they should not change once they have been assigned.
I know auto_increment is the way to go but I can not use auto_increment feature since the column in my table might repeat, its not unique. When I insert a new row to a table I need a way to find the next available spot to insert it.
For example table structure:
Primary Key = (ID, UserID)
ID UserID
3 6
3 1
1 3
Now when i do insert query i want to isert it at ID = 2 and not 4. With auto_increment it gives me 4
Is there a solution without using the loop in PHP? So far what i have is I fetch all rows into array and then find the next available digit in ID. Is it possible to do this without fetching all rows in PHP and just doing it on MySQL query ?
SELECT t1.id+1 AS MISSING_ID
FROM the_table AS t1
LEFT JOIN the_table AS t2 ON t1.id+1 = t2.id
WHERE t2.id IS NULL
ORDER BY t1.id LIMIT 1;
I made a fiddle: http://sqlfiddle.com/#!2/4d14d/2
No, it is not possible without processing the data. The preferred method to correct this issue is to adjust your table structure to support a unique, auto-incrementable field. Failing that, you will have to process the data (either in PHP or via an SQL statement) to find an open slot.
This should do the trick:
SELECT
min_table.ID+1 AS start,
MIN(max_table.ID) - 1 AS end
FROM
your_table AS min_table,
your_table AS max_table
WHERE
min_table.ID < max_table.ID
GROUP BY
min_table.ID
HAVING
start < MIN(max_table.ID)
The left hand column will return the first available spot in the sequence gap, and the second is the highest number in that particular gap.
Source: http://www.codediesel.com/mysql/sequence-gaps-in-mysql/
My workaround for not loaded project:
Suppose, you have questionset with question_id 's which belong to certain topic_id.
Suppose, user navigates and clicks "<Prev" "Next>" buttons to navigate questions.
You have only current id. Catching the direction of navigation, topic_id, question_id you can do a loop
do {
// query base, doing question_id++ or question_id-- depending on needed direction until you find next id within topic_id
} while( id!=null ) `
using incrementation or decrementation depending on direction of your move
i got an sql table which contains data for a menu in the php page.
SELECT * FROM `hizlierisim` ORDER BY id LIMIT 0 , 10
it is ordered by id row.but im asked to add order control on admin panel.
So there will Up&Down Buttons to re-arrange menu items.
i guess there is two ways to do this
one is to create a row called 'order' and change sql query to:
SELECT * FROM `hizlierisim` ORDER BY `order` LIMIT 0 , 10
or
exchange id's of the columns that we wanted to move.
and still use same sql:
SELECT * FROM `hizlierisim` ORDER BY id LIMIT 0 , 10
id method seems easier but i wonder if there is a possibility to exchange id's of two columns on mysql?
Messing with primary key fields just to satisfy some arbitrary ordering requirement is a bad idea. Especially in a foreign key situation. Add an 'order' column (which is a reserved word, by the way, so use something else if you want to save yourself some pain) is the practical option.
Changing PK IDs for this sort of functionality is a very bad practice; far better to introduce an ordering column and use that to sort by. Obviously don't call it ORDER as that's a reserved word...
The id of a row should be static, a permanent unique identifier for the record, such that it can be stored as a foreign key elsewhere.
Creating the "order_id" as you suggest would be preferable. This can be changed to any value you like without a side-effect, it's for ordering, changing it only effects ordering.
In terms of "swapping" values, there isn't anything I'm aware of, you just need to code it yourself.
Either...
BEGIN TRANSACTION
UPDATE x SET order_id = NULL WHERE order_id = 11
UPDATE x SET order_id = 11 WHERE order_id = 10
UPDATE x SET order_id = 10 WHERE order_id IS NULL
COMMIT TRANSACTION
(or similar)
Or maybe something like...
UPDATE
x
SET
order_id = CASE order_id WHEN 11 then 10 ELSE 11 END
WHERE
order_id IN (10,11)
By "exchange" I assume you're talking about updating primary key values of two columns which is horrible, HORRIBLE idea - I can't stress enough how bad it is and what implications it might have. I suggest you do some reading on what primary keys are and why you should never play with them.
Add another field and update it with values 0, 1 and so on and order on that column. You can also specify multiple columns in your ORDER BY clause which allows you to order by primary key and some other column(s).
At the moment, I select rows from 'table01 and table02' using:
SELECT t1.*,t2.* FROM table01 AS t1
INNER JOIN table02 AS t2 ON (t1.ID = t2.t1ID)
WHERE t1.UUID = 'whatever';
The UUID column is a unique index, type: char(15), with alphanumeric input. I know this isn't the fastest way to select data from the database, but the UUID is the only row-identifier that is available to the front-end.
Since I have to select by UUID, and not ID, I need to know what of these two options I should go for, if say the table consists of 100'000 rows. What speed differences would I look at, and would the index for the UUID grow to large, and lag the DB?
Get the ID before doing the "big" select
1. $id = SELECT ID FROM table01 WHERE UUID = '{alphanumeric character}';
2. SELECT t1.*,t2.* FROM table01 AS t1
INNER JOIN table02 AS t2 ON (t1.ID = t2.t1ID)
WHERE t1.ID = $id;
Or keep it the way it is now, using the UUID.
2. SELECT t1.*,t2.* FROM table01 AS t1
INNER JOIN table02 AS t2 ON (t1.ID = t2.t1ID)
WHERE t1.UUID = 'whatever';
Side note: All new rows are created by checking if the system generated uniqueid exists before trying to insert a new row. Keeping the column always unique.
Why not just try it out? Create a new db with those tables. Write a quick php script to populate the tables with more records than you can imagine being stored (if you're expecting 100k rows, insert 10 million). Then experiment with different indexes and queries (remember, EXPLAIN is your friend)...
When you finally get something you think works, put the query into a script on a webserver and hit it with ab (Apache Bench). You can watch what happens as you increase the concurrency of the requests (1 at a time, 2 at a time, 10 at a time, etc).
All this shouldn't take too long (maybe a few hours at most), but it will give you a FAR better answer than anyone at SO could for your specific problem (as we don't know your DB server config, exact schema, memory limits, etc)...
The second solution have the best performance. You will need to look up the row by the UUID in both solutions, but in the first solution you first do it by UUID, and then do a faster lookup by primary key, but then you've already found the right row by UUID so it doesn't matter that the second lookup is faster because the second lookup is unnecessary altogether.