Merge with same target and source table - sql

I have one table where I want to check if record exists leave it alone, if not insert new row and update previous row. I am wondering if I can use merge here like below ?
CREATE TABLE a
(keycol INT PRIMARY KEY,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL);
INSERT INTO a VALUES (1,0,0,0),(2,0,0,0);
MERGE INTO a
USING select 1 from a where col1 = 3
WHEN NOT MATCHED THEN
UPDATE SET
col2 = 2,
col2 = 2,
col3 = 2
where col1 = 3
WHEN NOT MATCHED THEN
INSERT (keycol, col1, col2, col3)
VALUES (4, 0, 0, 0)
Thanks,

MERGE INTO a
USING (
VALUES (3,3,2,2),
(4,0,0,0)
) AS source (keycol, col1, col2, col3)
ON a.keycol = source.keycol
AND a.col1 = source.col1
WHEN MATCHED THEN
UPDATE
SET col2 = source.col2,
col3 = source.col3
WHEN NOT MATCHED THEN
INSERT (keycol, col1, col2, col3)
VALUES (keycol, col1, col2, col3);

Related

Copy rows that has atleast one null value in any column in SQL Server

I have table_1 that has certain columns. I want to copy that data to table_2 that has atleast one null value in any column of table_1. For e.g. table_1 has three columns. Now, I want to copy those rows to table_2 that has atleast one null value in any of the three columns of table_1.
I tried it using following query:
insert into table_2 (col1, col2, col3)
select col1, col2, col3
from table_1
where col1 is null or col2 is null or col3 is null
But, there is an issue that table_2 has a column 'error_value' which should contain data that indicates which column(s) has NULL value corresponding to that particular row like it should mention 'col2 is null' if col2 has missing value in that row. If more than one column has NULL values, then it should mention about all those columns in 'error_value' column like 'col1, col2, col3' is null if all columns have missing values.
Any suggestions or help how can I implement it.
Use CASE expressions to get the null columns, then concatenate. As of SQL Server 2017 this can best be achieved with CONCAT_WS (https://learn.microsoft.com/de-de/sql/t-sql/functions/concat-ws-transact-sql?view=sql-server-ver15).
insert into table_2 (col1, col2, col3, error_value)
select
col1, col2, col3,
concat_ws(', ',
case when col1 is null then 'col1' end,
case when col2 is null then 'col2' end,
case when col3 is null then 'col3' end
) + ' is null'
from table_1
where col1 is null or col2 is null or col3 is null;
Update
2017 version introduced concat_ws which can simplify the code significantly - check out Thorsten Kettner's answer for details.
I'm leaving this answer here in the hope it will help some other reader that uses an older version of SQL Server.
First version
One simple solution would be to use a combination of case, concat, stuff, and the string concatenation operator (+), taking advantage of the fact that concat will implicitly convert null to empty strings, while + will not.
First, create and populate sample table (Please save us this step in your future questions):
create table table_1 (col1 int, col2 int, col3 int);
create table table_2 (col1 int, col2 int, col3 int, error_value varchar(100));
insert into table_1(col1, col2, col3) VALUES
(null, null, null),
(null, null, 1),
(null, 1, null),
(null, 1, 1),
(1, null, null),
(1, null, 1),
(1, 1, null),
(1, 1, 1);
Then, the insert...select statement:
insert into table_2 (col1, col2, col3, error_value)
select
col1, col2, col3, stuff(
concat(
',' + case when col1 is null then 'col1' end, -- will be null if col1 contains a value
',' + case when col2 is null then 'col2' end, -- will be null if col2 contains a value
',' + case when col3 is null then 'col3' end, -- will be null if col3 contains a value
' is null'), 1, 1, '')
from table_1
where col1 is null or col2 is null or col3 is null
See a live demo on rextester
How about below query using CONCAT and IIF:
insert into
table_2 (col1, col2, col3, col4)
select
col1,
col2,
col3,
CONCAT(
IIF(col1 is null, 'col1 ', ''),
IIF(col2 is null, 'col2 ', ''),
IIF(col3 is null, 'col3 ', ''),
' is null'
)
from
table_1
where
col1 is null
or col2 is null
or col3 is null
Tried with simple case statement and concat
INSERT INTO #table_2 (col1, col2, col3,error_value)
SELECT col1, col2, col3
,SUBSTRING(CONCAT( CASE WHEN col1 IS NULL THEN ',col1' ELSE '' END
,CASE WHEN col2 IS NULL THEN ',col2' ELSE '' END
,CASE WHEN col3 IS NULL THEN ',col3' ELSE '' END
),2,20) + ' is null'
FROM #table_1
WHERE col1 IS NULL OR col2 IS NULL OR col3 IS NULL

Get ID of inserted & Selected row after multiple insert [duplicate]

This question already has answers here:
Insert Into... Merge... Select (SQL Server)
(1 answer)
Combine OUTPUT inserted.id with value from selected row
(2 answers)
Closed 4 years ago.
I have a query running in loop which I am trying to optimize as this
INSERT INTO myTable (col1, col2, col3)
OUTPUT inserted.id, SOURCE_ROW_ID_NEEDED_HERE
SELECT col1, col2, col3
FROM myTable
WHERE col2 = 20 --any value
My problem is : col2 = 20 can have N number of rows which get inserted, I need the id of the source row for the new record. For example say there are 3 rows for col2 = 20 and id of them are 11,12,15. The new inserted ID are say 150,151,152.
I would need
11 150
12 151
15 152
Are you looking for something like
CREATE TABLE T1(
Col1 INT IDENTITY(1, 1),
Col2 INT,
Col3 INT
);
CREATE TABLE T2(
Col1 INT IDENTITY(1, 1),
Col2 INT,
Col3 INT
);
INSERT INTO T2(Col2, Col3) VALUES
(11, 150),
(12, 151),
(15, 152);
DECLARE #TT TABLE (ID INT, Col2 INT);
SET IDENTITY_INSERT T1 ON;
INSERT INTO T1 (Col1, Col2, Col3)
OUTPUT INSERTED.Col1,
INSERTED.Col2
INTO #TT (ID, Col2)
SELECT Col1,
Col2,
Col3
FROM T2;
SELECT *
FROM #TT;
SET IDENTITY_INSERT T1 OFF;
Demo

delete a row after comparing specific values with other rows in the same table

I have a table like this ( SQL SERVER )
col1 col2 col3 col4 col5
1 Goerge A B ++
2 Alex B B aa
2 Alex B B ++
now the second and the third rows have almost same values except in col5. in this case i want to delete one of them ( i want to be able to decide wich row from both of them i wanna delete ) like the one with value ++ so the table should look like this :
col1 col2 col3 col4 col5
1 Goerge A B ++
2 Alex B B aa
How can that be achieved?
Use CTE with ROW_NUMBER() to find (and delete) your duplicates
WITH CTE_Dup AS
(
SELECT *
, ROW_NUMBER OVER() PARTITION BY (Col1, Col2, Col3, Col4 ORDER BY Col5) RN
-- change ORDER BY criteria to make sure row you want to keep is 1st
FROM YourTable
)
DELETE --Try SELECT * to check before actually deleting
FROM CTE_Dup WHERE RN>1
It is not at all clear the logic you want here for determining which row is a "duplicate" but this example should show you a pretty straight forward way of handling this kind of thing.
declare #Something table
(
col1 int
, col2 varchar(10)
, col3 char(1)
, col4 char(1)
, col5 char(2)
)
insert #Something
values
(1, 'Goerge', 'A', 'B', '++')
, (2, 'Alex', 'B', 'B', 'aa')
, (2, 'Alex', 'B', 'B', '++')
;
with SortedValues as
(
select *
, ROW_NUMBER() over (partition by col1 order by col5 desc) as RowNum
from #Something
)
delete SortedValues
where RowNum > 1
select *
from #Something
You can try the below query. I grouped the columns and get the count of that and handled the conditions in the WHERE clause.
Query execution with the given sample data:
DECLARE #TestTable TABLE (col1 INT, col2 VARCHAR (20), col3 VARCHAR (20), col4 VARCHAR (20), col5 VARCHAR (20));
INSERT INTO #TestTable (col1, col2, col3, col4, col5) VALUES
(1, 'Goerge', 'A', 'B', '++'),
(2, 'Alex', 'B', 'B', 'aa'),
(2, 'Alex', 'B', 'B', '++');
SELECT * INTO #tmpResults FROM (
SELECT col1, col2, col3, col4, col5,
COUNT(*) OVER (PARTITION BY col1, col2, col3, col4 ORDER BY col1 ) AS CCnt
FROM #TestTable
) a
SELECT col1, col2, col3, col4, col5 FROM #tmpResults WHERE Ccnt = 1 AND Col5 = '++'
UNION
SELECT col1, col2, col3, col4, col5 FROM #tmpResults WHERE Ccnt > 1 AND Col5 <> '++';
DROP TABLE #tmpResults
Output:
col1 col2 col3 col4 col5
------------------------------------
1 Goerge A B ++
2 Alex B B aa

