Derby Merge insert not working - sql

I wanted to create a SQL query which is working like INSERT IF NOT EXISTS ELSE UPDATE
I found that Derby is capable of MERGE and i tried to use it to solve my issue.
MERGE INTO test_table a
USING test_table b
ON a.city = 'foo'
WHEN NOT MATCHED THEN INSERT values ( 'foo', '2012-11-11', 'UK')
WHEN MATCHED AND a.modification_date > '1111-11-11' THEN
UPDATE SET a.modification_date = '2012-11-11',
a.city = 'foo1',
a.country = 'US'
The above statement is giving me the following error:
Error code 30000, SQL state 23505: The statement was aborted because it
would have caused a duplicate key value in a unique or primary key
constraint or unique index identified by 'SQL150129144920080' defined on 'test_table'
How ever i can run the following statement:
INSERT INTO test_table values ( 'foo', '2012-11-11', 'UK');
Which is proving that the above city does not exists in the table yet.
My table is contains the following structure:
CREATE TABLE test_table(
city VARCHAR(100) NOT NULL PRIMARY KEY,
modification_date DATE NOT NULL,
country VARCHAR(2) NOT NULL);
Any help or advice is greatly appreciated.

You have missed the following sentence from here
"The unqualified source table name (or its correlation name) may not be the same as the unqualified target table name (or its correlation name)."
that means you cannot use one table as source and target at the same time !
just one example:
we have two schemas: schema1 and schema2
and two tables: schema1.table1 and schema2.table1
--i have to write all details:
create schema schema1;
create table schema1.table1 (
name varchar(255) not null,
id int not null primary key
);
create schema schema2;
create table schema2.table1 (
name varchar(255) not null,
id int not null primary key
);
--suppose we have inserted some entries into schema2.table1
insert into schema2.table1 values
('foo', 1), ('bar', 2);
--and we want just to copy values from schema2.table1 into schema1.table1
--apply MERGE INTO ... INSERT ...
merge into schema1.table1 as tableTarget
using schema2.table1 as tableSrc
on tableTarget.id= tableSrc.id
when matched then
update set tableTarget.name=tableSrc.name
when not matched then
insert(name, id) values (tableSrc.name, tableSrc.id);
--that has to work

You are not joining table a to table b so the query is likely trying to do an insert for every row in table B, assuming table B contains a city field try
MERGE INTO test_table as a
USING test_table b
ON a.city = b.city and a.city = 'foo'
WHEN NOT MATCHED THEN INSERT values ( 'foo', '2012-11-11', 'UK')
WHEN MATCHED AND a.modification_date > '1111-11-11' THEN
UPDATE SET a.modification_date = '2012-11-11',
a.city = 'foo1',
a.country = 'US';

Related

INSERT fails with ORA-01400: cannot insert NULL

I am working with ORACLE SQL Developer and I created a table with an id as PK, another FK, id_columnx and a column1 and inserted data into them. Then I added another 2 columns, column 2 and column 3 and when I try to insert data into these new added columns, I get the error:
ORA-01400: cannot insert NULL.
I have to mention that I don't have any triggers on the table and i DO have values in the INSERT statement. There seems to be a conflict with the PK id, but I don't understand why.
So here is the code:
create table mytable(id INT PRIMARY KEY, name varchar2(30));
insert into mytable values (1, 'Mary');
insert into mytable values (2, 'John');
insert into mytable values (3, 'Bill');
alter table mytable
add email VARCHAR2(30);
alter table mytable
add addess VARCHAR2(30);
insert into mytable (email, addess)
values ('mary#gmail.com', 'Street X');
And here is the error I get:
Error starting at line : 12 in command -
insert into mytable (email, addess)
values ('mary#gmail.com', 'Street X')
Error report -
ORA-01400: cannot insert NULL into ("ZAMFIRESCUA_49"."MYTABLE"."ID")
INSERT is for inserting new rows, UPDATE is for altering data in the current rows. As mentioned in the comments, it looks like you want to be updating Mary's row with her email/address:
UPDATE mytable
SET email = 'mary#gmail.com',
address = 'Street X'
WHERE ID = 1 --Mary's ID for example, replace with the ID of the row you want to update
You could also use a subquery to find the right ID so you don't have to always look it up:
UPDATE mytable
SET email = 'mary#gmail.com',
address = 'Street X'
WHERE ID = (SELECT ID FROM mytable WHERE name = 'Mary')
Edit:
I was thinking there were two tables while writing this answer, you could always just use the name field as your filter:
UPDATE mytable
SET email = 'mary#gmail.com',
address = 'Street X'
WHERE name = 'Mary'
You're missing a PK value in your last INSERT after the ALTER statements. Try this:
create table mytable(id INT PRIMARY KEY, name varchar2(30));
insert into mytable values (1, 'Mary');
insert into mytable values (2, 'John');
insert into mytable values (3, 'Bill');
alter table mytable add email VARCHAR2(30);
alter table mytable add addess VARCHAR2(30);
insert into mytable (id, email, addess) values (4, 'mary#gmail.com', 'Street X');

