How to insert a record in mysql even the key is missing - mysql

I have a table like this:
id ctr
A 1
I'd like to insert a row of id A and update ctr to 2
The new value of ctr which is 2 is based on the previous value increased by 1.
The table would look like this.
id ctr
A 1
A 2
Now I'd like to insert another row of id B. But since B does not exist in the table it will have a counter initialized to 1. So the table will look like below.
id ctr
A 1
A 2
B 1
Is there a way to do this in one sql statement? (I prefer mysql)
Thanks a lot.
edited:
Actually the table is very simple. the actual table would look like this:
id amt balance ctr
A 100 100 1
A 150 250 2
B 50 50 1
As with the ctr the balance is amt+the previous balance.
It is all inserts and no updates.
Thanks.

You can create the primary key on id with ctr, and set the ctr auto-increment.
So every time you insert a record with id = A, the ctr will automatically increase.
`ctr` int(11) NOT NULL AUTO_INCREMENT,
...
PRIMARY KEY (`id`,`ctr`)

You can use COALESCE( MAX(ID)+1, 1 ) in order to initialize the INSERT Statement for a non-existing letter's ID as
SET #ctr = 'B';
INSERT INTO tab
SELECT COALESCE(MAX(ID)+1,1),#ctr FROM tab WHERE ctr = #ctr
Demo
even for the cases the unique key does not exist, otherwise using
INSERT INTO ...
SELECT ... FROM ...
ON DUPLICATE KEY UPDATE ...
would fit better to use.

You can express in one query as as:
insert into t (id, ctr)
select id, coalesce(max(ctr) + 1, 0)
from (select 'a' as id union all
select 'b' as id
) x left join
t
using (id)
group by id;

Related

MS SQL Server lock all records where column X = y

