I'm pretty new to MySQL. I have two related tables, quite common case: Klients(KID, name, surname) and Visits(VID, VKID, dateOfVisit) - VKID is the Klient ID. I have a problem with suitable INSERT query, this is what I want to do:
1.Check if Klient with specific name and surname exists (let's assume that there are no people with the same surnames)
2.If yes, get the ID and do the INSERT to Visits table
3.If no, INSERT new Klient, get the ID and INSERT to Visits.
Is it possible to do in one query?
You would need to use the IF EXIST / NOT EXISTS and use a subquery to check the table. See the reference bwlo
http://dev.mysql.com/doc/refman/5.0/en/exists-and-not-exists-subqueries.html
HTH
The INSERT statement allows only one single target table.
So the query you're looking for is just impossible unless you use triggers or stored procedures.
But such problem is commonly solved using the fallowing small algorithm:
1) insert a record in table [Visits] assuming the parent record does exist in table [Klients]
INSERT INTO Visits (VKID, dateOfVisit)
SELECT KID, NOW()
FROM Klients
WHERE (name=#name) AND (surname=#surname)
2) check the number of inserted records after query (1)
3) if no record has been inserted, then add a new record table [Klients], and then run (1) again.
try something like this
IF (SELECT * FROM `sometable` WHERE name = 'somename' AND surname = 'somesurname') IS NULL THEN
INSERT INTO Table1(name,surname) VALUES ('somename', 'somesurname');
ELSE INSERT INTO visits(kid,name,surname)
SELECT kid, name, surname FROM Table1 WHERE name = 'somename' AND surname = 'somesurname';
END IF;
there is no need to specify 'VALUES' on the second insert
i have not tested it, but this is the general idea of what you are trying to accomplish.
These should be two queries in a transaction:
INSERT INTO Klients (name, surname)
VALUES ('John', 'Doe')
ON DUPLICATE KEY UPDATE
KID = LAST_INSERT_ID(KID);
INSERT INTO Visits (VKID, dateOfVisits)
VALUES (LAST_INSERT_ID(), NOW());
The first statement is an upsert statement where the update part uses not widely known, but intented exactly for the purpose functionality of LAST_INSERT_ID(), where explicitly passed value is stored for getting the value afterwards.
UPD: I forgot to mention that you would need to add a unique constraint on (surname, name).
Related
I'm trying to write a java sql query, the simplified table would be table(name,version) with a unique constraint on (name, version).
I'm trying to insert a row into my database with a conditional statement. Meaning that when a entry with the same name exists, it should insert the row with same name and its version increased by 1.
I have tried with the following:
INSERT INTO table(name,version)
VALUES(?, CASE WHEN EXISTS(SELECT name from table where name=?)
THEN (SELECT MAX(version) FROM table WHERE name = ?) +1
ELSE 1 END)
values are sent by user.
My question is, how can I access the 'name' inside the values so I could compare them?
If you want to write this as a single query:
INSERT INTO table (name, version)
SELECT ?, COLAESCE(MAX(t2.version) + 1, 1)
FROM table t2
WHERE t2.name = ?;
That said, this is dangerous. Two threads could execute this query "at the same time" and possibly create the same version number. You can prevent this from happening by adding a unique index/constraint on (name, version).
With the unique index/constraint, one of the updates will fail if there is a conflict.
I see at least two approaches:
1. For each pair of name and version you first query the max version:
SELECT MAX(VERSION) as MAX FROM <table> WHERE NAME = <name>
And then you insert the result + 1 with a corresponding insert query:
INSERT INTO <table>(NAME,VERSION) VALUES (<name>,result+1)
This approach is very straight-forward, easy-to-read and implement, however, not really performant because of so many queries necessary.
You can achieve that with sql alone with sql analytics and window functions, e.g.:
SELECT NAME, ROW_NUMBER() over (partition BY NAME ORDER BY NAME) as VERSION FROM<table>
You can then save the result of this query as a table using CREATE TABLE as SELECT...
(The assumption here is that the first version is 1, if it is not the case, then one could slightly rework the query). This solution would be very performant even for large datasets.
You should get the name before insertion. In your case, if something went wrong then how would you know about it so you get the name before insert query.
Not sure but you try this:
declare int version;
if exists(SELECT name from table where name=?)
then
version = SELECT MAX(version) FROM table WHERE name = ?
version += 1
else
version = 1
end
Regards.
This is actually a bad plan, you might be changing what the user's specified data. That is likely to not be what is desired, maybe they're not trying to create a new version but just unaware that the one wanted already exists. But, you can create a function, which your java calls, not only inserts the requested version or max+1 if the requested version already exists. Moreover it returns the actual values inserted.
-- create table
create table nv( name text
, version integer
, constraint nv_uk unique (name, version)
);
-- function to create version or 1+max if requested exists
create or replace function new_version
( name_in text
, version_in integer
)
returns record
language plpgsql strict
as $$
declare
violated_constraint text;
return_name_version record;
begin
insert into nv(name,version)
values (name_in,version_in)
returning (name, version) into return_name_version;
return return_name_version;
exception
when unique_violation
then
GET STACKED DIAGNOSTICS violated_constraint = CONSTRAINT_NAME;
if violated_constraint like '%nv\_uk%'
then
insert into nv(name,version)
select name_in, 1+max(version)
from nv
where name = name_in
group by name_in
returning (name, version) into return_name_version;
return return_name_version;
end if;
end;
$$;
-- create some data
insert into nv(name,version)
select 'n1', gn
from generate_series( 1,3) gn ;
-- test insert existing
select new_version('n2',1);
select new_version('n1',1);
select *
from nv
order by name, version;
I am doing a Java program in which I am reading input from some files and if it contains any string that I have in my list I have to update the table with name and its count.
First i created a table as follows
create table mobile(name varchar2(20),count int,primary key(name));
for instance,if I read string like "Sony unviels its new phone", table must be updated with name as sony count as 1.
what my doubt is initially it is an empty table. Can we update it as I said.
Thanks in advance....
First table creation
CREATE TABLE mobile
(
t_name VARCHAR (20),
t_count NUMBER,
PRIMARY KEY (t_name)
);
And insert statement
INSERT INTO mobile
VALUES ('Sony', 1);
Single statement for insert and update
MERGE INTO mobile t
USING (SELECT *
FROM mobile
WHERE LOWER (t_name) = 'sony') s
ON (t.t_name = s.t_name)
WHEN MATCHED
THEN
UPDATE SET t.t_count = t_count+1
WHEN NOT MATCHED
THEN
INSERT (t_name, t_count)
VALUES ('sony', 1);
Either you prepopulate you table with your given word list so something like
CREATE TABLE mobile(
name varchar2(20),
count_column int,
primary key(name)
);
INSERT INTO mobile (name, count_column) VALUES
('Sony', 0);
or you are adding some magic to your Java code which is deciding whether to do an UPDATE or an INSERT.
As an simple would look like theoretical
UPDATE mobile SET count_column to count_column+1 where name = 'Sony';
this will fail if there is none entry given.
You will have to execute an UPSERT like query , since oracle doesn't have it, you have to do a workaround to resolve this. please check this .
I am using PreparedStatement to prepare sql queries. I want to insert a row in table if and only if it doesn't exist.
I tried this -
INSERT INTO users (userId) VALUES (?) ON DUPLICATE KEY UPDATE userId = ?
But this will unnecessarily update the userId.
How can i insert the userId here ?
INSERT INTO users
(userId)
SELECT ?
WHERE NOT EXISTS
(
SELECT *
FROM users
where userId = ?
)
You may use
INSERT IGNORE INTO users (userId) VALUES (?)
But you should understand why do you want ignore errors.
on duplicate key does not work correctly when the table is an innodb. It creates exactly the problem you are describing. If you need the functionality of an innodb, then should you first check the existence of the row, otherwise can you convert the table to a myisam table.
edit: if you need to check the existence of the row before the decision to insert or update, then would I advice you to use a stored procedure.
DELIMITER //
CREATE PROCEDURE adjustusers(IN pId int)
BEGIN
DECLARE userid int;
select count(id) into userid from users where id = pId;
if userid = 1 then
update users set id = pId where id = pId;
else
insert into users(id) values(pId);
end if;
END //
DELIMITER ;
A stored procedure is precompiled, just as a prepared statement. Hence no SQL injection problems and some more functionality and only one call to the database.
In MySQL, if you specify ON DUPLICATE KEY UPDATE and a row is inserted that would cause a duplicate value in a UNIQUE index or PRIMARY KEY, an UPDATE of the old row is performed. For example, if column a is declared as UNIQUE and contains the value 1, the following two statements have identical effect:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
UPDATE table SET c=c+1 WHERE a=1;
I don't believe I've come across anything of the like in T-SQL. Does SQL Server offer anything comparable to MySQL's ON DUPLICATE KEY UPDATE?
I was surprised that none of the answers on this page contained an example of an actual query, so here you go:
A more complex example of inserting data and then handling duplicate
MERGE
INTO MyBigDB.dbo.METER_DATA WITH (HOLDLOCK) AS target
USING (SELECT
77748 AS rtu_id
,'12B096876' AS meter_id
,56112 AS meter_reading
,'20150602 00:20:11' AS time_local) AS source
(rtu_id, meter_id, meter_reading, time_local)
ON (target.rtu_id = source.rtu_id
AND target.time_local = source.time_local)
WHEN MATCHED
THEN UPDATE
SET meter_id = '12B096876'
,meter_reading = 56112
WHEN NOT MATCHED
THEN INSERT (rtu_id, meter_id, meter_reading, time_local)
VALUES (77748, '12B096876', 56112, '20150602 00:20:11');
There's no DUPLICATE KEY UPDATE equivalent, but MERGE and WHEN MATCHED might work for you
Inserting, Updating, and Deleting Data by Using MERGE
You can try the other way around. It does the same thing more or less.
UPDATE tablename
SET field1 = 'Test1',
field2 = 'Test2'
WHERE id = 1
IF ##ROWCOUNT = 0
INSERT INTO tablename
(id,
field1,
field2)
VALUES (1,
'Test1',
'Test2')
SQL Server 2008 has this feature, as part of TSQL.
See documentation on MERGE statement here - http://msdn.microsoft.com/en-us/library/bb510625.aspx
SQL server 2000 onwards has a concept of instead of triggers, which can accomplish the wanted functionality - although there will be a nasty trigger hiding behind the scenes.
Check the section "Insert or update?"
http://msdn.microsoft.com/en-us/library/aa224818(SQL.80).aspx
I would like to use the db2 merge statement submitting it as a statement from jdbc.
I am in the following scenario. I'm working with a proprietary persistence layer and I'm handling an entity I don't know whether it's already persisted or not and I would like to use the merge statement in order to insert or update a row on the database.
Is it possible?
Suppose I'm working with the table people with three columns: id, name, surname and I'm handling an entity with id="5", name="chuck", surname="norris" Am I able to issue:
MERGE INTO people AS t
USING (select '5' as id, 'chuck' as name, 'norris' as surname from SYSIBM.SYSDUMMY1)As s
ON (t.id = s.id)
WHEN MATCHED THEN
UPDATE SET t.name=s.name, t.surmane=s.surname
WHEN NOT MATCHED THEN
INSERT
(id, name, surname)
VALUES (s.id, s.name, s.surname)
such a statement? I'm trying to do that but I got an error. I don't think it's allowed to use a select after USING:
USING (select '5' as id, 'chuck' as name, 'norris' as surname from SYSIBM.SYSDUMMY1)As s
I also tryed to do:
USING VALUES('5','chuck','norris') AS s(id,chuck,norris)
but it dosn't work. Any help would be appreciated.
Besides, does anybody know if it's possible to use such a statement in a prepared statement, replacing the real values expressed into the USING part with '?' placeholders in order to set them to the prepared statement using the setXXX() methods?
Thanks
Thanks
Fil
The syntax for MERGE for your data would be something like this, assuming you're using DB2 Linux/Unix/Windows (LUW). The VALUES clause goes inside the parenthesis for the USING part.
Also, if you are using LUW, you cannot dynamically prepare a MERGE (I.E., your query can't have parameter markers) in LUW 9.5 or less. This was added in LUW 9.7.
MERGE INTO people AS t USING (
VALUES (5, 'Chuck', 'Norris'),
(6, 'John', 'Smith'),
(7, 'Abraham', 'Lincoln')
-- maybe more rows
) AS s (id, name, surname)
ON t.id = s.id
WHEN MATCHED THEN
UPDATE SET t.name=s.name, t.surname=s.surname
WHEN NOT MATCHED THEN
INSERT (id, name, surname)
VALUES (s.id, s.name, s.surname)
However, your actual problem with the fullselect may be that you have some typos in your query... for example "surmane" in UPDATE SET t.name=s.name, t.surmane=s.surname