How to effectively Merge in sql - sql

I have a source table.
id description test
12 this is the some value 0
10 this is the old value 5
And a temp table.(consists of latest changes which needed to be updated in source table)
id description test
10 this is the new value 15
14 this is the some new value 5
Desired output is-> select * from source_table; should give me:
id description test
10 this is the new value 15
14 this is the some new value 5
12 this is the some value 0
Trying to use something like this-
merge into source_table using temp_table
on source_table.id = temp_table.id
when matched then
delete;
insert into source_table (select * from temp_table);
Any suggestions how to achieve this in 1 single query(I am trying to avoid update) ?

If you want a query, you can run:
select id, description, test
from temp
union all
select id, description, test
from source
where not exists (select 1 from temp t where source.id = temp.id);
If you want to modify the data:
merge into source_table
using temp_table
on source_table.id = temp_table.id
when matched then
update set source_table.test = temp_table.test
when not matched then
insert into source_table (id, description, test)
values (temp_table.id, temp_table.description, temp_table.test);
Your naming convention is a little confusing relative to the normal documentation for merge. Typical naming would call "target" what you are calling "source", and then "source" is the new data in temp_table.

Related

Insert/Update a Column of Null Values with a Column From a View or Table

I'm having difficulty updating a column in a table which contains NULLS after a previous INSERT. The easiest way to illustrate my predicament is with an example:
CREATE TABLE TEST(p_ID INT, T varchar(50))
INSERT INTO TEST(p_ID)
SELECT *
FROM vw_MyView -- Contains a column of ints, but I will use strings here
The output as expected
SELECT * FROM TEST
p_ID T
6 NULL
7 NULL
8 NULL
9 NULL
10 NULL
11 NULL
12 NULL
13 NULL
14 NULL
My first attempt to add data was to insert the values from the view into (T)
INSERT INTO TEST(T)
SELECT *
FROM vw_MyView
WHERE T IS NULL -- Wrong! "Invalid column name 'T'."
But of course, the WHERE clause goes with the view... Thanks, Google.
My second attempt was to use UPDATE
BEGIN TRAN
UPDATE TEST
SET TEST.T = T.ID
FROM
(
SELECT *
FROM vw_MyView
) T
WHERE TEST.T IS NULL -- Why is this allowed here?
SELECT * FROM TEST
ROLLBACK TRAN
Which also fails because it only uses the first row of the view to update the column
p_ID T
6 a
7 a
8 a
9 a
10 a
11 a
12 a
13 a
14 a
The desired output is
p_ID T
6 a
7 b
8 c
9 d
10 e
11 f
12 g
13 h
14 i
Please note the (p_ID) column does not exist in the actual table, I only used it for a quick and dirty example. Besides the obvious question of how to
update/insert the desired values to the column, I feel that I've my have a flaw in my conception of when and how to use UPDATE or INSERT and WHERE. Any help would be great. Thanks.
UPDATE
I've updated my example to clear up some confusion as to the p_ID column, it doesn't exist in the table, I wish it did... My main goal is to get the values from the view to update the NULL values in the column (T). Thanks.
I think you're looking for
INSERT INTO Test(p_ID, T)
SELECT IntCol, IntCol
FROM vw_MyView;
And according to the desired output, if you're going to UPDATE the table then
UPDATE Test
SET T = p_Id;
Demo
Update test with data from vw_MyView
update t
set T = v.charCol
from test t
join vw_MyView v on t.p_ID = v.IntCol;
The mistake is to use *.
Try to always accurately list the required fields. That is, in your case, you need to rewrite it as follows:
CREATE TABLE TEST (p_ID INT, T varchar (50));
INSERT INTO TEST (p_ID)
SELECT p_ID
FROM vw_MyView;
Similarly, in defining views, avoid using *, always list required fields explicitly
Update:
START TRANS
UPDATE t
SET t.T=mv.ID
FROM TEST as t
INNER JOIN vw_MyView as mv on t.Key=mv.Key
and t.T IS NULL;
ROLLBACK TRAN
This answers the original version of the question.
You seem to want something like this:
INSERT INTO TEST (p_id, T)
SELECT v.?, v.? -- ? is the column name for the view
FROM vw_MyView v;
Or, if you already have the data in the table, you don't need the view at all:
UPDATE t
SET t.T = t.ID
FROM TEST t
WHERE t.T IS NULL ;

