Can I use the output clause in the Update command to generate 2 rows - sql

I can use OUTPUT to generate the before and after row values when doing an update to a table. And I can put these values into a table.
But they go into the table in 1 single row.
I need the before values as a row, and the after values as a new row.
So this snippet isn't working for me at the moment, but it shows the thing I am trying to do. Is there any way to do this using OUTPUT.
update dbo.table1
set employee = employee + '_updated'
OUTPUT 'd',DELETED.* INTO dbo.table2,
OUTPUT 'i',INSERTED.* INTO dbo.table2,
WHERE id = 4 OR id = 2;
This snippet below works, but only creates a single row:
update dbo.table1
set employee = employee + '_updated'
OUTPUT 'd', DELETED.*, 'i', INSERTED.* INTO dbo.table2,
WHERE id = 4 OR id = 2;
I can do it using triggers, but that's not allowed in this case.
And I can do it manually (selecting what I'm going to update into table2, then doing the update...)
Any tips or hints appreciated on how to do it just using just OUTPUT in the update?
Rgds, Dave

To elaborate on an answer given in the comments:
declare #TempTable table (
d_id int,
d_employee varchar(50),
d_other varchar(50),
u_id int,
u_employee varchar(50),
u_other varchar(50)
)
update Table1
set employee = employee + '_updated'
output deleted.id d_id, deleted.employee d_employee, deleted.other d_other,
inserted.id u_id, inserted.employee u_employee, inserted.other u_other
into #TempTable
where id = 4 or id = 2;
insert Table2 (change_type, employee_id, employee, other)
select
'd',
d_id,
d_employee,
d_other
from #TempTable
union all
select
'i',
u_id,
u_employee,
u_other
from #TempTable
I made some assumptions about your schema as it wasn't given, but this should get you started.

Related

Generate ID for duplicate values in sql server

