SQL MERGE INTO - How to delete then insert matched/existing data? - sql-server

I currently have a stored procedure that compares my target table (Ticket_Report) to my data source table (New_Tickets).
I am using a MERGE INTO statement to compare these two. When it finds a match between the two tables, it updates the current row in the target table with the corresponding info from the source table. If it dosent find a match, it inserts that data from the source table into the target table.
MERGE INTO Ticket_REPORT T1
USING #New_Tickets T2
ON T1.TICKET_NO=T2.TICKET_NO
WHEN MATCHED THEN
UPDATE SET
T1.TICKET_NO = T2.TICKET_NO,
T1.ASSIGNED_GROUP = T2.ASSIGNED_GROUP,
T1.ASSIGNEE = T2.ASSIGNEE,
T1.FNAME = T2.FNAME,
T1.LNAME = T2.LNAME
WHEN NOT MATCHED THEN
INSERT VALUES(
T2.TICKET_NO,
T2.ASSIGNED_GROUP,
T2.ASSIGNEE,
T2.FNAME,
T2.LNAME
);
What I need to do is, when I find a MATCH, instead of just updating the row, I need to delete that row, and re-insert it into the target table. Can anyone show me how to both DELETE and INSERT one after the other whenever I find a MATCH?

Here is an example:
DECLARE #t1 TABLE(id INT IDENTITY, col1 INT)
DECLARE #t2 TABLE(id INT IDENTITY, col1 INT, old_col1 INT)
INSERT INTO #t1(col1) VALUES(5), (10), (15)
INSERT INTO #t2(col1) VALUES(7), (14), (21), (28)
MERGE INTO #t2 t2
USING #t1 t1 ON t1.id = t2.id
WHEN MATCHED THEN DELETE
WHEN NOT MATCHED THEN INSERT VALUES(t1.col1, NULL)
OUTPUT t1.col1, Deleted.col1 INTO #t2(col1, old_col1);
SELECT * FROM #t2
ORDER BY id
Output:
id col1 old_col1
4 28 NULL
5 5 7
6 10 14
7 15 21
First 3 rows have same ids in both, so match part will delete those(7, 14, 21). 28 will remain. And you insert new values and keep old values in OUTPUT clause.

#Giorgi, that doesn't work completely.
If you #t1 has more rows than #t2, there'll be duplicates.
DECLARE #t1 TABLE(id INT IDENTITY, col1 INT)
DECLARE #t2 TABLE(id INT IDENTITY, col1 INT, old_col1 INT)
INSERT INTO #t1(col1) VALUES(5), (10), (15), (20), (25)
INSERT INTO #t2(col1) VALUES(7), (14), (21), (28)
MERGE INTO #t2 t2
USING #t1 t1 ON t1.id = t2.id
WHEN MATCHED THEN DELETE
WHEN NOT MATCHED THEN INSERT VALUES(t1.col1, NULL)
OUTPUT t1.col1, Deleted.col1 INTO #t2(col1, old_col1);
SELECT * FROM #t2
ORDER BY id
id col1 old_col1
5 25 NULL
6 25 NULL
7 5 7
8 10 14
9 15 21
10 20 28

The easiest way is run 2 times
DECLARE #t1 TABLE(id INT IDENTITY, col1 INT)
DECLARE #t2 TABLE(id INT IDENTITY, col1 INT, old_col1 INT)
INSERT INTO #t1(col1) VALUES(5), (10), (15), (20), (25)
INSERT INTO #t2(col1) VALUES(7), (14), (21), (28)
-- FIRST RUN;
MERGE INTO #t2 t2
USING #t1 t1 ON t1.id = t2.id
WHEN MATCHED THEN DELETE
--SECOND RUN;
MERGE INTO #t2 t2
USING #t1 t1 ON t1.id = t2.id
WHEN NOT MATCHED THEN INSERT VALUES(t1.col1, NULL)

Related

Return Null if no value is retrieved by join

I wish to write a query to return the a result by joining three tables:
ReceiptID in the Result is picked by FeeID 841. If the Client doesn't have a receipt or the clients receipt doesn't have FeeID 841 the result should be NULL.
Your exepected results do not make any sense, since it excludes receipts that are associated with clients. But his is a simple join...
declare #Table1 table (ApplicationID int identity (1,1), ClientID int)
declare #Table2 table (RecieptID int identity(1,1), ClientID int)
declare #Table3 table (ID int identity(1,1), RecepitID int, FeeID int)
insert into #Table1
values
(1), (2), (3)
insert into #Table2
values
(1), (1), (3)
insert into #Table3
values
(1, 841), (2, 840), (3, 220)
select
t1.ApplicationID
,t1.ClientID
,t2.RecieptID
,t3.FeeID
from
#Table1 t1
left join #Table2 t2 on t1.ClientID = t2.ClientID
left join #Table3 t3 on t3.RecepitID = t2.RecieptID

If value is X in table1 mark value X in table2