Oracle Merge/Insert

I'm trying to see if there is an existing 'IMPORTID' in a table consisting of two columns: 'IMPORTID','STEP'. If there is, increment the record's STEP column by 1.
I've just tested using hardcoded values and there are no rows in the table currently. I'm expecting the following SQL to insert an entry but nothing is happening.
MERGE into TOSTEP t
USING
(SELECT 'Test' IMPORTID, 1 STEP FROM TOSTEP) s
ON
(s.IMPORTID = t.IMPORTID)
WHEN MATCHED THEN
update set t.STEP = s.STEP +1
WHEN NOT MATCHED THEN
insert (IMPORTID,STEP)
values ('Test',1);
You are merging an empty table with itself..meaning there is no data selected in using clause. Change (SELECT 'Test' IMPORTID, 1 STEP FROM TOSTEP) s into (SELECT 'Test' IMPORTID, 1 STEP FROM DUAL) s and it should work
FULL CODE:
MERGE into TOSTEP t
USING
(SELECT 'Test' IMPORTID, 1 STEP FROM DUAL) s
ON
(s.IMPORTID = t.IMPORTID)
WHEN MATCHED THEN
update set t.STEP = s.STEP +1
WHEN NOT MATCHED THEN
insert (IMPORTID,STEP)
values (s.IMPORTID, s.STEP);

SQLite select average from one table and update average field from another table using trigger

I have some problems with creating a trigger that will update values in a second table with average values from the first table. Since i am not very familiar with sqlite statements, maybe you guys can help me.
Here is my problem : i have one table table_1
and another table table_2
What i want is that table_2 "average_value" field to be updated with the average of values from table_1 that have the same field.
In this particular case resulting the aveage_value field in table 2 like this :
5 - for field_a
8 - for field_b
0 - for field_c
Any suggestions will help me alot ! Thank You.
To compute the average value of a specific field dynamically, use this query:
SELECT AVG(value)
FROM table_1
WHERE field = 'field_a';
If you want instead a view that looks like your table_2, you can define it like this:
CREATE VIEW view_2 AS
SELECT field,
AVG(value) AS average_value
FROM table_1
GROUP BY field;
If you really want table_2 to be an actual table, you could compute the average values like this:
UPDATE table_2
SET average_value = (SELECT AVG(value)
FROM table_1
WHERE table_1.field = table_2.field);
If you want table_2 to be an actual table, and want to update only those values that have changed, use a trigger like this:
CREATE TRIGGER update_average_value_after_insert
AFTER INSERT ON table_1
FOR EACH ROW
BEGIN
UPDATE table_2
SET average_value = (SELECT AVG(value)
FROM table1
WHERE table1.field = NEW.field)
WHERE field = NEW.field;
END;
(You also need similar triggers for UPDATE and DELETE.)

SQL With... Update

Is there any way to do some kind of "WITH...UPDATE" action on SQL?
For example:
WITH changes AS
(...)
UPDATE table
SET id = changes.target
FROM table INNER JOIN changes ON table.id = changes.base
WHERE table.id = changes.base;
Some context information: What I'm trying to do is to generate a base/target list from a table and then use it to change values in another table (changing values equal to base into target)
Thanks!
You can use merge, with the equivalent of your with clause as the using clause, but because you're updating the field you're joining on you need to do a bit more work; this:
merge into t42
using (
select 1 as base, 10 as target
from dual
) changes
on (t42.id = changes.base)
when matched then
update set t42.id = changes.target;
.. gives error:
ORA-38104: Columns referenced in the ON Clause cannot be updated: "T42"."ID"
Of course, it depends a bit what you're doing in the CTE, but as long as you can join to your table withint that to get the rowid you can use that for the on clause instead:
merge into t42
using (
select t42.id as base, t42.id * 10 as target, t42.rowid as r_id
from t42
where id in (1, 2)
) changes
on (t42.rowid = changes.r_id)
when matched then
update set t42.id = changes.target;
If I create my t42 table with an id column and have rows with values 1, 2 and 3, this will update the first two to 10 and 20, and leave the third one alone.
SQL Fiddle demo.
It doesn't have to be rowid, it can be a real column if it uniquely identifies the row; normally that would be an id, which would normally never change (as a primary key), you just can't use it and update it at the same time.

