How to capture columns from joined tables using output clause? - sql

I am updating a table called tableA by joining tableB and tableC at the same time I am capturing updated records into temp table using output clause from tableA . Now I want capture columns from table B for the updated data but output clause isn't allowing the same.
Eg:
Update SLC Set SLC.Datascrublevel = C.Datascrublevel
OUTPUT [Deleted].Systemcode,
[Deleted].Systemkey,
[Deleted].datascrublevel,
[Inserted].datascrublevel
INTO #TEMP1
FROM TABLEA SLC with(nolock)
INNER JOIN TABLEB SC ON SC.SystemCode = SLC.SystemCode
INNER JOIN TABLEC SL ON SL.SystemCode = SLC.SystemCode and SLC.SystemKey = SL.Systemkey
INNER JOIN #TEMP C ON SLC.Datascrublevel <> C.DataScrubLevel AND C.Systemcode = SLC.SystemCode and C.Systemkey = SLC.SystemKey
Now I want columns from tableB to capture into temp table using output clause. Please provide your advise if there are any alternative ways.

Just Like you have given it as [deleted].[Column Name] and [Inserted].[Column Name] add one more column as [SC].[Column Name]
Example :
IF OBJECT_ID('TempDb..#TABLEA') IS NOT NULL
DROP TABLE #TABLEA
IF OBJECT_ID('TempDb..#TABLEB') IS NOT NULL
DROP TABLE #TABLEB
IF OBJECT_ID('TempDb..#TABLEC') IS NOT NULL
DROP TABLE #TABLEC
IF OBJECT_ID('TempDb..#TABLED') IS NOT NULL
DROP TABLE #TABLED
CREATE TABLE #TABLEA
(
SeqNo INT IDENTITY(1,1),
MyDate DATE
)
CREATE TABLE #TABLEB
(
SeqNo INT IDENTITY(1,1),
FullName VARCHAR(20)
)
CREATE TABLE #TABLEC
(
SeqNo INT IDENTITY(1,1),
FullName VARCHAR(20),
MyDate DATE
)
CREATE TABLE #TABLED
(
SeqNo INT,
MyDate DATE,
FullName VARCHAR(20)
)
INSERT INTO #TABLEA
(
MyDate
)
SELECT GETDATE()
UNION
SELECT GETDATE()+1
UNION
SELECT GETDATE()-1
INSERT INTO #TABLEB
(
FullName
)
VALUES('A'),('B'),('C')
INSERT INTO #TABLEC
(
FullName
)
VALUES('A'),('B'),('C')
UPDATE C
SET MyDate = A.MyDate
OUTPUT
deleted.SeqNo,
deleted.MyDate,
B.FullName
INTO #TABLED
FROM #TABLEC C
INNER JOIN #TABLEB B
ON C.FullName = B.FullName
INNER JOIN #TABLEA A
ON A.SeqNo = B.SeqNo
SELECT * FROM #TABLED

Related

Set column values in TableA to corresponding values from TableB if some value exist in TableC

