I looked into MySQL duplicate key but cant figure it out.
I have a table like below:
id series chapter path(can be unique)
I want only insert data and not update. Lets say I have data like below:
seri:Naruto, klasor:567 ==> If both of these exist in table then do not insert.
seri:Naruto, klasor:568 ==> If Naruto exist but 568 does not exist then do insert.
How can I achieve this?
Easiest way would be to define unique index with two columns on that table:
ALTER TABLE yourtable ADD UNIQUE INDEX (seri,klasor);
You may also define two column primary key, which would work just as well.
Then use INSERT IGNORE to only add rows when they will not be duplicates:
INSERT IGNORE INTO yourtable (seri, klasor) VALUES ('Naruto',567);
INSERT IGNORE INTO yourtable (seri, klasor) VALUES ('Naruto',568);
Edit: As per comments, you can't use UNIQUE INDEX which complicates things.
SET #seri='Naruto';
SET #klasor=567;
INSERT INTO yourtable
SELECT seri,klasor FROM (SELECT #seri AS seri, #klasor AS klasor)
WHERE NOT EXISTS (SELECT seri, klasor FROM yourtable WHERE seri=#seri AND klasor=#klasor);
You may use the above query with two local variables or convert it to single statement by replacing the local variables with actual values.
Better way would be to use stored procedure:
CREATE PROCEDURE yourinsert (vseri VARCHAR(8), vklasor INT)
BEGIN
DECLARE i INT;
SELECT COUNT(*) INTO i FROM yourtable WHERE seri=vseri AND klasor=vklasor;
IF i=0 THEN
INSERT INTO yourtable (seri,klasor) VALUES (vseri, vklasor);
END IF;
END;
This would allow you to perform the INSERT using:
CALL yourinsert('Naruto',567);
INSERT INTO table_name (seri, klasor) VALUES ('Naruto',567)
WHERE NOT EXISTS( SELECT seri,klasor FROM table_name WEHERE seri='Naruto' AND klasor=567
)
Hope this helps..
I create a script/workflow exportation/importation from 2 system.
I have Table1 {id, name, description}
I want to create a script (not a procedure). I could (I didnt succed) adding procedure into my workflow. (create and delete at the end)
id is auto increment
I cant change the table
I can be sure that between the time I start execution of my script and the end, there will not be an insertion of one of my items into the database.
The script insert {name,description} but I want to NOT insert if the element (name or name and description) is there.
BASE QUERY :
INSERT INTO TABLE1 (name,description) VALUES ('itemX','this is item X')
BASE Script :
Use database1;
BEGIN;
SELECT * FROM TABLE1 ;
SELECT * FROM TABLE3 ;
INSERT INTO TABLE1 (name,description) VALUES ('itemX','this is item X');
set #idTable1 = LAST_INSERT_ID();
INSERT INTO TABLE3 (idTable1,idTable2) VALUES (#idTable1,1);
INSERT INTO TABLE3 (idTable1,idTable2) VALUES (#idTable1,2);
SELECT * FROM TABLE1 ;
SELECT * FROM TABLE3 ;
ROLLBACK;
I want to protect the multiple insertion on TABLE1. But without changing the table.
Maybe I did it wrong
I tried IF but not working outside procedure.
I tried IGNORE (valid only if id is the same, but never the same, its
auto increment)
I tried WHEN
I tried ON DUPLICATE KEY
Because of #idTable1, I will need change the " set #idTable1 = LAST_INSERT_ID();" if I doesnt have if else. But if my item is the only one with the same "name", I can get this instead of last_insert_id.
I opted for creating procedure before my "BEGIN" and removed them at the end of the script.
Just create the table with name as primary key, then be sure that you take care of the key capitalization (uppercase or lowercase) to avoid duplicates.
CREATE TABLE TABLE1(
id INT AUTO_INCREMENT,
name CHAR(30),
description CHAR(100),
PRIMARY KEY (name)
)
create unique constraint on name field if possible.
Otherwise, create trigger before insert in order to ignore duplicate insertion.
Trigger for checking duplicate on two fields a and b:
delimiter //
drop trigger if exists aborting_trigger //
create trigger aborting_trigger before insert on t
for each row
begin
set #found := false;
select true into #found from t where a=new.a and b=new.b;
if #found then
signal sqlstate '45000' set message_text = 'duplicate insert';
end if;
end //
delimiter ;
The trigger here provides feature similar to unique constraint. After creation you should use INSERT IGNORE or INSERT ...ON DUPLICATE KEY UPDATE
I am trying to find an equivalent of the following MySql query in Sql Server (2012)?
INSERT INTO mytable (COL_A, COL_B, COL_C, COL_D)
VALUES ( 'VAL_A','VAL_B', 'VAL_C', 'VAL_D')
ON DUPLICATE KEY UPDATE COL_D= VALUES(COL_D);
Can anyone help?
PS. I have read that MERGE query has similar function, but I find the syntax of that very different.
You are basically looking for an Insert or Update pattern sometimes referred to as an Upsert.
I recommend this: Insert or Update pattern for Sql Server - Sam Saffron
For a procedure that will be dealing with single rows, either these transactions would work well:
Sam Saffron's First Solution (Adapted for this schema):
begin tran
if exists (
select *
from mytable with (updlock,serializable)
where col_a = #val_a
and col_b = #val_b
and col_c = #val_c
)
begin
update mytable
set col_d = #val_d
where col_a = #val_a
and col_b = #val_b
and col_c = #val_c;
end
else
begin
insert into mytable (col_a, col_b, col_c, col_d)
values (#val_a, #val_b, #val_c, #val_d);
end
commit tran
Sam Saffron's Second Solution (Adapted for this schema):
begin tran
update mytable with (serializable)
set col_d = #val_d
where col_a = #val_a
and col_b = #val_b
and col_c = #val_c;
if ##rowcount = 0
begin
insert into mytable (col_a, col_b, col_c, col_d)
values (#val_a, #val_b, #val_c, #val_d);
end
commit tran
Even with a creative use of IGNORE_DUP_KEY, you'd still be stuck having to use an insert/update block or a merge statement.
A creative use of IGNORE_DUP_KEY - Paul White #Sql_Kiwi
update mytable
set col_d = 'val_d'
where col_a = 'val_a'
and col_b = 'val_b'
and col_c = 'val_c';
insert into mytable (col_a, col_b, col_c, col_d)
select 'val_a','val_b', 'val_c', 'val_d'
where not exists (select *
from mytable with (serializable)
where col_a = 'val_a'
and col_b = 'val_b'
and col_c = 'val_c'
);
The Merge answer provided by Spock should do what you want.
Merge isn't necessarily recommended. I use it, but I'd never admit that to #AaronBertrand.
Use Caution with SQL Server's MERGE Statement - Aaron Bertrand
Can I optimize this merge statement - Aaron Bertrand
If you are using indexed views and MERGE, please read this! - Aaron Bertrand
An Interesting MERGE Bug - Paul White
UPSERT Race Condition With Merge
Try this...
I've added comments to try and explain what happens where in a SQL Merge statement.
Source : MSDN : Merge Statement
The Merge Statement is different to the ON DUPLICATE KEY UPDATE statement in that you can tell it what columns to use for the merge.
CREATE TABLE #mytable(COL_A VARCHAR(10), COL_B VARCHAR(10), COL_C VARCHAR(10), COL_D VARCHAR(10))
INSERT INTO #mytable VALUES('1','0.1', '0.2', '0.3'); --<These are the values we'll be updating
SELECT * FROM #mytable --< Starting values (1 row)
MERGE #mytable AS target --< This is the target we want to merge into
USING ( --< This is the source of your merge. Can me any select statement
SELECT '1' AS VAL_A,'1.1' AS VAL_B, '1.2' AS VAL_C, '1.3' AS VAL_D --<These are the values we'll use for the update. (Assuming column COL_A = '1' = Primary Key)
UNION
SELECT '2' AS VAL_A,'2.1' AS VAL_B, '2.2' AS VAL_C, '2.3' AS VAL_D) --<These values will be inserted (cause no COL_A = '2' exists)
AS source (VAL_A, VAL_B, VAL_C, VAL_D) --< Column Names of our virtual "Source" table
ON (target.COL_A = source.VAL_A) --< This is what we'll use to find a match "JOIN source on Target" using the Primary Key
WHEN MATCHED THEN --< This is what we'll do WHEN we find a match, in your example, UPDATE COL_D = VALUES(COL_D);
UPDATE SET
target.COL_B = source.VAL_B,
target.COL_C = source.VAL_C,
target.COL_D = source.VAL_D
WHEN NOT MATCHED THEN --< This is what we'll do when we didn't find a match
INSERT (COL_A, COL_B, COL_C, COL_D)
VALUES (source.VAL_A, source.VAL_B, source.VAL_C, source.VAL_D)
--OUTPUT deleted.*, $action, inserted.* --< Uncomment this if you want a summary of what was inserted on updated.
--INTO #Output --< Uncomment this if you want the results to be stored in another table. NOTE* The table must exists
;
SELECT * FROM #mytable --< Ending values (2 row, 1 new, 1 updated)
Hope that helps
You can simulate a near identitical behaviour using an INSTEAD OF TRIGGER:
CREATE TRIGGER tMyTable ON MyTable
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
SELECT i.COL_A, i.COL_B, i.COL_C, i.COL_D,
CASE WHEN mt.COL_D IS NULL THEN 0 ELSE 1 END AS KeyExists
INTO #tmpMyTable
FROM INSERTED i
LEFT JOIN MyTable mt
ON i.COL_D = mt.COL_D;
INSERT INTO MyTable(COL_A, COL_B, COL_C, COL_D)
SELECT COL_A, COL_B, COL_C, COL_D
FROM #tmpMyTable
WHERE KeyExists = 0;
UPDATE mt
SET mt.COL_A = t.COL_A, mt.COL_B = t.COL_B, mt.COL_C = t.COL_C
FROM MyTable mt
INNER JOIN #tmpMyTable t
ON mt.COL_D = t.COL_D AND t.KeyExists = 1;
END;
SqlFiddle here
How it works
We first project a list of all rows being attempted to be inserted into the table into a #temp table, noting which of those ARE already in the underlying table via a LEFT OUTER JOIN on the key column(s) COL_D which detect the duplication criteria.
We then need to repeat the actual work of an INSERT statement, by inserting those rows which are not already in the table (because of the INSTEAD OF, we have removed the responsibility of insertion from the engine and need to do this ourselves).
Finally, we update all non-key columns in the matched rows with the newly 'inserted' data.
Salient Points
It works under the covers, i.e. any insert into the table while the trigger is enabled will be subject to the trigger (e.g. Application ORM, other stored procedures etc). The caller will generally be UNAWARE that the INSTEAD OF trigger is in place.
There must be a key of sorts to detect the duplicate criterion (natural or surrogate). I've assumed COL_D in this case, but it could be a composite key. (Key but cannot be IDENTITY for obvious reasons, since the client wouldn't be inserting an Identity)
The trigger works for both single and multiple row INSERTS
NB
The standard disclaimers with triggers apply, and more so with INSTEAD OF triggers - as this can cause surprising changes in observable behaviour of Sql Server, such as this - even well intended INSTEAD OF triggers can cause hours of wasted effort and frustration for developers and DBA's who are not aware of their presence on your table.
This will affect ALL inserts into the table. Not just yours.
Stored Procedure will save the day.
Here I assume that COL_A and COL_B are unique columns and are type of INT
NB! Don't have sql-server instance ATM so cannot guarantee correctness of the syntax.
UPDATE! Here is a link to SQLFIDDLE
CREATE TABLE mytable
(
COL_A int UNIQUE,
COL_B int UNIQUE,
COL_C int,
COL_D int,
)
GO
INSERT INTO mytable (COL_A, COL_B, COL_C, COL_D)
VALUES (1,1,1,1),
(2,2,2,2),
(3,3,3,3),
(4,4,4,4);
GO
CREATE PROCEDURE updateDuplicate(#COL_A INT, #COL_B INT, #COL_C INT, #COL_D INT)
AS
BEGIN
DECLARE #ret INT
SELECT #ret = COUNT(*)
FROM mytable p
WHERE p.COL_A = #COL_A
AND p.COL_B = #COL_B
IF (#ret = 0)
INSERT INTO mytable (COL_A, COL_B, COL_C, COL_D)
VALUES ( #COL_A, #COL_B, #COL_C, #COL_D)
IF (#ret > 0)
UPDATE mytable SET COL_D = #COL_D WHERE col_A = #COL_A AND COL_B = #COL_B
END;
GO
Then call this procedure with needed values instead of Update statement
exec updateDuplicate 1, 1, 1, 2
GO
SELECT * from mytable
GO
There's no DUPLICATE KEY UPDATE equivalent in sql server,but you can use merged and when matched of sql server to get this done ,have a look here:
multiple operations using merge
Hoping someone can help me with a mysql query
Here’s what I have:
I table with a column “networkname” that contains data like this:
“VLAN-338-Network1-A,VLAN-364-Network2-A,VLAN-988-Network3-A,VLAN-1051-Network4-A”
I need a MySQL query that will update that column with only the vlan numbers in ascending order, stripping out everything else. ie.
“338, 364, 988, 1051”
Thanks,
David
In this script, I create a procedure to loop through the networkname values and parse out the numbers to a separate table, and then update YourTable using a group_concat function. This assumes your networkname values follow the 'VLAN-XXX' pattern in your example where 'XXX' is the 3-4 digit number you want to extract. This also assumes each record has a unique ID.
CREATE PROCEDURE networkname_parser()
BEGIN
-- load test data
drop table if exists YourTable;
create table YourTable
(
ID int not null auto_increment,
networkname nvarchar(100),
primary key (ID)
);
insert into YourTable(networkname) values
('VLAN-338-Network1-A,VLAN-364-Network2-A,VLAN-988-Network3-A,VLAN-1051-Network4-A'),
('VLAN-231-Network1-A,VLAN-4567-Network2-A'),
('VLAN-9876-Network1-A,VLAN-321-Network2-A,VLAN-1678-Network3-A');
-- add commas to the end of networkname for parsing
update YourTable set networkname = concat(networkname,',');
-- parse networkname into related table
drop table if exists ParseYourString;
create table ParseYourString(ID int,NetworkNumbers int);
while (select count(*) from YourTable where networkname like 'VLAN-%') > 0
do
insert into ParseYourString
select ID,replace(substr(networkname,6,4),'-','')
from YourTable
where networkname like 'VLAN-%';
update YourTable
set networkname = right(networkname,char_length(networkname)-instr(networkname,','))
where networkname like 'VLAN-%';
end while;
-- update YourTable.networkname with NetworkNumbers
update YourTable t
inner join (select ID,group_concat(networknumbers order by networknumbers asc) as networknumbers
from ParseYourString
group by ID) n
on n.ID = t.ID
set t.networkname = n.networknumbers;
END//
Call to procedure and select the results:
call networkname_parser();
select * from YourTable;
SQL Fiddle: http://www.sqlfiddle.com/#!2/01c77/1
I have a table with floor names that I need to replace with numerical values. I'm building a lookup table by hand. Not sure what to do next... Is there a better way?
Screenshot: http://i49.tinypic.com/2mc921e.png
BEGIN TRY
BEGIN TRANSACTION
-- lookup table
DECLARE #FloorLkup TABLE(
FloorName VARCHAR(MAX) NOT NULL,
FloorNum INT NOT NULL
);
INSERT INTO #FloorLkup SELECT 'First floor', '1'
INSERT INTO #FloorLkup SELECT 'First', '1'
INSERT INTO #FloorLkup SELECT 'Second floor', '2'
INSERT INTO #FloorLkup SELECT 'Second', '2'
-- etc.
INSERT INTO #FloorLkup SELECT 'Ninth', '9'
print 'Done'
COMMIT TRANSACTION
END TRY
BEGIN CATCH
print 'Did not work'
ROLLBACK
END CATCH
Also, problem is: some floors are named First and First floor, etc.
-- populate your #FloorLkup table here
-- now add a new numeric column:
ALTER TABLE dbo.UnknownTableName
ADD FloorNumber INT;
-- now update based on matches in #FloorLkup
UPDATE t
SET FloorNumber = fl.FloorNum
FROM dbo.UnknownTableName AS t
INNER JOIN #FloorLkup AS fl
ON t.FloorName = fl.FloorName;
-- check your work
SELECT * FROM dbo.UnknownTableName;
-- if it's correct:
ALTER TABLE dbo.UnknownTableName
DROP COLUMN FloorName;
You'll have to replace UnknownTableName with the name of your destination table, since you forgot to tell us. You may also need to remove any constraints or other explicit references to the FloorName column before you can drop it.
Also please don't be afraid of vowels or longer names. Lookup and FloorNumber are actually a LOT easier to type than Lkup and FloorNum.