SQL merge command on one table

I am trying to get my head over MERGE sql statement. What I want to achieve is:
Insert new values into the CSScolorOrders table but update corQuantity column if the record with colID and ordID already exist
This is what I ended up with:
MERGE INTO CSScolorOrders AS TARGET
USING (SELECT * FROM CSScolorOrders WHERE ordID = 3) AS SOURCE
ON (SOURCE.colID = 1) WHEN
MATCHED THEN UPDATE SET corQuantity = 1
WHEN
NOT MATCHED BY TARGET
THEN INSERT (colID, ordID, corQuantity) VALUES (1, 3, 1);
Unfortunately it does not raise any exception so I do not know why it doesn't work.
As discussed here, you'll see that a merge is exactly as it sounds. taking two tables and searching for the value you joined them on lets call it "X". if X is a match then you perform an update on that record. If it does not exist then you would perform an insert on the targeted table using the values selected.
In your case i'm not entirely sure if your join
( ON (SOURCE.colID = 1) )
is correct. i'm pretty sure this needs to be
on(Source.colID = Target.colID)
So the full statement should be this:
MERGE INTO CSScolorOrders AS TARGET
USING (SELECT * FROM CSScolorOrders WHERE ordID = 3) AS SOURCE
on(Source.colID = Target.colID)
WHEN MATCHED THEN
UPDATE SET corQuantity = 1
WHEN NOT MATCHED BY TARGET
THEN INSERT (colID, ordID, corQuantity) VALUES (1, 3, 1);
but i haven't tested this and am not 100% sure what your table columns are and what exactly you're attempting to join. But the link i provided should point you in the right direction.
Hope this helps!
MERGE INTO accounting_values AS Target
USING (select #entity_id as entity_id, #fiscal_year as fiscal_year, #fiscal_period as fiscal_period) AS source
ON (target.entity_id = source.entity_id AND target.fiscal_year = source.fiscal_year AND target.fiscal_period = source.fiscal_period)
WHEN MATCHED THEN
UPDATE SET value = #value
WHEN NOT MATCHED BY Target THEN
INSERT
(entity_id, fiscal_year, fiscal_period, value)
VALUES (#entity_ID, #fiscal_year, #fiscal_period, #value);
This is tested. It doesn't reference the table twice. I was trying to duplicate MySQL's ON DUPLICATE KEY UPDATE.
MERGE CSScolorOrders AS TARGET
USING (SELECT * FROM CSScolorOrders WHERE ordID = 3) AS SOURCE
ON (SOURCE.colID = TARGET.colID) WHEN
MATCHED THEN UPDATE SET corQuantity = 1
WHEN
NOT MATCHED
THEN INSERT (colID, ordID, corQuantity) VALUES (1, 3, 1);
To UPDATE or INSERT in a table whether this table contains a record or not, you can you MERGE as follows:
ensure USING clause returns ONE entry
express the matching condition(s) in the ON clause
In your case (example below tested on Oracle):
MERGE INTO CSScolorOrders AS TARGET
USING (SELECT 'OneEntry' FROM DUAL) AS SOURCE
ON (colID = 1 and ordID = 3) WHEN
MATCHED THEN UPDATE SET corQuantity = 1
WHEN
NOT MATCHED BY TARGET
THEN INSERT (colID, ordID, corQuantity) VALUES (1, 3, 1);
Most databases have a DUMMY table allowing arbitrary SELECT (above, I use DUAL), but if we only use the table mentioned in your question, you can e.g. replace
SELECT 'OneEntry' FROM DUAL
with
SELECT COUNT(*) FROM CSScolorOrders