Insert into a Informix table or update if exists

I want to add a row to an Informix database table, but when a row exists with the same unique key I want to update the row.
I have found a solution for MySQL here which is as follows but I need it for Informix:
INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE name="A", age=19
You probably should use the MERGE statement.
Given a suitable table:
create table table (id serial not null primary key, name varchar(20) not null, age integer not null);
this SQL works:
MERGE INTO table AS dst
USING (SELECT 1 AS id, 'A' AS name, 19 AS age
FROM sysmaster:'informix'.sysdual
) AS src
ON dst.id = src.id
WHEN NOT MATCHED THEN INSERT (dst.id, dst.name, dst.age)
VALUES (src.id, src.name, src.age)
WHEN MATCHED THEN UPDATE SET dst.name = src.name, dst.age = src.age
Informix has interesting rules allowing the use of keywords as identifiers without needing double quotes (indeed, unless you have DELIMIDENT set in the environment, double quotes are simply an alternative to single quotes around strings).
You can try the same behavior using the MERGE statement:
Example, creation of the target table:
CREATE TABLE target
(
id SERIAL PRIMARY KEY CONSTRAINT pk_tst,
name CHAR(1),
age SMALLINT
);
Create a temporary source table and insert the record you want:
CREATE TEMP TABLE source
(
id INT,
name CHAR(1),
age SMALLINT
) WITH NO LOG;
INSERT INTO source (id, name, age) VALUES (1, 'A', 19);
The MERGE would be:
MERGE INTO target AS t
USING source AS s ON t.id = s.id
WHEN MATCHED THEN
UPDATE
SET t.name = s.name, t.age = s.age
WHEN NOT MATCHED THEN
INSERT (id, name, age)
VALUES (s.id, s.name, s.age);
You'll see that the record was inserted then you can:
UPDATE source
SET age = 20
WHERE id = 1;
And test the MERGE again.
Another way to do it is create a stored procedure, basically you will do the INSERT statement and check the SQL error code, if it's -100 you go for the UPDATE.
Something like:
CREATE PROCEDURE sp_insrt_target(v_id INT, v_name CHAR(1), v_age SMALLINT)
ON EXCEPTION IN (-100)
UPDATE target
SET name = v_name, age = v_age
WHERE id = v_id;
END EXCEPTION
INSERT INTO target VALUES (v_id, v_name, v_age);
END PROCEDURE;

Merge with primary key violation