I'm trying to figure out how to make the following scenario work out in SQL Server:
We have two tables in a DB. Table1 contains full information of users with a column called "Tag" marked with values from 1 to 300.
We have another table which contains two columns where one is the number 1 to 300 as well and the second with either 0 or 1 as value.
We'd like that whenever a tag number is used in Table1 it marks it as "0" in Table2 standing for "in use" as 1 means "available".
Anyone who can assist me?
Try this:
DECLARE #tbl1 as TABLE(
Tag INT
)
DECLARE #tbl2 as TABLE(
Id INT,
IsAvailable BIT
)
INSERT INTO #tbl1 VALUES(1)
INSERT INTO #tbl1 VALUES(2)
INSERT INTO #tbl1 VALUES(5)
INSERT INTO #tbl2 VALUES(1,NULL)
INSERT INTO #tbl2 VALUES(2,NULL)
INSERT INTO #tbl2 VALUES(3,NULL)
INSERT INTO #tbl2 VALUES(4,NULL)
INSERT INTO #tbl2 VALUES(5,NULL)
SELECT
T1.Tag,
T2.Id,
CASE ISNULL(T1.Tag,0) WHEN 0 THEN 1 ELSE 0 END AS IsAvailable
FROM #tbl2 T2
LEFT JOIN #tbl1 T1 ON T1.Tag=T2.Id

Join table with unique values

