Insert multiple rows using SQL Merge statement when condition matched - sql

Have Table with Non Identity Primary key. so whenever we insert new record we have to pass Primary key also.
I have to insert into the above table when record is not matched using Merge statement. But the problem is i could not increment the Primary key for each insert. it it throwing can not insert duplicate in Primary key column.
Please find the Sample Merge query below.
Is it possible to insert multiple rows by increment primary key.
MERGE DBO.Table1 T1
USING (DBO.Table2 )T2
ON (T1.ID = T2.ID)
WHEN MATCHED
THEN UPDATE SET
T1.CURVE = T2.CURVE
WHEN NOT MATCHED
THEN INSERT (ID, CURVE )
Values ( T2.ID, T2.CURVE);

The code in your example works, here's a demo easy to reproduce
;with cteT as ( SELECT * FROM (VALUES (1,'T1 Val 1') , (2,'T1 Val 2') , (4,'T1 Val 4') ) as T1(ID, Curve)
)SELECT * INTO #Dest FROM cteT
;with cteT as ( SELECT * FROM (VALUES (3,'T2 Val 3') , (4,'T2 Val 4') , (5,'T2 Val 5') ) as T1(ID, Curve)
)SELECT * INTO #Srce FROM cteT;
MERGE INTO #Dest as T1
USING #Srce as T2 ON T1.ID=T2.ID
WHEN MATCHED THEN UPDATE SET T1.Curve=T2.CURVE
WHEN NOT MATCHED BY TARGET
THEN INSERT (ID, Curve) VALUES(T2.ID, T2.Curve)
;
SELECT * FROM #Dest ORDER BY ID
DROP TABLE #Dest
DROP TABLE #Srce
This is the output, note that for 1 & 2 the value is unchanged, for 4 it's updated from T2, and for 3 and 5 it's inserted from T2.
ID Curve
1 T1 Val 1
2 T1 Val 2
3 T2 Val 3
4 T2 Val 4
5 T2 Val 5
This means that most likely either the problem is in your values for T2.ID or your sample is too pared down and misses a complication in your actual code. I'd start by checking your data in T2.ID
SELECT ID, COUNT(ID) as IDCount FROM DBO.Table2 GROUP BY ID HAVING COUNT(*) > 1
SELECT * FROM DBO.Table2 WHERE ID IS NULL
and see if any records turn up. If those are both empty, look at your actual merge code and see how it might differ from what you posted. If you post updated sample code I'll try to have a look.

Related

Postgresql update column based on set of values from another table