I'm working with a legacy table which I cannot change. It looks similar to:
CREATE TABLE foo
(
Id int IDENTITY(1,1) not null,
OwnerId int not null,
OwnerRecordId int not null,
SomeColumn varchra(50) not null,
CONSTRAINT ix_foo_OwnerId (OwnerId)
-- Ideally the following constraint would exist, but it doesn't. It is enforced
-- with code alone. There are currently duplicates, which should not
-- not exist, but they prevent creation of the unique index.
--CONSTRAINT ux_foo UNIQUE (OwnerId, OwnerRecordId)
)
OwnerRecordId is similar to an identity column within each OwnerId:
Id
OwnerId
OwnerRecordId
1
16
1
2
16
2
3
16
3
4
57
1
Now I would like to copy all records from ownerId 16 to ownerId 57. So OwnerId 57 would end up with 3 new records, and their OwnerRecordId would range from 2 - 4.
While this copying is taking places, other processes might be creating new records.
I thought about doing the following, but the sub-query seems slow:
insert into foo (OwnerId, SomeColumn, OwnerRecordId)
select
(57, SomeColumn, (select isnull(max(OwnerRecordId), 0) + 1
from foo where ownerId = 57)
from foo
where OwnerId = 16
Then I thought I could lock the table where OwnerId = 57. If I could do this I could lock those records, get the current maximum, and then use ROW_NUMBER in my select and add that to the MAX value I grabbed once.
Only, I can't seem to prevent other users from selecting from the table short of a table lock. Is there a way to lock records where colun OwnerId = 57? Doing so would prevent others from geting the current max(OwnerRecordId) + 1 value.
Perhas there is a better approach?
Certainly the unique index should be added, I can't do that at this point though.
The following code should hopefully do the correct amount of locking
insert into foo WITH (SERIALIZABLE)
(OwnerId, SomeColumn, OwnerRecordId)
select
57,
SomeColumn,
(select isnull(max(OwnerRecordId), 0) + 1
from foo with (SERIALIZABLE, UPDLOCK)
where ownerId = 57)
from foo
where OwnerId = 16;
SERIALIZABLE (which is a synonym for HOLDLOCK) will cause a range lock over all the rows where ownerId = 57, and UPDLOCK will cause that lock to be held until the end of the transaction.
You need an index on (OwnerId) with OwnerRecordId as another key column or as an INCLUDE, otherwise the whole table will get locked.
Do not fall into the trap of using XLOCK, it doesn't work unless you are actually modifying that table reference.
You say you have many different IDs to copy, in which case it would be more performant to do it in bulk.
Dump the list into a temp table (or a Table Valued Parameter), then do a joined update. Something like this
CREATE TABLE #tmp (SourceId int, TargetId int, primary key (SourceId, TargetId))
-- insert using statements or BULK INSERT or SqlBulkCopy etc
insert into foo WITH (SERIALIZABLE)
(OwnerId, SomeColumn, OwnerRecordId)
select
t.TargetId,
f.SomeColumn,
ISNULL(f2.mx, 0) + ROW_NUMBER() OVER (PARTITION BY f.OwnerId ORDER BY f.OwnerRecordId)
FROM foo f
JOIN #tmp t ON t.SourceId = f.OwnerId
LEFT JOIN (
SELECT f2.OwnerId, mx = MAX(f2.OwnerRecordId)
FROM foo f2 WITH (SERIALIZABLE, UPDLOCK)
GROUP BY f2.OwnerId
) f2 ON f2.OwnerId = t.TargetId;

insert a new record into a mysql table with one of the values incremented by 1

I've got the following table:
productId price
1 price_value1
2 price_value2
3 price_value3
I would like to insert a new product into the table and assign it a new productId. In this case its value equals to 4.
So I want my new table to look like so:
productId price
1 price_value1
2 price_value2
3 price_value3
4 price_value4
So as far as I understand, in order to do that I have to somehow retrieve the max value of productId and insert it using INSERT INTO mytable VALUES (productId + 1, price_value4).
But how do I find out the maximum value of productId?
I tried INSERT INTO mytable VALUES (SELECT MAX(productId) + 1 FROM mytable, price_value4) but it didn't work.
This should Work:
Select the max(productID) and price_value4 as a columns from mytable and insert the result.
INSERT INTO mytable (SELECT MAX(productId) + 1, 'price_value4' FROM mytable);
However, if you are not going to jump some number you can just add an auto increment id key to product_id and then you will have only to insert the price, the product ID will be incremented automatically..
This will do so :
ALTER TABLE mytable
MODIFY COLUMN `productId` INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT;
you can change INT(10) with the INT(5) for example depanding on the size you want to give to your productId column
EDIT :
In return to the OP question in comments why his solution wouldn't work
Some suggetions says you have to make the SELECT statment in insert always between parenthesis
INSERT INTO mytable VALUES ( (SELECT MAX(ID)+1 FROM mytable) , price_value4)
.. In my Case it Return
(1093): You can't specify target table
'mytable' for update in FROM clause
AND HERE IS WHY (Quoting From the documentation)
When selecting from and inserting into the same table, MySQL creates
an internal temporary table to hold the rows from the SELECT and then
inserts those rows into the target table. However, you cannot use
INSERT INTO t ... SELECT ... FROM t when t is a TEMPORARY table,
because TEMPORARY tables cannot be referred to twice in the same
statement
BUT there is away to overcome by using a query instead of the table itself in the FROM, which has the effect of copying the requested table values instead of referencing the one that you are updating..
INSERT INTO mytable VALUES (
(SELECT MAX(ID)+1 FROM (SELECT * FROM mytable ) as mytmp ),
'price_value4');
OR (Quoting From the documentation)
To avoid ambiguous column reference problems when the SELECT and the
INSERT refer to the same table, provide a unique alias for each table
used in the SELECT part, and qualify column names in that part with
the appropriate alias.
INSERT INTO mytable Values ( (SELECT MAX(ID)+1 FROM mytable as mytmp) , 'price_value4')
This is a duplicate question. In order to take advantage of the auto-incrementing capability of the column, do not supply a value for that column when inserting rows.
A simple syntax to create table
CREATE TABLE Product (
productId MEDIUMINT NOT NULL AUTO_INCREMENT,
price INT NOT NULL,
PRIMARY KEY (productid)
);
While inserting supplied default or leave column as blank or supplied value as NULL. Take a look at below code snippet.
INSERT INTO Product (price) VALUES
('10'),('20'),('4'),
('30');
refer this link

How to insert if not exists with selecting from same table?

I have my table schema in H2 db as follows:
create table if not exists Test ( id bigint not null,name varchar(255), primary key (id) );
alter table Test add constraint if not exists Test_NAME UNIQUE (name);
I want to insert a value for the name attribute as 'Default' if it does not exist in the table by selecting the latest id value from the table and increment it by one.
Example:
Do not insert if an entry for name = Default already exists.
ID | Name
1 | Default
Insert if an entry for name = Default does not exists.
ID | Name
1 | ABC
2 | XYZ
For the id column, find the max id and increment it by one. In this case, insert id=3 and name=Default.
My query is as follows:
INSERT INTO Test (id , name)
SELECT max(id) + 1, 'Default' from Test
WHERE NOT EXISTS (SELECT * FROM Test where name='Default');
However, it gives me an error saying:
NULL not allowed for column "ID"; SQL statement
as it applies the where condition on the inner select statement.
I also tried:
MERGE INTO Test KEY(name) VALUES (SELECT MAX(id) + 1 from Test, 'Default');
It gives an error because, merge tries to update with the new values.
If it finds 'Default', it will update the row with new id causing primary key violation.
Is there a better way to do this? How can I make the query work?
You are massively overcomplicating this. Define the id field as auto increment and place a unique index on the name field. The unique index prevents duplicate names to be inserted, while the auto increment increases the value of the id field by 1 (by default) if the insert is successful.
I updated id to auto increment and the following query work flawlessly
INSERT INTO Test (name) select * from (select 'Default') as tmp WHERE NOT EXISTS (SELECT name from Test where name='Default');
when you run your query first time, no record found in table so, it give error 'null' there, so if you add IFNULL() function there as below
INSERT INTO Test (id , name)
SELECT **IFNULL**(max(id),0) + 1, 'Default'
FROM Test
WHERE NOT EXISTS (SELECT * FROM Test where name='Default');

Mysql statement with unique id created

I would like to ask how the mysql statement should be written in which unique ids are created in individual result rows (but actually not existed in the table)
For example, the content of table 'ABC' is:
name type
John A
Mary B
Peter C
Fred A
Gary C
Susan D
Alan A
When mysql statement SELECT * from ABC where type = 'A' is used, the result is
name type
John A
Fred A
Alan A
But I would like to put unique ids in each row of the mysql result, but the ids are NOT actually put in the table. For example,
(newid) name type
1 John A
2 Fred A
3 Alan A
Then the row can be further searched by using the following mysql statement.
SELECT * from ABC WHERE (newid) = '1'
The result is:
(newid) name type
1 John A
Thanks.
You can add an autoincrement column
ALTER TABLE your_table ADD id INT NOT NULL AUTO_INCREMENT PRIMARY KEY;
You need to add a PRIMARY KEY AUTO_INCREMENT field in your table.
Do the following and check:
ALTER TABLE `your_table`
ADD COLUMN `id_primary` int NOT NULL AUTO_INCREMENT FIRST ,
ADD PRIMARY KEY (`id_primary`);
So in your case it would be like:
ALTER TABLE `ABC`
ADD COLUMN `id_primary` int NOT NULL AUTO_INCREMENT FIRST ,
ADD PRIMARY KEY (`id_primary`);
Note:
Here id_primary field is considered to be your newid
This is how your table looks like before this update. See Demo before altering table
After the update look at your table now See Demo after altering table
You can use variable to achieve that, but like #1000111 said, the right thing you should do is alter your table:
select *
from (
select abc.*, #newid := #newid + 1 as newid
from abc
cross join (select #newid := 0) t
where type = 'A'
) t1
where t1.newid = 1

mysql insert with value equal to primary key + 1

I have an auto increment column ID, and for some situation I wanted the other column to be equal to the primary key + 1 value
ID | other
1 | 2
2 | 3
3 | 4
4 | 123 (some situation, it is not always plus 1)
How can I achieve this?
Here's what I have tried
INSERT INTO table (`ID`,`other`) VALUES ('',(SELECT MAX(ID)+1 FROM table))
But that returns an error
You can't specify target table 'table' for update in FROM clause
Try Below query:
ALTER TABLE dbo.table ADD
Column AS ([ID]+1)
GO
It will definitely work
Using a normal AUTO_INCREMENT column as id, I cannot think of a way to do this in MySQL. Triggers, which otherwise would have been an option, don't work well with AUTO_INCREMENT columns.
The only way I see is to do two commands for an INSERT;
INSERT INTO bop (value) VALUES ('These values should be 1 and 2');
UPDATE bop SET other = id+1 WHERE id = LAST_INSERT_ID();
An SQLfiddle to test with.
The closest I'm getting to what you're looking for is to generate sequences separately from AUTO_INCREMENT using a function, and use that instead to generate the table id;
DELIMITER //
CREATE TABLE bop (
id INT UNIQUE,
other INT,
value VARCHAR(64)
)//
CREATE TABLE bop_seq ( seq INT ) // -- Sequence table
INSERT INTO bop_seq VALUES (1) // -- Start value
CREATE FUNCTION bop_nextval() RETURNS int
BEGIN
SET #tmp = (SELECT seq FROM bop_seq FOR UPDATE);
UPDATE bop_seq SET seq = seq + 1;
RETURN #tmp;
END//
CREATE TRIGGER bop_auto BEFORE INSERT ON bop
FOR EACH ROW
SET NEW.id = bop_nextval(), NEW.other=NEW.id + 1;
//
That'd let you do inserts and have it autonumber like you want. The FOR UPDATE should keep the sequence transaction safe, but I've not load tested so you may want to do that.
Another SQLfiddle.
I solved this by updating 2 times the DB..
I wanted to do +1 from 19 till ..
UPDATE `table` SET `id`=`id`+101 WHERE id <= 19
UPDATE `table` SET `id`=`id`-100 WHERE id <= 119 AND id >= 101