Sql insert select from – multiple rows with unique column id

I am trying to copy multiple records using one query using insert select from.
Insert into tab_A(colId, col1, col2, col3)
Select colId, col1, col2, col3 form tab_A
Where colId in ( 2,4,6)
Would it be possible to assign different colId for new entries? For example colid 2 should be replaced with 23, 4 with 24 and 6 with 25. How could I achieve it in a single query?
this would work
Insert into tab_A(colId, col1, col2, col3)
Select 23 , col1, col2, col3 form tab_A Where colId = 2 UNION ALL
Select 24 , col1, col2, col3 form tab_A Where colId = 4 UNION ALL
Select 25 , col1, col2, col3 form tab_A Where colId = 6
If you give some more info I could provide somthing more reusable. Should/is colId (be) an identity column?
EDIT
This would work in this very specialised case
Insert into tab_A(colId, col1, col2, col3)
Select ((colId - 4) * (-1)) + colId + 20 , col1, col2, col3
form tab_A Where colId IN (2, 4, 6)
The function newId = ((oldId - 4) * (-1)) + oldId + 20 is obviously specific to the stated problem.
EDIT2
I suspect somthing like this is more generic approach is appropriate.
DECLARE #MaxColID INT
BEGIN TRANSACTION
SELECT #MaxColID = MAX(ColID) FROM tab_A
INSERT tab_A(colId, col1, col2, col3)
SELECT row + #MaxColID, col1, col2, col3
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY ColID) row, col1, col2, col3
FROM tab_A WHERE colID IN (2, 4, 6)
)
COMMIT
EDIT 3
If you think EDIT 2 is actually what you want then you really want to make ColID an IDENTITY column, then you could do this.
INSERT tab_A (col1, col2, col3)
SELECT col1, col2, col3 FROM tab_A WHERE colId IN (2, 4, 6)
I dont see col4 or col6 in your query, but is this what you want:
Insert into tab_A(colId, col1, col2, col3)
Select colId, col1, 23, col3 form tab_A
Where colId in ( 2,4,6)
have you just tried adding the disired difference to colId -
In your case, since you need to replace 2 by 23, difference is 21.
Insert into tab_A(colId, col1, col2, col3)
Select colId+21, col1, col2, col3
form tab_A Where colId in ( 2,4,6)
Note: I missed the part, that the differnce is not consistent in your case.
The proposed solution will work only if difference is same
There are a few options:
Add the new ID column to the original table and populate it with the new values before you do this insert, selecting the new ID column instead of the old. This would be the tidiest solution I think.
Alternative - Modify the ID value on the insert based on a rule e.g.
INSERT INTO tab_A(colID, col1, col2, col3)
SELECT colId + 20, col1, col2, col3
FROM tab_A
WHERE colID IN(2,4,6)
Last resort - Process the insert sequentially with a cursor, modifying the ID value each time.
You could also write case in the select. when 2 then 23 or whatever value.