Dummy data to illustrate my problem:
create table table1 (category_id int,unit varchar,is_valid bool);
insert into table1 (category_id, unit, is_valid)
VALUES (1, 'a', true), (2, 'z', true);
create table table2 (category_id int,unit varchar);
insert into table2 (category_id, unit)
values(1, 'a'),(1, 'b'),(1, 'c'),(2, 'd'),(2, 'e');
So the data looks like:
Table 1:
category_id
unit
is_valid
1
a
true
2
z
true
Table 2:
category_id
unit
1
a
1
b
1
c
2
d
2
e
I want to update the is_valid column in Table 1, if the category_id/unit combination from Table 1 doesn't match any of the rows in Table 2. For example, the first row in Table 1 is valid, since (1, a) is in Table 2. However, the second row in Table 1 is not valid, since (2, z) is not in Table 2.
How can I update the column using postgresql? I tried a few different where clauses of the form
UPDATE table1 SET is_valid = false WHERE...
but I cannot get a WHERE clause that works how I want.
You can just set the value of is_valid the the result of a ` where exists (select ...). See Demo.
update table1 t1
set is_valid = exists (select null
from table2 t2
where (t2.category_id, t2.unit) = (t1.category_id, t1.unit)
);
NOTES:
Advantage: Query correctly sets the is_valid column regardless of the current value and is a vary simple query.
Disadvantage: Query sets the value of is_valid for every row in the table; even thoes already correctly set.
You need to decide whether the disadvantage out ways the advantage. If so then the same basic technique in a much more complicated query:
with to_valid (category_id, unit, is_valid) as
(select category_id
, unit
, exists (select null
from table2 t2
where (t2.category_id, t2.unit) = (t1.category_id, t1.unit)
)
from table1 t1
)
update table1 tu
set is_valid = to_valid.is_valid
from to_valid
where (tu.category_id, tu.unit) = (to_valid.category_id, to_valid.unit)
and tu.is_valid is distinct from to_valid.is_valid;

Oracle -- Update the exact column referenced in the ON clause

I think this requirement is rarely encountered so I couldn't search for similar questions.
I have a table that needs to update the ID. For example ID 123 in table1 is actually supposed to be 456. I have a separate reference table built that stores the mapping (e.g. old 123 maps to new id 456).
I used the below query but apparently it returned error 38104, columns referenced in the ON clause cannot be updated.
MERGE INTO table1
USING ref_table ON (table1.ID = ref_table.ID_Old)
WHEN MATCHED THEN UPDATE SET table.ID = ref_table.ID_New;
Is there other way to achieve my purpose?
Thanks and much appreciated for your answer!
Use the ROWID pseudocolumn:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TABLE1( ID ) AS
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 3 FROM DUAL;
CREATE TABLE REF_TABLE( ID_OLD, ID_NEW ) AS
SELECT 1, 4 FROM DUAL UNION ALL
SELECT 2, 5 FROM DUAL;
MERGE INTO TABLE1 dst
USING ( SELECT t.ROWID AS rid,
r.id_new
FROM TABLE1 t
INNER JOIN REF_TABLE r
ON ( t.id = r.id_old ) ) src
ON ( dst.ROWID = src.RID )
WHEN MATCHED THEN
UPDATE SET id = src.id_new;
Query 1:
SELECT * FROM table1
Results:
| ID |
|----|
| 4 |
| 5 |
| 3 |
You can't update a column used in the ON clause in a MERGE. But if you don't need to make other changes that MERGE allows like WHEN NOT MATCHED or deleting, etc. you can just use a UPDATE to achieve this.
You mentioned this is an ID that needs an update. Here's an example using a scalar subquery. As it is an ID, this presumes UNIQUE ID_OLD values in REF_TABLE. I wasn't sure if Every row needs an update or only a sub-set, so set the update here to only update rows that have a value in REF_TABLE.
CREATE TABLE TABLE1(
ID NUMBER
);
CREATE TABLE REF_TABLE(
ID_OLD NUMBER,
ID_NEW NUMBER
);
INSERT INTO TABLE1 VALUES (1);
INSERT INTO TABLE1 VALUES (2);
INSERT INTO TABLE1 VALUES (100);
INSERT INTO REF_TABLE VALUES (1,10);
INSERT INTO REF_TABLE VALUES (2,20);
Initial State:
SELECT * FROM TABLE1;
ID
1
2
100
Then make the UPDATE
UPDATE TABLE1
SET TABLE1.ID = (SELECT REF_TABLE.ID_NEW
FROM REF_TABLE
WHERE REF_TABLE.ID_OLD = ID)
WHERE TABLE1.ID IN (SELECT REF_TABLE.ID_OLD
FROM REF_TABLE);
2 rows updated.
And check the change:
SELECT * FROM TABLE1;
ID
10
20
100

How to update in order and query the updated fields when updating in SQL in a single statement

I need to calculate Dividend Factors in the DB and the basic calculation needed in a general way is row2 field2 = (row2's field1) * (row1's field2) where the field2 is the value I need to both update and query at the same time i.e. when I calculate it for one row, I need the calculated value of the previous row for this row.
Now I have a temp table with has all the values and now I need to calculate the final values, but when I tried this:
UPDATE
#temp
SET
field2 = IsNull(
(SELECT d2.field2 * d.field1 FROM #temp AS d2 WHERE d2.rowNr = d.rowNr - 1)
,d.field1
)
FROM
#temp as d
;
It always saw that the field2 was always NULL and went with the default action, with it should do only for the first row.
Now currently there are only two methods I know for doing this:
Loop through the #temp with a cursor
Use a while statement and loop through the table that way (I opted for this one, because I thought there is no point in using a cursor for a small table of 10-20 rows max)
But I still would like to get this into a single statement, but I have no idea how to do this. I am using MS SQL 2008 R2.
EDIT:
This is the actual data I am working with: (Note, that all field2 values are NULL prior to the calculation and the data type is money)
field1 field2(expected values)
------ ----------------------
1,033 1,033
1,0363 1,0705
1,0558 1,1302
1,0157 1,1479
1,0188 1,1695
1,026 1,1999
1,0286 1,2342
1,0323 1,2741
1,0319 1,3147
Okay if I'm understanding this, you want to find field2 which is based on previous rows of field2 that were just calculated so you need either some form of loop or recursion. Try this recursive solution out:
Setting Up Tables
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp;
DECLARE #yourTable TABLE (ID INT,field1 INT, field2 INT);
INSERT INTO #yourTable(ID,field1,field2)
VALUES (1111,11,11),(2222,22,22),(3333,33,33);
SELECT ROW_NUMBER() OVER (ORDER BY ID) rowNr,
ID,
field1,
field2 INTO #temp
FROM #yourTable;
Calculating values
WITH cte_recursion
AS
(
SELECT TOP 1
rowNR,
ID,
field1,
field2,
field1 AS dividend_factor
FROM #temp A
ORDER BY rowNr
UNION ALL
SELECT B.rowNr,
B.ID,
B.field1,
B.field2,
B.field1 * A.dividend_factor
FROM cte_recursion A
INNER JOIN #temp B
ON A.rowNr = B.rowNr - 1
)
Actual Update
UPDATE #yourTable
SET field2 = B.dividend_factor
FROM #yourTable A
INNER JOIN cte_recursion B
ON A.ID = B.ID
OPTION (MAXRECURSION 0)
SELECT *
FROM #yourTable
Results:
ID field1 field2
----------- ----------- -----------
1111 11 11
2222 22 242
3333 33 7986
Personally I wouldn't use the update because you have to constantly make sure the data is update to date. I'd much rather use the CTE I used to calculate the values and put it in a view so that you know the values are ALWAYS up to date and you don't have to worry about running it. Either that or having a dividend_factor column in your actual table that will be NULL unless the value is updated. Just my two cents
UPDATE d1
SET d1.field2 = IsNull(d2.field2 * d1.field1, d1.field1)
FROM #temp AS d1
left outer join #temp AS d2
on d2.rowNr = d1.rowNr - 1
magic
select d1.field1, EXP(SUM(LOG(d2.field1)))
from #temp AS d1
join #temp AS d2
on d2.rowNr <= d1.rowNr
group by d1.field1
op claims wrong answer
test for youself
drop table #temp;
create table #temp (ID int, val money);
insert into #temp (ID, val) values
(1, 1.033)
, (2, 1.0363)
, (3, 1.0558)
, (4, 1.0157)
, (5, 1.0188)
, (6, 1.026)
, (7, 1.0286)
, (8, 1.0323)
, (9, 1.0319);
SELECT TOP 10 [t1].[ID], EXP(SUM(LOG([t2].[val])))
from #temp AS t1
join #temp AS t2
on t2.[ID] <= t1.[ID]
group by t1.[ID]
order by t1.[ID]

SQL query to get ID from multiple inputs

I'm not sure the title is clear, but this is the situation.
I have a table that looks like this:
ID inputID value
4 1 10
4 2 20
4 3 100
6 1 15
6 2 20
6 3 44
I have user input that gives me the values for inputID 1 and inputID 2, after which I want to get the ID to get the other information corresponding to that ID.
Example: If the user gives inputID(1)=10, inputID(2)=20, I want to get 4
Using simple AND statements don't solve this problem. I have asked already asked a few people, but I can't seem to solve this seemingly simple problem.
What about something like this?
SELECT t1.ID
FROM table t1
INNER JOIN table t2
ON t1.ID = t2.ID
WHERE
t1.inputID = 1 AND t1.value = #input1 AND
t2.inputID = 2 AND t2.value = #input2
select ID from T as T1
where inputID=1 and value=10
and EXISTS(select id from T where ID=T1.ID and inputID=2 and value=20)
Try this query:
Create Table #tmp ( Id int, inputId int, value int)
insert into #tmp values (4,1,10)
insert into #tmp values (4,2,20)
insert into #tmp values (4,3,100)
insert into #tmp values (6,1,15)
insert into #tmp values (6,2,20)
insert into #tmp values (6,3,44)
-- my query
Select top 1 id from
(Select id,count(id) as cnt from #tmp where (inputId=1 and value=10) OR (inputId=2 and value=20) Group by id) tbl
order by cnt desc
-- Scoregraphic's query
SELECT t1.ID
FROM #tmp t1
INNER JOIN #tmp t2
ON t1.ID = t2.ID
WHERE
t1.inputID = 1 AND t1.value = 10 AND
t2.inputID = 2 AND t2.value = 20
Drop table #tmp
On seeing the execution plan my query is takes overall 23% and ScoreGraphic's query takes 21%

SQLite: accumulator (sum) column in a SELECT statement

I have a table like this one:
SELECT value FROM table;
value
1
3
13
1
5
I would like to add an accumulator column, so that I have this result:
value accumulated
1 1
3 4
13 17
1 18
5 23
How can I do this? What's the real name of what I want to do? Thanks
try this way:
select value,
(select sum(t2.value) from table t2 where t2.id <= t1.id ) as accumulated
from table t1
but if it will not work on your database, just add order by something
select value,
(select sum(t2.value) from table t2 where t2.id <= t1.id order by id ) as accumulated
from table t1
order by id
this works on an oracle ;) but it should on a sqlite too
Here's a method to create a running total without the inefficiency of summing all prior rows. (I know this question is 6 years old but it's one of the first google entries for sqlite running total.)
create table t1 (value integer, accumulated integer, id integer primary key);
insert into t1 (value) values (1);
insert into t1 (value) values (3);
insert into t1 (value) values (13);
insert into t1 (value) values (1);
insert into t1 (value) values (5);
UPDATE
t1
SET
accumulated = ifnull(
(
SELECT
ifnull(accumulated,0)
FROM
t1 ROWPRIOR
WHERE
ROWPRIOR.id = (t1.id -1 )),0) + value;
.headers on
select * from t1;
value|accumulated|id
1|1|1
3|4|2
13|17|3
1|18|4
5|23|5
This should only be run once after importing all the values. Or, set the accumulated column to all nulls before running again.
The operation is called a running sum. SQLite does not support it as is, but there are ways to make it work. One is just as Sebastian Brózda posted. Another I detailed here in another question.