I found following link to assign identical ID to duplicates in SQL server,
my understanding there is no sql server function to automatically generate it rather than using insert and update queries in link attached, is that statement True, if yes, then what would be the trigger if for example someone insert data to MyTable then run insert and update query from link:
Assign identical ID to duplicates in SQL server
INSERT INTO secondTable (word) SELECT distinct word FROM MyTable;
UPDATE MyTable SET ID = (SELECT id from secondTable where MyTable.word = secondTable.word)
thanks,
S
Is this what you want? I can't think of an "automatic" solution that would just increase the Id for new words.
CREATE TABLE MyTable (
Id INT NOT NULL,
Word NVARCHAR(255) NOT NULL
PRIMARY KEY (Id, Word)); -- primary key will make it impossible to have more than one combination of word and id
DECLARE #word NVARCHAR(255) = 'Hello!';
-- Get existing id or calculate a new id
DECLARE #Id INT = (SELECT Id FROM MyTable WHERE Word = #word);
IF(#id IS NULL) SET #Id = (SELECT MAX(Id) + 1 FROM MyTable);
INSERT INTO MyTable (Id, Word)
VALUES (#id, #word)
SELECT * FROM MyTable
If you can't for some reason have id and word as a combined primary key, you may use an unique index to make sure that there is only one combination

How to Delete the Existing data based on Where clause condition using Merge

i have written a merge Statement where i am facing trouble to delete the data basing on Where Clause Condition.
Let me explain my scenario Clearly
For example i have inserted Data from Source to Target based on Date Key Condition.Take an Instance 10 Records Inserted.
For example some changes in the records and it has been updated through the Merge Statement .
For the Same Date key based on Conditions now three records has came and need to be inserted and rest should be deleted for that Date Key.
How i need to proceed on this before 10 records are not getting deleted and new records adding for that one
My Example Code :
DELETE FROM #Table1
CREATE TABLE #Table1
(ID INT ,Name VARCHAR(30),DATEKEY INT)
INSERT INTO #Table1 (ID,Name,DATEKEY)VALUES (1,'Mohan',20131231)
INSERT INTO #Table1 (ID,Name,DATEKEY)VALUES (2,'Raj',20131231)
INSERT INTO #Table1 (ID,Name,DATEKEY)VALUES (3,'Majjaa',20131231)
INSERT INTO #Table1 (ID,Name,DATEKEY)VALUES (4,'Majjaa',20131231)
CREATE TABLE #Table2
(ID INT ,Name VARCHAR(30),DATEKEY INT)
DECLARE #i_DateKey INT
SET #i_DateKey = '20131231'
MERGE #Table2 AS T
USING (
SELECT pdp.ID
,pdp.Name
,pdp.DATEKEY
FROM #Table1 AS pdp
WHERE (
pdp.DateKey = #i_DateKey
OR #i_DateKey IS NULL
)
) AS S
ON T.ID = S.ID
AND T.DateKey = S.DateKey
AND T.NAME = S.NAME
WHEN MATCHED
THEN
UPDATE
SET T.NAME = S.NAME
WHEN NOT MATCHED BY TARGET
THEN
INSERT
VALUES (
S.ID
,S.Name
,S.DateKey
)
WHEN NOT MATCHED BY SOURCE
THEN
DELETE ;
Now the target table will be loaded with Rows now if i send the another row for Same Date key then it need to be deleted all the 4 rows and reload the new Row if the new row is same then need to update
i think this is one big mistake,
ON T.ID = S.ID --i am not sure about this
AND T.DateKey = S.DateKey
AND T.NAME = S.NAME -- remove this because this when matched then update will do what ?
WHEN MATCHED
THEN
UPDATE
SET T.NAME = S.NAME

Update while copy records

I want to copy records from one table to another. While doing this I want to set a flag of those records I copy.
This is how I would do it (simplified):
BEGIN TRANSACTION copyTran
insert into destination_table (name)
select top 100 name
from source_table WITH (TABLOCKX)
order by id
update source_table
set copy_flag = 1
where id in (select top 100 id from source_table order by id)
COMMIT TRANSACTION copyTran
Is there an easier way?
By leveraging OUTPUT clause you can boil it down to a single UPDATE statement
UPDATE source_table
SET copy_flag = 1
OUTPUT inserted.name
INTO destination_table(name)
WHERE id IN
(
SELECT TOP 100 id
FROM source_table
ORDER BY id
)
Note: Now tested. Should work just fine.
The problem with your query is, that you may get different records in your UPDATE if someone inserts some data while you are running your query. It is saver to use the INSERTED keyword.
Declare #temp TABLE (Id integer);
INSERT INTO destination_table (name)
OUTPUT INSERTED.Id into #temp
SELECT TOP 100 name
FROM source_table
ORDER BY id
UPDATE source_table
SET copy_flag = 1
WHERE Id IN (SELECT Id FROM #temp)
I think that you could use a temporary table in which you will store the top 100 ids from your source table, after having ordered them. This way you will avoid executing the select statement in where clause of update for each id.
BEGIN TRANSACTION copyTran
insert into destination_table (name)
select top 100 name
from source_table
order by id
declare #Ids TABLE(id int)
#Ids = (select top 100 id from source_table order by id)
update source_table
set copy_flag = 1
where id in (SELECT * FROM #Ids)
COMMIT TRANSACTION copyTran

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.

Delete duplicated rows and Update references

How do I Delete duplicated rows in one Table and update References in another table to the remaining row? The duplication only occurs in the name. The Id Columns are Identity columns.
Example:
Assume we have two tables Doubles and Data.
Doubles table (
Id int,
Name varchar(50)
)
Data Table (
Id int,
DoublesId int
)
Now I Have Two entries in the Doubls table:
Id Name
1 Foo
2 Foo
And two entries in the Data Table:
ID DoublesId
1 1
2 2
At the end there should be only one entry in the Doubles Table:
Id Name
1 Foo
And two entries in the Data Table:
Id DoublesId
1 1
2 1
In the doubles Table there can be any number of duplicated rows per name (up to 30) and also regular 'single' rows.
I've not run this, but hopefully it should be correct, and close enough to the final soln to get you there. Let me know any mistakes if you like and I'll update the answer.
--updates the data table to the min ids for each name
update Data
set id = final_id
from
Data
join
Doubles
on Doubles.id = Data.id
join
(
select
name
min(id) as final_id
from Doubles
group by name
) min_ids
on min_ids.name = Doubles.name
--deletes redundant ids from the Doubles table
delete
from Doubles
where id not in
(
select
min(id) as final_id
from Doubles
group by name
)
Note: I have taken the liberty to rename your Id's to DoubleID and DataID respectively. I find that eassier to work with.
DECLARE #Doubles TABLE (DoubleID INT, Name VARCHAR(50))
DECLARE #Data TABLE (DataID INT, DoubleID INT)
INSERT INTO #Doubles VALUES (1, 'Foo')
INSERT INTO #Doubles VALUES (2, 'Foo')
INSERT INTO #Doubles VALUES (3, 'Bar')
INSERT INTO #Doubles VALUES (4, 'Bar')
INSERT INTO #Data VALUES (1, 1)
INSERT INTO #Data VALUES (1, 2)
INSERT INTO #Data VALUES (1, 3)
INSERT INTO #Data VALUES (1, 4)
SELECT * FROM #Doubles
SELECT * FROM #Data
UPDATE #Data
SET DoubleID = MinDoubleID
FROM #Data dt
INNER JOIN #Doubles db ON db.DoubleID = dt.DoubleID
INNER JOIN (
SELECT db.Name, MinDoubleID = MIN(db.DoubleID)
FROM #Doubles db
GROUP BY db.Name
) dbmin ON dbmin.Name = db.Name
/* Kudos to quassnoi */
;WITH q AS (
SELECT Name, ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Name) AS rn
FROM #Doubles
)
DELETE
FROM q
WHERE rn > 1
SELECT * FROM #Doubles
SELECT * FROM #Data
Take a look at this one, i have tried this, working fine
--create table Doubles ( Id int, Name varchar(50))
--create table Data( Id int, DoublesId int)
--select * from doubles
--select * from data
Declare #NonDuplicateID int
Declare #NonDuplicateName varchar(max)
DECLARE #sqlQuery nvarchar(max)
DECLARE DeleteDuplicate CURSOR FOR
SELECT Max(id),name AS SingleID FROM Doubles
GROUP BY [NAME]
OPEN DeleteDuplicate
FETCH NEXT FROM DeleteDuplicate INTO #NonDuplicateID, #NonDuplicateName
--Fetch next record
WHILE ##FETCH_STATUS = 0
BEGIN
--select b.ID , b.DoublesID, a.[name],a.id asdasd
--from doubles a inner join data b
--on
--a.ID=b.DoublesID
--where b.DoublesID<>#NonDuplicateID
--and a.[name]=#NonDuplicateName
print '---------------------------------------------';
select
#sqlQuery =
'update b
set b.DoublesID=' + cast(#NonDuplicateID as varchar(50)) + '
from
doubles a
inner join
data b
on
a.ID=b.DoublesID
where b.DoublesID<>' + cast(#NonDuplicateID as varchar(50)) +
' and a.[name]=''' + cast(#NonDuplicateName as varchar(max)) +'''';
print #sqlQuery
exec sp_executeSQL #sqlQuery
print '---------------------------------------------';
-- now move the cursor
FETCH NEXT FROM DeleteDuplicate INTO #NonDuplicateID ,#NonDuplicateName
END
CLOSE DeleteDuplicate --Close cursor
DEALLOCATE DeleteDuplicate --Deallocate cursor
---- Delete duplicate rows from original table
DELETE
FROM doubles
WHERE ID NOT IN
(
SELECT MAX(ID)
FROM doubles
GROUP BY [NAME]
)
Please try and let me know if this helped you
Thanks
~ Aamod
If you are using MYSQL following worked for me. I did it for 2 steps
Step 1 -> Update all Data rows to one Double table reference (with lowest id)
Step 2 -> Delete all duplicates with keeping lowest id
Step 1 ->
update Data
join
Doubles
on Data.DoublesId = Doubles.id
join
(
select name, min(id) as final_id
from Doubles
group by name
) min_ids
on min_ids.name = Doubles.name
set DoublesId = min_ids.final_id;
Step 2 ->
DELETE c1 FROM Doubles c1
INNER JOIN Doubles c2
WHERE
c1.id > c2.id AND
c1.name = c2.name;