Below is the case, this is simple group data but join / grouping picking duplicate data....
CREATE TABLE #t1 (UnID bigint IDENTITY(1,1), name varchar(5), id bigint)
insert into #t1 (name,id)values('a',1)
insert into #t1 (name,id)values('a',2)
insert into #t1 (name,id)values('a',3)
insert into #t1 (name,id)values('a',4)
insert into #t1 (name,id)values('b',5)
insert into #t1 (name,id)values('c',6)
CREATE TABLE #t2 (name varchar(5), id bigint)
insert into #t2 (name,id)values('a',1)
insert into #t2 (name,id)values('a',2)
insert into #t2 (name,id)values('a',3)
insert into #t2 (name,id)values('b',4)
insert into #t2 (name,id)values('c',5)
select
a.UnID, a.name as aName, a.Id as aId, b.Id as bId
into #t3
from #t1 a
join #t2 b on a.name = b.name
select max(bid),unid from #t3
group by UnId
Grouping Result
bid unid
3 1
3 2
3 3
3 4
4 5
5 6
Grouping desired Result
bid unid
1 1
2 2
3 3
NULL 4
4 5
5 6
and if bid not found then null
Below make me possible BUT still looking for better approach, xid is extra field for holding loop value....
CREATE TABLE #t4 (ID bigint IDENTITY(1,1), bid bigint)
SELECT
a.UnID, a.name as aName, a.Id as aId, b.Id as bId,NULL as xid
INTO #t3
FROM #t1 a
JOIN #t2 b ONa.name = b.name
DECLARE t_cursor CURSOR FOR
SELECT max(bid),unid
FROM #t3
GROUP BY UnId
DECLARE #bid bigint, #unid bigint
OPEN t_cursor
FETCH NEXT FROM t_cursor
INTO #bid, #unid
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #maxBid BIGINT
SET #maxBid = 0
SELECT #maxBid = max(bid)
FROM #t3
WHERE UnId= #unid
AND NOT bid IN (SELECT #t4.bid FROM #t4) GROUP BY UnId
INSERT into #t4
SELECT #maxBid as bid
set #maxBid = isnull(#maxBid,0)
if(#maxBid != 0)
begin
update #t3 set xid = #maxBid where unid = #unid
end
FETCH NEXT FROM t_cursor
INTO #bid, #unid
END
CLOSE t_cursor;
DEALLOCATE t_cursor;
SELECT * FROM #t3 order by unid,bid
SELECT max(xid),unid from #t3
group by UnId

How to autoincrement the id without identity?

I'm trying do to a bulk insert from another table in sql server. My query is currently like that :
INSERT INTO Table1(Id, Value)
SELECT ??, Value
FROM Table2;
Now, my problem is obviously by what I replace ??. Id is an integer column without an identity property. I would like that for each inserted row, Id take the current max(Id) + 1.
Can I do that directly in my insert command?
If you were using a newer version of SQL Server (2008+) you could try ROW_NUMBER():
DECLARE #BASE INT
SET #BASE = (SELECT IsNull(MAX(ID),0) FROM Table1)
INSERT INTO Table1(Id, Value)
SELECT
#BASE + ROW_NUMBER() OVER (ORDER BY Value) ID,
Value
FROM Table2;
SQL Fiddle
Since you are using SQL Server 2000, you could try like bellow:
DECLARE #BASE INT
SET #BASE = (SELECT IsNull(MAX(ID),0) FROM Table1)
INSERT INTO Table1(Id, Value)
SELECT
#BASE + (SELECT COUNT(*) FROM Table2 AS i2 WHERE i2.Value <= a.Value),
a.Value
FROM Table2 a
But it will only works if Value in Table2 is unique
SQL Fiddle
If Table2 has a primary key (field PK), then you could use:
INSERT INTO Table1(Id, Value)
SELECT
#BASE + (SELECT COUNT(*) FROM Table2 AS i2 WHERE i2.PK <= a.PK),
a.Value
FROM Table2 a
Here is one wicked way.
We create a temp table with identity to generate new ids. This way we avoid the while loop.
DECLARE #CurrentMaxID INT,
#DynamicQuery NVARCHAR(MAX)
--TODO : Acquired table lock here on table1
SELECT #FirstNextID = ISNULL(MAX(Id), 0)
FROM Table1 --WITH(TABLOCK)
CREATE TABLE #TempTableWithID( Table2Id INT,
Table1FuturId INT IDENTITY(1, 1))
INSERT INTO #TempTableWithID(Table2Id)
SELECT Id --Here we use identity to generate table1 futur id
FROM Table2
INSERT INTO Table1(Id, value)
SELECT Temp.Table1FuturId + #FirstNextID,
Table2.Value
FROM Table2
INNER JOIN #TempTableWithID AS Temp ON Table2.Id = Temp.Table2Id
--TODO : release table lock here on table1
DROP TABLE #TempTableWithID
If I'm understanding you correctly, this should work.
CREATE TABLE #tbl1 (ID int, Value float)
CREATE TABLE #tbl2 (ID int, Value float)
INSERT INTO #tbl2 values (4, 2.0)
INSERT INTO #tbl2 values (8, 3.0)
INSERT INTO #tbl2 values (6, 4.0)
INSERT INTO #tbl1 values (1,1.0)
INSERT INTO #tbl1 values (3,3)
INSERT INTO #tbl1 values (9,3)
/*meat and potatoes start*/
INSERT INTO #tbl1(Id, Value)
SELECT (SELECT MAX(ID) FROM #tbl1) + ROW_NUMBER() OVER (ORDER BY Value) ID, Value
FROM #tbl2;
/*meat and potatoes end*/
Select * From #tbl1
drop table #tbl1
drop table #tbl2
Why not IDENT_CURRENT() ?
SELECT IDENT_CURRENT('yourtablename')
It gives you the next ID reference. But this only works if the ID column has IDENTITY turned on.
OR you can try a SEQUENCE and the NEXT VALUE FOR.
i.e.
CREATE TABLE Test.TestTable
(CounterColumn int PRIMARY KEY,
Name nvarchar(25) NOT NULL) ;
GO
INSERT Test.TestTable (CounterColumn,Name)
VALUES (NEXT VALUE FOR Test.CountBy1, 'Syed') ;
GO
SELECT * FROM Test.TestTable;
GO

How do I find records out of order - SQL?

Let's say I have a table with an ID Identity column, some data, and a datestamp. Like this:
1 data 5/1/2013 12:30
2 data 5/2/2013 15:32
3 data 5/2/2013 16:45
4 data 5/3/2013 9:32
5 data 5/5/2013 8:21
6 data 5/4/2013 9:36
7 data 5/6/2013 11:42
How do I write a query that will show me the one record that is timestamped 5/4? The table has millions of records. I've done some searching, but I don't know what to call what I'm searching for. :/
declare #t table(id int, bla char(4), timestamp datetime)
insert #t values
(1,'data','5/1/2013 12:30'),
(2,'data','5/2/2013 15:32'),
(3,'data','5/2/2013 16:45'),
(4,'data','5/3/2013 9:32'),
(5,'data','5/5/2013 8:21'),
(6,'data','5/4/2013 9:36'),
(7,'data','5/6/2013 11:42')
select timestamp
from
(
select rn1 = row_number() over (order by id),
rn2 = row_number() over (order by timestamp), timestamp
from #t
) a
where rn1 not in (rn2, rn2-1)
in 2008 r2, this would be a way
DECLARE #Table AS TABLE
(id INT , ladate DATETIME)
INSERT INTO #Table VALUES (1, '2013-05-01')
INSERT INTO #Table VALUES (2, '2013-05-02')
INSERT INTO #Table VALUES (3, '2013-05-03')
INSERT INTO #Table VALUES (4, '2013-05-05')
INSERT INTO #Table VALUES (5, '2013-05-04')
INSERT INTO #Table VALUES (6, '2013-05-06')
INSERT INTO #Table VALUES (7, '2013-05-07')
INSERT INTO #Table VALUES (8, '2013-05-08')
--I added the records in the sort order but if not just make sure you are sorted in the query
SELECT t2.ladate FROM #Table T1
INNER JOIN #Table T2 ON T1.Id = T2.Id + 1
INNER JOIN #Table t3 ON t2.id = t3.id + 1
WHERE t3.ladate < t2.ladate AND t2.ladate > t1.ladate
-- I made the assumption that your Id are all there, 1,2,3,4,5.... none missing... if there are rownumbers missing, you can use row_number()

Resources