How to block wrong inserts and updates into one table?

Situation is as below. I have a table like here:
Col1 Col2 Col3 Col4 Col5 Col6
a b c 1 e 1
a b c 3 l 1
a b c 1 e 0
a b f 1 f 1
The idea is that I cant update existing data or add new row
which has combination a b c 1 ? 1.
I have to block adding 1 in last column if there
is already some combination of cols 1-3, but I can still may
add same combination with 0 in col 6.
For more complex logic, you use triggers: trigger gets triggered at specified time (insert, update, delete) - before (use word "for") or after.
Your example - trigger that does not allow inserting given combination of columns:
create table MyTable (
Col1 char, Col2 char, Col3 char, Col4 int, Col5 char, Col6 int
);
insert into MyTable (Col1, Col2, Col3, Col4, Col5, Col6)
values ('a', 'b', 'c', 1, 'e', '1');
insert into MyTable (Col1, Col2, Col3, Col4, Col5, Col6)
values ('a', 'b', 'f', 1, 'e', '1');
create trigger [dbo].[trigger_MyTable] on MyTable for insert as
begin
if 0 < (select count(*) from inserted where
Col1='a' and Col2='b' and Col3='c' and Col4=1 and Col6=1)
begin
raiserror 50009 'wrong insert, no way!'
rollback transaction
return
end
end;
-- now it fails
insert into MyTable (Col1, Col2, Col3, Col4, Col5, Col6)
values ('a', 'b', 'c', 1, 'e', '1');
Of course, you can create trigger for update (use "inserted" table), or for delete (use "deleted" table).
You might do other actions (like inserting another row in another table), ...
Give a MSDN try as well: http://msdn.microsoft.com/en-us/library/ms189799(v=SQL.90).aspx
Unless I misunderstood you requirements I think you want a unique constraint across all columns except col5.
Alter Table Foo
ADD CONSTRAINT ak_orf UNIQUE
(Col1, Col2 ,Col3 ,Col4 ,Col6),