I have a TableA that I just added two columns to, StartDate and StopDate. It also contain the column UserName.
I want to fill it with values from TableB that also has StartDate and StopDate. TableB has another column called UserId.
TableC has two columns, Id and Name.
This is what I want to do, explained with some kind of pseudocode:
for each row in TableB, where TableB.UserId exists in TableC.Id, take the corresponding row in TableA where TableA.UserName = TableC.Name and set the TableA.StartDate = TableB.StartDate & TableA.StopDate = TableB.StopDate.
I tried to create a join statement that would do it but the result was kind of embarrassing.
Help would be much appreciated.
Edit: What I have tried:
UPDATE TableA
SET STARTDATE = (
SELECT TableB.StartDate
FROM TableB
WHERE TableB.UserId = (??? what should I write here)?
And then same for StopDate
You can achieve this using the "update-from" syntax.
*I see you've already solved your question, but I'll post this as a possible alternative.
declare #TableA table (id int identity(1, 1), startDate datetime, stopDate datetime, userName varchar(20))
declare #TableB table (id int identity(1, 1), startDate datetime, stopDate datetime, userId int)
declare #TableC table (userId int, userName varchar(20))
insert into #TableA (userName)
select 'A' union all select 'B'
insert into #TableB (userId, startDate, stopDate)
select 1, '2015-01-01', '2015-01-31' union all select 2, '2015-12-01', '2015-12-31'
insert into #TableC
select 1, 'A' union all select 2, 'B'
update
A
set
A.startDate = B.startDate,
A.stopDate = B.stopDate
from
#TableA A
inner join #TableC C on C.userName = A.userName
inner join #TableB B on B.userId = C.userId
select
*
from
#TableA
I solved it, here is my solution
UPDATE TableA
SET StartDate = up.StartDate, EndDate = up.EndDate
FROM TableB up WHERE up.UserID = (SELECT Id from TableC u where u.Name = TableA.UserName)

How can I pull data from multiple sql tables using a join statement

I'm designing a simple in-office ticket system, and would like to include a field for the party responsible for the next action. To do so right this moment I'm thinking of using tableName and tableID as specifiers for the specific responsible party (could be a technician, customer, or third party, all in different tables)
It would be fine to pull that data in and run another select call using the name of the table as a parameter, but the extra data flow slows things down significantly.
Is there a way to use a single join statement to return the details of the party with a column for the table name and one for the individual table id or is there a better way to store the data from multiple potential tables?
You can use left join to achieve your requirement :-
Set Nocount On;
Declare #OfficeTickets Table
(
Id Int Identity(1,1)
,Column1 Varchar(100)
,PartyType Varchar(1)
,TechnicianId Int Null
,CustomerId Int Null
,ThirdPartyId Int Null
)
Declare #OfficeTickets1 Table
(
Id Int Identity(1,1)
,Column1 Varchar(100)
,TableName Varchar(100)
,TableId Int Null
)
Declare #Technician Table
(
Id Int Identity(1,1)
,TechnicianName Varchar(100)
)
Declare #Customers Table
(
Id Int Identity(1,1)
,CustomerName Varchar(100)
)
Declare #ThirdParty Table
(
Id Int Identity(1,1)
,ThirdPartyName Varchar(100)
)
Insert Into #Technician(TechnicianName) Values
('Technician_1')
,('Technician_2')
,('Technician_3')
Insert Into #Customers(CustomerName) Values
('Customer_1')
,('Customer_2')
,('Customer_3')
Insert Into #ThirdParty(ThirdPartyName) Values
('ThirdParty_1')
,('ThirdParty_2')
,('ThirdParty_3')
,('ThirdParty_4')
Insert Into #OfficeTickets(Column1,PartyType,TechnicianId,CustomerId,ThirdPartyId) Values
('ABC','T',3,Null,Null)
,('XYZ','C',Null,2,Null)
,('PUQ','P',Null,Null,4)
Insert Into #OfficeTickets1(Column1,TableName,TableId) Values
('ABC','Technician',3)
,('XYZ','Customers',2)
,('PUQ','ThirdParty',4)
---- taken separate columns for parties
Select ot.Id
,ot.Column1
,t.TechnicianName
,c.CustomerName
,tp.ThirdPartyName
From #OfficeTickets As ot
Left Join #Technician As t On ot.PartyType = 'T' And ot.TechnicianId = t.Id
Left Join #Customers As c On ot.PartyType = 'C' And ot.CustomerId = c.Id
Left Join #ThirdParty As tp On ot.PartyType = 'P' And ot.ThirdPartyId = tp.Id
---- by TableName and TableId
Select ot.Id
,ot.Column1
,t.TechnicianName
,c.CustomerName
,tp.ThirdPartyName
From #OfficeTickets1 As ot
Left Join #Technician As t On ot.TableName = 'Technician' And ot.TableId = t.Id
Left Join #Customers As c On ot.TableName = 'Customers' And ot.TableId = c.Id
Left Join #ThirdParty As tp On ot.TableName = 'ThirdParty' And ot.TableId = tp.Id
output:-

Merging two parent > child table sets

I need to get the data in two parent > child table sets merged/combined into a third parent > child table.
The tables look like this:
The only difference in the three sets of tables is that TableC has a TableType column to help discern the difference between a TableA record and a TableB record.
My first thought was to use a cursor.. Here's code to create the table structure, insert some records, and then merge the data together. It works very well, sooooo....
--Create the tables
CREATE TABLE TableA
(
ID int not null identity primary key,
Name VARCHAR(30)
);
CREATE TABLE TableAChild
(
ID int not null identity primary key,
Parent int not null,
Name VARCHAR(30),
CONSTRAINT FK_A FOREIGN KEY (Parent) REFERENCES TableA(ID)
);
CREATE TABLE TableB
(
ID int not null identity primary key,
Name VARCHAR(30)
);
CREATE TABLE TableBChild
(
ID int not null identity primary key,
Parent int not null,
Name VARCHAR(30),
CONSTRAINT FK_B FOREIGN KEY (Parent) REFERENCES TableB(ID)
);
CREATE TABLE TableC
(
ID int not null identity primary key,
TableType VARCHAR(1),
Name VARCHAR(30)
);
CREATE TABLE TableCChild
(
ID int not null identity primary key,
Parent int not null,
Name VARCHAR(30),
CONSTRAINT FK_C FOREIGN KEY (Parent) REFERENCES TableC(ID)
);
-- Insert some test records..
INSERT INTO TableA (Name) Values ('A1')
INSERT INTO TableAChild (Name, Parent) VALUES ('A1Child', SCOPE_IDENTITY())
INSERT INTO TableB (Name) Values ('B1')
INSERT INTO TableBChild (Name, Parent) VALUES ('B1Child', SCOPE_IDENTITY())
-- Needed throughout..
DECLARE #ID INT
-- Merge TableA and TableAChild into TableC and TableCChild
DECLARE TableACursor CURSOR
-- Get the primary key from TableA
FOR SELECT ID FROM TableA
OPEN TableACursor
FETCH NEXT FROM TableACursor INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
-- INSERT INTO SELECT the parent record into TableC, being sure to specify a TableType
INSERT INTO TableC (Name, TableType) SELECT Name, 'A' FROM TableA WHERE ID = #ID
-- INSERT INTO SELECT the child record into TableCChild using the parent ID of the last row inserted (SCOPE_IDENTITY())
-- and the current record from the cursor (#ID).
INSERT INTO TableCChild(Name, Parent) SELECT Name, SCOPE_IDENTITY() FROM TableAChild WHERE Parent = #ID
FETCH NEXT FROM TableACursor INTO #ID
END;
CLOSE TableACursor
DEALLOCATE TableACursor
-- Repeat for TableB
DECLARE TableBCursor CURSOR
FOR SELECT ID FROM TableB
OPEN TableBCursor
FETCH NEXT FROM TableBCursor INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO TableC (Name, TableType) SELECT Name, 'B' FROM TableB WHERE ID = #ID
INSERT INTO TableCChild(Name, Parent) SELECT Name, SCOPE_IDENTITY() FROM TableBChild WHERE Parent = #ID
FETCH NEXT FROM TableBCursor INTO #ID
END;
CLOSE TableBCursor
DEALLOCATE TableBCursor
Now, my question(s):
I've always been told that cursors are bad. But I couldn't find another way of doing it. I'm wondering if there's some way to do that with a CTE?
If the cursor is appropriate in this situation, how did I do? Is there a better way of doing what I did? It doesn't look very DRY to me, but I'm no SQL expert.
Lastly, if you want to re-run the query above, here's a small script to delete the tables that were created.
DROP TABLE TableAChild
DROP TABLE TableBChild
DROP TABLE TableCChild
DROP TABLE TableA
DROP TABLE TableB
DROP TABLE TableC
The correct result should look like:
You can use merge as described by Adam Machanic in Dr. OUTPUT or: How I Learned to Stop Worrying and Love the MERGE and in this question to get a mapping between the new identity value and the old primary key value in a table variable and the use that when you insert to your child tables.
declare #T table(ID int, IDC int);
merge dbo.TableC as C
using dbo.TableA as A
on 0 = 1
when not matched by target then
insert (TableType, Name) values('A', A.Name)
output A.ID, inserted.ID into #T(ID, IDC);
insert into dbo.TableCChild(Parent, Name)
select T.IDC, AC.Name
from dbo.TableAChild as AC
inner join #T as T
on AC.Parent = T.ID;
delete from #T;
merge dbo.TableC as C
using dbo.TableB as B
on 0 = 1
when not matched by target then
insert (TableType, Name) values('B', B.Name)
output B.ID, inserted.ID into #T(ID, IDC);
insert into dbo.TableCChild(Parent, Name)
select T.IDC, BC.Name
from dbo.TableBChild as BC
inner join #T as T
on BC.Parent = T.ID;
SQL Fiddle
Here is one way to do this without a cursor or other RBAR type stuff.
ALTER TABLE TableC ADD LegacyID INT
GO
INSERT INTO TableC (TableType, Name, LegacyID)
SELECT 'A', Name, ID
FROM TableA
INSERT TableCChild
SELECT C.ID, AC.Name
FROM TableAChild AC
JOIN TableA A ON A.Id = AC.ID
JOIN TableC C ON C.LegacyID = A.ID AND C.TableType = 'A'
INSERT INTO TableC (TableType, Name, LegacyID)
SELECT 'B', Name, ID
FROM TableB
INSERT TableCChild
SELECT C.ID, AC.Name
FROM TableBChild AC
JOIN TableB A ON A.Id = AC.ID
JOIN TableC C ON C.LegacyID = A.ID AND C.TableType = 'B'
ALTER TABLE TableC DROP COLUMN LegacyID
GO
You can use a map table to link the old and new ids together based on some key.
In my example, I am using the order of insertion into TableC.
Create a map table with an identity column.
Add data in TableC table based on order of ID of TableA and get the inserted ids in the map
Use the same order of TableA.id to get a ROWNUMBER() and match it with the identity column of the map table and update the old_id in map to match TableA.id with TableC.id .
Use the map to insert into the TableCChild table
Truncate the map and rinse and repeat for other tables.
Sample Query
CREATE TABLE #map(id int identity,new_id int,old_id int);
INSERT INTO TableC
(
TableType,
Name
)output inserted.id into #map(new_id)
SELECT 'A',Name
FROM TableA
ORDER BY ID
update m
set m.old_id = ta.id
FROM #map m
inner join
(
select row_number()OVER(order by id asc) rn,id
from tableA
)ta on ta.rn = m.id
INSERT INTO TableCChild (Name, Parent)
SELECT Name,M.new_ID
FROM #Map M
INNER JOIN TableAChild TA ON M.old_id = TA.Parent
TRUNCATE TABLE #map
INSERT INTO TableC
(
TableType,
Name
)output inserted.id into #map(new_id)
SELECT 'B',Name
FROM TableB
ORDER BY ID
update m
set m.old_id = tb.id
FROM #map m
inner join
(
select row_number()OVER(order by id asc) rn,id
from tableB
)tb on tb.rn = m.id
INSERT INTO TableCChild (Name, Parent)
SELECT Name,M.new_ID
FROM #Map M
INNER JOIN TableBChild TB ON M.old_id = TB.Parent
DROP TABLE #Map
I just wrote the following SQL to do it if the Name is unique in TableA and unique in TableB
INSERT INTO TableCChild
(
Parent,
NAME
)
SELECT tc.ID,
ta.Name
FROM TableAChild AS ta
JOIN TableA a
ON a.ID = ta.Parent
JOIN TableC AS tc
ON tc.Name = a.Name
AND tc.TableType = 'A'
UNION
SELECT tc.ID,
tb.Name
FROM TableBChild AS tb
JOIN TableB b
ON b.ID = tb.Parent
JOIN TableC AS tc
ON tc.Name = b.Name
AND tc.TableType = 'B'
If Name is not unique and only the ID is the Unique Identifier then I would add the LegacyId as suggested and the code would then be as follows
/* Change Table C to Have LegacyId as well and this is used to find the New Key for Inserts
CREATE TABLE TableC
(
ID INT NOT NULL IDENTITY PRIMARY KEY,
TableType VARCHAR(1),
LegacyId INT,
NAME VARCHAR(30)
);
*/
INSERT INTO TableC (Name, TableType, LegacyId)
SELECT DISTINCT NAME,
'A',
Id
FROM TableA
UNION
SELECT DISTINCT NAME,
'B',
Id
FROM TableB
INSERT INTO TableCChild
(
Parent,
NAME
)
SELECT tc.ID,
ta.Name
FROM TableAChild AS ta
JOIN TableA a
ON a.ID = ta.Parent
JOIN TableC AS tc
ON tc.LegacyId = a.Id
AND tc.TableType = 'A'
UNION
SELECT tc.ID,
tb.Name
FROM TableBChild AS tb
JOIN TableB b
ON b.ID = tb.Parent
JOIN TableC AS tc
ON tc.LegacyId = b.Id
AND tc.TableType = 'B'
We can reach this by turning the Identity column off till we finish the insertion like the following example.
--Create the tables
CREATE TABLE TableA
(
ID int not null identity primary key,
Name VARCHAR(30)
);
CREATE TABLE TableAChild
(
ID int not null identity primary key,
Parent int not null,
Name VARCHAR(30),
CONSTRAINT FK_A FOREIGN KEY (Parent) REFERENCES TableA(ID)
);
CREATE TABLE TableB
(
ID int not null identity primary key,
Name VARCHAR(30)
);
CREATE TABLE TableBChild
(
ID int not null identity primary key,
Parent int not null,
Name VARCHAR(30),
CONSTRAINT FK_B FOREIGN KEY (Parent) REFERENCES TableB(ID)
);
CREATE TABLE TableC
(
ID int not null identity primary key,
TableType VARCHAR(1),
Name VARCHAR(30)
);
CREATE TABLE TableCChild
(
ID int not null identity primary key,
Parent int not null,
Name VARCHAR(30),
CONSTRAINT FK_C FOREIGN KEY (Parent) REFERENCES TableC(ID)
);
-- Insert some test records..
INSERT INTO TableA (Name) Values ('A1')
INSERT INTO TableAChild (Name, Parent) VALUES ('A1Child', SCOPE_IDENTITY())
INSERT INTO TableB (Name) Values ('B1')
INSERT INTO TableBChild (Name, Parent) VALUES ('B1Child', SCOPE_IDENTITY())
SET IDENTITY_INSERT TableC ON
INSERT INTO TableC(ID, TableType, Name)
SELECT ID, 'A', Name FROM TableA
INSERT INTO TableCChild(Parent, Name)
SELECT Parent, Name FROM TableAChild
DECLARE #MAXID INT
SELECT #MAXID = MAX(ID) FROM TableC
PRINT #MAXID
SET IDENTITY_INSERT TableC ON
INSERT INTO TableC(ID, TableType, Name)
SELECT ID + #MAXID, 'B', Name FROM TableB
SET IDENTITY_INSERT TableC OFF
INSERT INTO TableCChild(Parent, Name)
SELECT Parent + #MAXID, Name FROM TableBChild
SET IDENTITY_INSERT TableC OFF
SELECT * FROM TableC
SELECT * FROM TableCChild
DROP TABLE TableAChild
DROP TABLE TableBChild
DROP TABLE TableCChild
DROP TABLE TableA
DROP TABLE TableB
DROP TABLE TableC
If you need to insert records in third table TableC and TableCChild for later use then it's fine to insert data in these tables but if you only need this table data to use it in your stored procedure for the time being then you can also just work with first two tables to get the desired result.
select * from (
select a.ID,'A' as TableType,a.Name from TableA a inner join TableAChild b on a.ID=b.ID
union
select a.ID,'B' as TableType,a.Name from TableB a inner join TableBChild b on a.ID=b.ID) TableC
Similarly get TableCChild
select * from
(
select b.ID,b.Parent,b.Name from TableA a inner join TableAChild b on a.ID=b.ID
union
select b.ID,b.Parent,b.Name from TableB a inner join TableBChild b on a.ID=b.ID) TableCChild
And if you have to insert in TableC and TableCChild then you have to recreate TableC with primary key on ID and TableType, and turn off the identity for ID column.

Query to update records from two different tables

I have a query that updates table records.
UPDATE #TEMP
SET [fld_LastName] = CustomerProfile.fld_LastName
,fld_FirstName = CustomerProfile.fld_FirstName
,fld_BirthDate = CustomerProfile.fld_BirthDate
FROM [DB_1].[dbo].[tbl_Customer] AS CustomerProfile --c
WHERE CustomerProfile.fld_CustomerNo = #TEMP.fld_CustomerNo
I want to update the records when the customer is not present in:
[DB_1].[dbo].[tbl_Customer]
I would want to look up to:
[DB_2].[dbo].[tbl_Customer]
How can i do that in an SQL Query?
Thanks a lot.
This should work for you:
UPDATE B
SET [fld_LastName] = CustomerProfile.fld_LastName
,fld_FirstName = CustomerProfile.fld_FirstName
,fld_BirthDate = CustomerProfile.fld_BirthDate
FROM [DB_1].[dbo].[tbl_Customer] AS CustomerProfile INNER JOIN #TEMP B
ON CustomerProfile.fld_CustomerNo = B.fld_CustomerNo
WHERE B.[fld_LastName] IS NULL OR B.fld_FirstName IS NULL OR B.fld_BirthDate IS NULL
You can do it with one update. Something like this:
DECLARE #a TABLE (id INT, val INT)
DECLARE #b TABLE (id INT, val INT)
DECLARE #c TABLE (id INT, val INT)
INSERT #a SELECT 1,NULL UNION SELECT 2,NULL UNION SELECT 3,30
INSERT #b SELECT 1,10
INSERT #c SELECT 1,20 UNION SELECT 2,20
SELECT * FROM #a
UPDATE #a
SET val = COALESCE(b.val,c.val,a.val)
FROM #a a
LEFT JOIN
#b b ON a.id=b.id
LEFT JOIN
#c c ON a.id=c.id
SELECT * FROM #a
This assumes that you prefer NOT NULL values from DB_2 to NULL values in DB_1--if those fields are even nullable.
UPDATE #TEMP
SET fld_LastName = COALESCE(b.fld_LastName,c.fld_LastName,a.fld_LastName)
,fld_FirstName = COALESCE(b.fld_FirstName,c.fld_FirstName,a.fld_FirstName)
,fld_BirthDate = COALESCE(b.fld_BirthDate,c.fld_BirthDate,a.fld_BirthDate)
FROM #Temp a
LEFT JOIN
DB_1.dbo.tbl_Customer b ON a.fld_CustomerNo = b.fld_CustomerNo
LEFT JOIN
DB_2.dbo.tbl_Customer c ON a.fld_CustomerNo = c.fld_CustomerNo

SQL join different tables depending on row information

Suppose I have table A with a field that can be either 1 or 2...
How do I select such that for each row in table A, if the field is 1, join the select with table B and if the field is 2, join the select with table C?
(
SELECT MyField1, MyField2 FROM A
INNER JOIN B ON A.Id = B.Id
AND A.MyField = 1
)
UNION
(
SELECT MyField1, MyField2 FROM A
INNER JOIN C ON A.Id = C.Id
AND A.MyField = 2
)
Somethine like this can work
DECLARE #TableA TABLE(
ID INT
)
DECLARE #TableB TABLE(
ID INT,
Val VARCHAR(50)
)
DECLARE #TableC TABLE(
ID INT,
Val VARCHAR(50)
)
INSERT INTO #TableA SELECT 1
INSERT INTO #TableA SELECT 2
INSERT INTO #TableB SELECT 1, 'B'
INSERT INTO #TableC SELECT 2, 'C'
SELECT *
FROM #TableA a INNER JOIN
#TableB b ON a.ID = b.ID
AND a.ID = 1
UNION
SELECT *
FROM #TableA a INNER JOIN
#TableC c ON a.ID = c.ID
AND a.ID = 2