I have 2 tables below and trying to update all values in tableA where applicable.
create table tableA(
key number(38,0),
tab1_uid varchar(500),
as_of_date date
)
create table TableB(
key number(38,0),
tab2_uid varchar(500),
as_of_date date
)
insert into tableA (key,tab1_uid,as_of_date) values (1,'123','2022-08-15'),(2,'345','2022-08-15'),(3,'256','2022-08-15');
insert into tableB (key,tab2_uid,as_of_date) values (0,'123','2022-08-18'),(0,'345','2022-08-18'),(1,'123','2022-08-17'),(2,'345','2022-08-17');
I want to update tableB key with tableA key values where tab1_uid = tab2_uid and tableB key = '0'
I tried doing below but getting Unsupported subquery type cannot be evaluated.
update tableB a
set participant_key = (
select b.key from tableA b
where a.tab2_uid = b.tab1_uid
and a.key = 0 and b.as_of_date = '2022-08-18'
)
I wasn't able to reproduce the error.
I took the sample script, and changed participant_key to key (because the column participant_key doesn't exist anywhere), and the sample script run as expected:
create table tableA(
key number(38,0),
tab1_uid varchar(500),
as_of_date date
);
create table TableB(
key number(38,0),
tab2_uid varchar(500),
as_of_date date
);
insert into tableA (key,tab1_uid,as_of_date) values (1,'123','2022-08-15'),(2,'345','2022-08-15'),(3,'256','2022-08-15');
insert into tableB (key,tab2_uid,as_of_date) values (0,'123','2022-08-18'),(0,'345','2022-08-18'),(1,'123','2022-08-17'),(2,'345','2022-08-17');
update tableB a
set key = (
select b.key from tableA b
where a.tab2_uid = b.tab1_uid
and a.key = 0 and b.as_of_date = '2022-08-18'
);
Related
I'm trying to implement the following logic in SQL Server:
every time data is inserted into MainTable, all this data should be also inserted into a backup table MainTable_BACKUP, and every row inserted into MainTable should have a foreign key BackupRecordId pointing to MainTable_BACKUP.
Can it be achieved using a trigger?
CREATE TRIGGER TRG_MainTable
ON MainTable
AFTER INSERT AS
BEGIN
INSERT INTO MainTable_BACKUP
SELECT *
FROM INSERTED
-- UPDATE INSERTED SET BackupRecordId = ??? somehow...
END
Yes you can.
Assuming that you have an identity column named BackupRecordId on you MainTable_BACKUP table, you can create an after insert trigger like this
Create Table MainTable
(
ID int IDENTITY(1, 1) PRIMARY KEY,
Description NVARCHAR(50),
BackupRecordId int
)
Create table MainTable_BACKUP
(
BackupRecordId int IDENTITY(1, 1) PRIMARY KEY,
[Id] int,
Description NVARCHAR(50)
)
CREATE TRIGGER TRG_MainTable
ON MainTable
AFTER INSERT AS
BEGIN
INSERT INTO MainTable_BACKUP([Id], Description)
SELECT [Id], Description FROM INSERTED
UPDATE MainTable
SET BackupRecordId = MP.BackupRecordId
FROM MainTable
INNER JOIN inserted i on i.Id = MainTable.Id
INNER JOIN MainTable_BACKUP MP ON MP.Id = MainTable.Id
END
You can try it with this:
insert into MainTable(Description)
values ('Testing')
select * from MainTable
select * from MainTable_BACKUP
I am trying to delete some records and inserting into other table at the same time. While deleting it's a self join on that table which checks some conditions. I want to do both delete and inset operations using OUTPUT clause.
Code:
DELETE dbo.Test
OUTPUT DELETED.Recipient_Key,
DELETED.Home_Dt,
DELETED.Batch_No,
DELETED.Brand_Cd,
DELETED.Campaign_Cd,
DELETED.Campaign_Nm,
DELETED.CampaignType_Cd
INTO dbo.Error
FROM dbo.Test AS PR1
INNER JOIN Staging.dbo.Test AS PR2
ON PR2.Recipient_Key = PR1.Recipient_Key
AND PR2.Batch_No = PR1.Batch_No
AND PR2.Home_Dt <> PR1.Home_Dt;
With the self join you need to specify an alias.
drop table if exists #test;
create table #test (
Id int not null primary key clustered identity(1, 1)
, SomeColumn varchar(255) not null
);
drop table if exists #error;
create table #error (
Id int not null primary key clustered
, SomeColumn varchar(255) not null
);
insert into
#test (SomeColumn)
values
('A'), ('B'), ('C');
select * from #test;
select * from #error;
delete a
output
Deleted.Id, Deleted.SomeColumn
into
#error (Id, SomeColumn)
from
#test as a
inner join
#test as b
on
a.Id = b.Id
and a.Id % 2 = 1;
select * from #test;
select * from #error;
I have a situation in which I need to insert some values from a query into a table that has an identity PK. For some of the records, I need also to insert values in another table which has a 1-to-1 (partial) relationship:
CREATE TABLE A (
Id int identity primary key clustered,
Somevalue varchar(100),
SomeOtherValue int)
CREATE TABLE B (Id int primary key clustered,
SomeFlag bit)
DECLARE #inserted TABLE(NewId int, OldId)
INSERT INTO A (Somevalue)
OUTPUT Inserted.Id into #inserted(NewId)
SELECT SomeValue
FROM A
WHERE <certain condition>
INSERT INTO B (Id, SomeFlag)
SELECT
i.NewId, B.SomeFlag
FROM #inserted i
JOIN A ON <some condition>
JOIN B ON A.Id = B.Id
The problem is that the query from A in the first INSERT/SELECT returns records that can only be differentiated by the Id, which I cannot insert. Unfortunately I cannot change the structure of the A table, to insert the "previous" Id which would solve my problem.
Any idea that could lead to a solution?
With INSERT ... OUTPUT ... SELECT ... you can't output columns that are not in the target table. You can try MERGE instead:
MERGE INTO A as tgt
USING (SELECT Id, SomeValue FROM A WHERE <your conditions>) AS src
ON 0 = 1
WHEN NOT MATCHED THEN
INSERT (SomeValue)
VALUES (src.SomeValue)
OUTPUT (inserted.Id, src.Id) -- this is your new Id / old Id mapping
INTO #inserted
;
SCOPE_IDENTITY() returns the last identity value generated by the current session and current scope. You could stick that into a #table and use that to insert into B
SELECT SCOPE_IDENTITY() as newid into #c
Though, your INSERT INTO B join conditions implies to me that the value in B is already known ?
I have these 3 tables:
CREATE TABLE tblPrimary(
Id INT IDENTITY(1,1) NOT NULL,
SampleID VARCHAR(8)
PRIMARY KEY (Id)
)
CREATE TABLE tblSecondary(
PrimaryId INT NOT NULL,
SampleName VARCHAR(50) NULL
)
CREATE TABLE tblSample(
SampleId VARCHAR(8) NOT NULL,
Name VARCHAR(50) NULL
PRIMARY KEY (SampleId)
)
Some sample data for tblSample
INSERT INTO tblSample VALUES ('A-1101', 'The CP 1014')
INSERT INTO tblSample VALUES ('A-1102', 'The NT 1014')
INSERT INTO tblSample VALUES ('A-1103', 'The LO 1014')
INSERT INTO tblSample VALUES ('A-1104', 'The AE 1014')
INSERT INTO tblSample VALUES ('A-1105', 'The PW 1014')
INSERT INTO tblSample VALUES ('A-1106', 'The QW 1014')
I'm currently inserting data from tblSample to tblPrimary with the following query:
INSERT INTO tblPrimary
SELECT s.SampleID FROM tblSample s
LEFT JOIN tblPrimary p on s.SampleId = p.SampleID
WHERE s.SampleId NOT IN (SELECT SampleID FROM tblPrimary)
Now I want to insert data into tblSecondary also, during the data insert into tblPrimary.
The newly generated `tblPrimary.PrimaryId` will be inserted into the tblSecondary.PrimiaryId` column
`tblSample.Name` will be inserted into the `tblSecondary.SampleName` column
It will be a cascading data inserting process
What do I need to do after the above insert query for this to get done?
I want the tblSecondary result to be as follows:
You will need a table variable and output clause for this something like....
DECLARE #NewIds (ID INT, SampleID varchar(8));
insert into tblPrimary(SampleID)
OUTPUT inserted.ID, inserted.SampleID INTO #NewIds (ID,SampleID )
select s.SampleID
from tblSample s
left join tblPrimary p on s.SampleId = p.SampleID
where s.SampleId not in (select SampleID from tblPrimary)
-- Now insert rows into tblSecondary table
INSERT INTO tblSecondary(PrimaryId, SampleName )
SELECT n.ID , S.Name
FROM tblSample s
INNER JOIN #NewIds n ON s.SampleId = n.SampleID
I am using an INSTEAD OF insert trigger on a table to set an incrementing version number on the row and also copy the row to a 2nd history/audit table.
The rows are inserted to both tables without a problem.
However, I am having trouble returning the new identity from the 1st table back to the user.
Schema
CREATE TABLE Table1
(
id INT IDENTITY(1,1) PRIMARY KEY,
name VARCHAR(250) NOT NULL UNIQUE,
rowVersion INT NOT NULL
)
CREATE TABLE Table1History
(
id INT NOT NULL,
name VARCHAR(250) NOT NULL,
rowVersion INT NOT NULL
)
CREATE TRIGGER TRG_INS_Table1
ON Table1
INSTEAD OF INSERT
AS
DECLARE #OutputTbl TABLE (id INT, name VARCHAR(250))
BEGIN
--make the insert
INSERT INTO Table1 (name, rowVersion)
OUTPUT INSERTED.id, INSERTED.name INTO #OutputTbl(id, name)
SELECT i.name, 1
FROM INSERTED i
--copy into history table
INSERT INTO Table1History (id, name, rowVersion)
SELECT t.ID, i.name, 1
FROM INSERTED i
JOIN #OutputTbl t on i.name = t.name
END
CREATE TRIGGER TRG_UPD_Table1
ON Table1
INSTEAD OF UPDATE
AS
BEGIN
--make the update
UPDATE Table1
SET name = i.name,
rowVersion = (SELECT d.rowVersion + 1 FROM DELETED d WHERE d.id = i.id)
FROM INSERTED i
WHERE Table1.id = i.id
--copy into history table
INSERT INTO Table1History (id, name, rowVersion)
SELECT i.id ,i.name, (SELECT d.rowVersion + 1 FROM DELETED d WHERE d.id = i.id)
FROM INSERTED i
END
Joining on the name column in the insert trigger is not ideal, but it needs to handle multiple inserts at once.
eg INSERT INTO Table1 (name) VALUES('xxx'),('yyy')
Attempted Solutions
When doing an insert, SCOPE_IDENTITY is NULL.
INSERT INTO Table1(name)
VALUES('xxx')
SELECT SCOPE_IDENTITY()
or
INSERT INTO Table1(name)
VALUES('xxx')
RETURN SCOPE_IDENTITY()
I've also tried using OUTPUT - which returns 0:
DECLARE #IdentityOutput TABLE (id INT)
INSERT INTO Table1(name)
OUTPUT INSERTED.id INTO #IdentityOutput
VALUES('xxx')
SELECT id FROM #IdentityOutput
The rows are inserted fine and have IDs, but I cannot access them unless I use the below - which seems hacky:
INSERT INTO Table1(name)
VALUES('xxx')
SELECT id from Table1 WHERE name = 'xxx'
What is the proper way to get the new ID??
Solution
Impossible! You can't reliably return the identity when doing an INSERT on a table that has an INSTEAD OF trigger. Sidux's answer below is a good workaround for my situation (replace INSTEAD OF trigger with AFTER trigger and added DEFAULT columns).
CREATE TABLE Table1
(
id INT IDENTITY(1,1) PRIMARY KEY,
name VARCHAR(250) NOT NULL UNIQUE,
rowVersion INT NOT NULL
)
GO
CREATE TABLE Table1History
(
id INT NOT NULL,
name VARCHAR(250) NOT NULL,
rowVersion INT NOT NULL
)
GO
CREATE TRIGGER TRG_INS_Table1
ON Table1
INSTEAD OF INSERT
AS
DECLARE #OutputTbl TABLE (id INT, name VARCHAR(250))
BEGIN
--make the insert
INSERT INTO Table1 (name, rowVersion)
SELECT i.name, 1
FROM INSERTED i
END
GO
CREATE TRIGGER TRG_UPD_Table1
ON Table1
INSTEAD OF UPDATE
AS
BEGIN
--make the update
UPDATE Table1
SET name = i.name,
rowVersion = (SELECT d.rowVersion + 1 FROM DELETED d WHERE d.id = i.id)
FROM INSERTED i
WHERE Table1.id = i.id
END
GO
CREATE TRIGGER TRG_AFT_INS_Table1
ON Table1
AFTER INSERT, UPDATE
AS
BEGIN
INSERT INTO Table1History (id, name, rowVersion)
SELECT i.ID, i.name, i.rowversion
FROM INSERTED i
END
GO
INSERT INTO Table1 (name) VALUES('xxx'),('yyy')
SELECT * FROM Table1History
-----------------------------------------------
id name rowVersion
2 yyy 1
1 xxx 1
-----------------------------------------------
UPDATE Table1 SET name = 'xxx1' WHERE id = 1;
SELECT * FROM Table1History
-----------------------------------------------
id name rowVersion
2 yyy 1
1 xxx 1
1 xxx1 2
-----------------------------------------------
Basically you do not need TRG_INS_Table1 trigger, you can just use DEFAULT value = 1 for column and that's it. Also if you use DATETIME column instead of rowversion, you can just insert the state of INSERTED table to the history with the GETDATE() value. In that case you can order by Dtime column DESC and you have history.