I have a file based import thingy where the users can post files to be imported in the database. New records are inserted and records with an already existing Id are updated.
After posting a file with
ID NAME
5 Silly
they can correct this by posting a new file with
ID NAME
5 Sally
I have a bulk insert (C# windows service) of the file into a bulk table (Sql Server Azure v12). The files can contain millions of rows so I'd like to avoid iterating through rows. After the bulk insert i have a SP that does a merge update / insert and updates already existing rows and inserts new ones.
The problem I've come across is when the users post a new record and a correction of the same record in the same file. I get a PRIMARY KEY VIOLATION on the target table.
Is there a nice way to solve this?
Here's an example:
--drop table #bulk
--drop table #target
create table #bulk(
id int,
name varchar(10)
)
insert into #bulk values (1,'John')
insert into #bulk values (2,'Sally')
insert into #bulk values (3,'Paul')
insert into #bulk values (4,'Gretchen')
insert into #bulk values (5,'Penny')
insert into #bulk values (5,'Peggy')
create table #target(
id int not null,
name varchar(10),
primary key (id))
merge #target as target
using(select id, name from #bulk) as bulktable
on target.id = bulktable.id
when matched then update
set target.name = bulktable.name
when not matched then
insert(id, name) values (bulktable.id, bulktable.name);
This will handle the latest value for name.
You need a new create script for #bulk
CREATE TABLE #bulk
(
row_id int identity(1,1),
id int,
name varchar(10)
)
This is the script you can use with the new bulk table:
;WITH CTE as
(
SELECT
id, name,
row_number() over (partition by id order by row_id desc) rn
FROM #bulk
), CTE2 as
(
SELECT id, name
FROM CTE
WHERE rn = 1
)
MERGE #target as target
USING CTE2 as bulktable
on target.id = bulktable.id
WHEN matched and
not exists(SELECT target.name except SELECT bulktable.name)
-- this will handle null values. Otherwise it could simply have been:
-- matched and target.name <> bulktable.name
THEN update
SET target.name = bulktable.name
WHEN not matched THEN
INSERT(id, name) VALUES (bulktable.id, bulktable.name);

Trigger After Update SQL

I have Customer table. To simplify lets say i have two columns
Id
Name
I have a second table (Log) that I want to update ONLY when the Id column of my customer changes. Yes you heard me right that the primary key (Id) will change!
I took a stab but the NewId that gets pulled is the first record in the Customer table not the updated record
ALTER TRIGGER [dbo].[tr_ID_Modified]
ON [dbo].[customer]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF UPDATE (Id)
BEGIN
UPDATE [log]
SET NewId = Id
FROM customer
END
END
Many would make the argument that if you are changing PK values, you need to rethink the database/table design. However, if you need a quick & dirty fix, add a column to the customer table that is unique (and not null). Use this column to join between the [inserted] and [deleted] tables in your update trigger. Here's a sample script:
CREATE TABLE dbo.Customer (
Id INT CONSTRAINT PK_Customer PRIMARY KEY,
Name VARCHAR(128),
UQColumn INT IDENTITY NOT NULL CONSTRAINT UQ_Customer_UQColumn UNIQUE
)
CREATE TABLE dbo.[Log] (
CustomerId INT NOT NULL,
LogMsg VARCHAR(MAX)
)
INSERT INTO dbo.Customer
(Id, Name)
VALUES
(1, 'Larry'),
(2, 'Curley'),
(3, 'Moe')
INSERT INTO dbo.[Log]
(CustomerId, LogMsg)
VALUES
(1, 'Larry is cool'),
(1, 'Larry rocks'),
(2, 'Curley cracks me up'),
(3, 'Moe is mean')
CREATE TRIGGER [dbo].[tr_Customer_Upd]
ON [dbo].[customer]
FOR UPDATE
AS
BEGIN
UPDATE l
SET CustomerId = i.Id
FROM inserted i
JOIN deleted d
ON i.UQColumn = d.UQColumn
JOIN [Log] l
ON l.CustomerId = d.Id
END
SELECT *
FROM dbo.[Log]
UPDATE dbo.Customer
SET Id = 4
WHERE Id = 1
SELECT *
FROM dbo.[Log]

Use autogenerated column to populate another column

How can I use an auto-generated column to populate another column during an INSERT statement?
Long story short: we are reusing a database table and an related ASP page to display completely different data than was originally intended.
I have a table similar in structure to the following. It's structure is out of my control.
ID int NON-NULL, IDENTITY(1,1)
OrderNo varchar(50) NON-NULL, UNIQUE
More ...
The table has been repurposed and we are not using the OrderNo column. However, it's NON-NULL and UNIQUE. As dummy data, I want to populate it with the row's ID column.
I have the following SQL so far, but can't work out how to use the row's generated ID.
INSERT INTO MyTable (OrderNo, More)
OUTPUT INSERTED.ID
VALUES (CAST(ID AS varchar(50)))
This just gives:
Msg 207, Level 16, State 1, Line 3
Invalid column name 'ID'.
Here's a solution using the OUTPUT clause. Unfortunately, you won't be able to do it in a single statement.
CREATE TABLE Orders (
ID int not null identity(1,1),
OrderNo varchar(50) not null unique
)
CREATE TABLE #NewIDs ( ID int )
INSERT Orders (OrderNo)
OUTPUT INSERTED.ID INTO #NewIDs
SELECT 12345
UPDATE o
SET o.OrderNo = i.ID
FROM Orders o
JOIN #NewIDs i
ON i.ID = o.ID
SELECT * FROM Orders
One option would be:
create trigger YourTable_Trigger
on YourTable
INSTEAD OF INSERT
as begin
INSERT INTO YourTable (OrderNo, AnotherField)
SELECT 0, AnotherField FROM Inserted
UPDATE YourTable SET OrderNo = SCOPE_IDENTITY() WHERE ID = SCOPE_IDENTITY()
end;
And here is the Fiddle.
Good luck.