I have an sql query that simply adds a row to an existing database table. The first field is id and I assume this value has to be specified in the query.
So then id has to be the number of rows existing + 1. I'm determining the id like this:
SELECT COUNT(1) FROM testtable
The problem is that this returns the number of rows that have ever been added, including deleted ones. Because I have been adding and removing from this table, this number is greater than the number of EXISTING rows in the table which is what I want.
How can I count the existing rows in the table instead of the existing + deleted rows?
If possible switch to using an auto_increment column for your id and don't be concerned with gaps in the sequence of ids. Your own implementation of id generation may inflict more harm especially in a long run.
Now, back to your immediate question. You are probably looking for this
SELECT MAX(id) + 1 new_id
FROM Table1
Note: This query might fail under heavy load when several concurrent sessions issuing this
query might grab the same id and your subsequent INSERT will fail. Therefore again consider using an auto_increment for your id.
Here is SQLFiddle demo
That's a bad idea, because you can end up with duplicate IDs, especially if you delete rows in the middle. If you're not going to use an auto-increment field, you can add the ID to the insert. Just use this in place of the value:
((SELECT MAX(t.id) + 1 FROM table t)
The full query would then be:
INSERT INTO table_name (id, col1, col2, col3) VALUES ((SELECT MAX(t.id) + 1 FROM table_name t), :col1, :col2, :col3)
SELECT COUNT(*) from table
will return only the number of entries in it, not based on the ID
There is also possibility to find, and fill the gaps, by picking first free id
SELECT MIN(t1.id + 1) AS free_id
FROM t1
WHERE NOT EXISTS (SELECT t2.id FROM t2 WHERE t2.id = t1.id + 1)
Related
I want to assign values to a column
In this manner
1,2,3,4,5,...1000
If I insert values for column
1,2,3,4,5,6,7
Using auto increment
Now I want to delete 2,3
And the next insertion should be 2,3, not 8
After 2,3 is completed then my insertion should start with 8 ...
AUTO_INCREMENT behaves like this; it doesn't fill the gaps arising from deleted data, it just appends the new entries to the "bottom" of the current enumeration. This is because, in a vast majority of cases, it's a very bad practice to reuse unique identifiers.
There is pretty much nothing you can do about it... except changing the design of your table so that the identifier field doesn't have the AUTO_INCREMENT property and, then, implementing your own logic to fill the gaps upon insertion. Finding the smallest unused identifier and assigning it to the upcoming insertion is pretty easy:
SET #GapId = (
SELECT MIN(T1.ID + 1)
FROM MyTable T1
LEFT JOIN MyTable T2 ON T2.ID = T1.ID + 1
WHERE T2.ID IS NULL
);
INSERT INTO MyTable(ID,...) VALUES (#GapId,...);
I have 2 tables with similar columns in MYSQL. I am copying data from one to another with INSERT INTO table2 SELECT * FROM table1 WHERE column1=smth. I have different columns as autoincrement and KEY in tables. When I use mysqli_insert_id i get the first one rather then last one inserted. Is there any way to get the last one?
Thanks
There is no inherit ordering of data in a relational database. You have to specify which field it is that you wish to order by like:
INSERT INTO table2
SELECT *
FROM table1
WHERE column1=smth
ORDER BY <field to sort by here>
LIMIT 1;
Relying on the order a record is written to a table is a very bad idea. If you have an auto-numbered id on table1 then just use ORDER BY id DESC LIMIT 1 to sort the result set by ID in descending order and pick the last one.
Updated to address OP's question about mysqli_insert_id
According to the Mysql reference the function called here is last_insert_id() where it states:
Important If you insert multiple rows using a single INSERT statement,
LAST_INSERT_ID() returns the value generated for the first inserted
row only. The reason for this is to make it possible to reproduce
easily the same INSERT statement against some other server.
Unfortunately, you'll have to do a second query to get the true "Last inserted id". Your best bet might be to run a SELECT COUNT(*) FROM table1 WHERE column1=smth; and then use that count(*) return to add to the mysqli_insert_id value. That's not great, but if you have high volume where this one function is getting hit a lot, this is probably the safest route.
The less safe route would be SELECT max(id) FROM table2 or SELECT max(id) FROM table2 Where column1=smth. But... again, depending on your keys and the number of times this insert is getting hit, this might be risky.
$stmt = $dbh->prepare("SELECT id FROM table1 UNION SELECT id FROM table2 UNION SELECT id FROM table3 ORDER BY id DESC LIMIT 1");
The code above is my code for auto generating ID. I want to select the last inserted ID. What I want to do is I want to get the last ID inserted on three tables. It can be from any of the three tables. And then i want to increment that ID. But the catch is i need to know which table the last ID is from so i can select another field and record that the last ID has a certain attribute. This attribute is depending on the table that is why i want to get the table.
Add a discriminator column, and use the MAX aggregate function for each query to avoid sorting a huge intermediate resultset, and use UNION ALL set operator in place of UNION operator. (Since each query will return only one row, that's not going to make much of a difference; but where we don't need to eliminate duplicate rows, we prefer the UNION ALL set operator to avoid the unnecessary (and sometimes expensive) operation.
Something like this should return the result you seem to be after:
( SELECT 'table1' AS source
, MAX(t1.id) AS max_id
FROM table1 t1
)
UNION ALL
( SELECT 'table2' AS source
, MAX(t2.id) AS max_id
FROM table2 t2
)
UNION ALL
( SELECT 'table3' AS source
, MAX(t3.id) AS max_id
FROM table3 t3
)
ORDER BY max_id DESC
LIMIT 1
That will give you a resultset that identifies the table name that had the maximum id.
NOTE: This assumes that the "last inserted id" is identified by the maximum value. If two tables have the same maximum id value, it's indeterminate which row will be returned. You can add source to the ORDER BY to make it deterministic:
ORDER BY max_id DESC, source DESC
(The actual requirements aren't clear to me; the statement above should return the same value that was being returned by the query in the question, along with a discriminator which tells which table the id value is from.)
Reference: https://dev.mysql.com/doc/refman/5.5/en/union.html
NOTE: This may satisfy your use case, but in the more general case, we advise avoiding this approach to get the id value of the row that was last inserted.
For values of an auto_increment column, where the value is automatically assigned, the last_insert_id function will return the id value of the first row that was inserted by the immediately preceding INSERT statement in the same session. In a multiuser system, it is not safe to assume that the "highest" id value was a row inserted by the current session - that approach is effectively broken.
Try to get last inserted ID of all three tables and with inserted time then differentiate based on inserted time
I need to be able to safely insert a row with first available ID. I read alot answers about PRIMARY_KEY and AUTO_INCREMENT and all this stuff, but this is something else. I need to keep and be able to work on database with fixed ID range from 1 to 60000. Is there any way to do that with MySQL? Writing own function that check what is nearest "free" ID, is not an option cause there may be conflicts on multiuser usage.
In the best case scenario,MySQL would somehow work as with PRIMARY_KEY, but reusing keys.
Follow these steps:
1) Create a sequence table with columns id, rowstate.
2) Insert ids 1-60000 to that with the rowstate 1
3) Whenever you want to insert in your main table, search for the lowest id from the sequence table having rowstate=1 and update the sequence to -1.
When you want to delete a record from the main table, set the rowstate of the id to 1.
you are write, you need to concider the the concurrency issues
hence you need to implement a table lock mechnism
1) Lock mysql table
2) Insert the record, you can just use the auto_increment property since no two values would be added at the same time (i dont think you have to lock tables if this is used anyway)
3) If you dont want to use auto_increment, any of the above suggested code will work
You can try like this:
INSERT INTO tableName (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE id=(Select max(id)+1 from tableName);
For more info: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
OR
Getting highest id from table and increament it and insert your row.
Select max(id) from tableName;
You will get the id. Than add 1 it and insert into table. Additionally you can check it should be less than 60000.
I know this is not the best answer, but can be consider as second alternative.
To get the first id that is free you can do
select id - (id-ra) as lowest
from
(
select id, #r := #r + 1 as ra
from t, (select #r := 0) rank
order by id
)
x
where ra <> id
limit 1
SQLFiddle demo
You can put that in a procedure where you lock the table during the operation
delimiter |
CREATE PROCEDURE create_save ()
BEGIN
LOCK TABLES your_table WRITE;
set #lowid := 0;
select #lowid := id - (id-ra)
from
(
select id, #r := #r + 1 as ra
from your_table, (select #r := 0) rank
order by id
)
x
where ra <> id
limit 1;
if #lowid between 1 and 59999
then
insert into your_table (id, othercolumn)
select #lowid, 12345;
end if;
UNLOCK TABLES;
end
|
delimiter ;
I have a table which has several thousand records.
I want to update all the records which have a duplicate firstname
How can I achieve this with a single query?
Sample table structure:
Fname varchar(100)
Lname varchar(100)
Duplicates int
This duplicate column must be updated with the total number of duplicates with a single query.
Is this possible without running in a loop?
update table as t1
inner join (
select
fname,
count(fname) as total
from table
group by fname) as t2
on t1.fname = t2.fname
set t1.duplicates = t2.total
I have a table which has several thousand records. I want to update all the records which have a duplicate firstname How can I achieve this with a single query?
Are you absolutely sure you want to store the number of the so called duplicates? If not, it's a rather simple query:
SELECT fname, COUNT(1) AS number FROM yourtable GROUP BY fname;
I don't see why you would want to store that number though. What if there's another record inserted? What if there are records deleted? The "number of duplicates" will remain the same, and therefore will become incorrect at the first mutation.
Create the column first, then write a query like:
UPDATE table SET table.duplicates = (SELECT COUNT(*) FROM table r GROUP BY Fname/Lname/some_id)
Maybe this other SO will help?
How do I UPDATE from a SELECT in SQL Server?
You might not be able to do this. You can't update the same table that you are selecting from in the same query.