SQL SCOPE_IDENTITY or OUTPUT with data from another table - sql

I'm trying to get the ID of each row inserted from a mass insert and create a mapping table with an ID from my temp table #InsertedClients. I'm using the same temp table for the initial insert but the PDATClientID is not part of the insert. I can't seem to figure out how to do this correctly. When I inserting into the Client_Mapping table using SCOPE_IDENTITY it only grabs the ID from the first insert and puts it along with all of my PDATClientIDs. After doing a lot of Googling I believe I should be using OUTPUT, but can't seem to figure how to put the INSERTED.CLID for each record together with each PDATClientID. I should also say that I can't use a cursor there are too rows. Any help is greatly appreciated!
IF NOT EXISTS (
SELECT ID
FROM sysobjects
WHERE id = object_id(N'[Client_Mapping]')
AND OBJECTPROPERTY(id, N'IsUserTable') = 1
)
CREATE TABLE [Client_Mapping] (
ClientMappingID INT Identity(1, 1) NOT NULL
,PDATClientID INT NOT NULL
,CLID INT NOT NULL
,AUDITDATE DATETIME NOT NULL
,CONSTRAINT [PK_Client_Mapping] PRIMARY KEY (ClientMappingID)
)
-- Begin Inserts
SELECT
First_Name
,Middle_Name
,Last_Name
,CONVERT(DATETIME, Date_Of_Birth) AS DOB
,a.Address_Line as Address1
,a.Address_Line_2 as Address2
,a.Zip_Code as ZipCode -- ??? Do I need to account for zipcode + 4
,REPLACE(REPLACE(REPLACE(cphp.Number_Or_Address, '(', ''), ')', '-'), ' ', '') AS HomePhone
,REPLACE(REPLACE(REPLACE(cpcp.Number_Or_Address, '(', ''), ')', '-'), ' ', '') AS CellPhone
,cpem.Number_Or_Address AS EMAIL
,c.Client_ID as PDATClientID
,c.Action AS [ClientAction]
,ca.Action as [ClientAddressAction]
,a.Action AS [AddressAction]
,cphp.Action AS [HomePhoneAction]
,cpcp.Action AS [CellPhoneAction]
,cpem.Action AS [EmailAction]
INTO #InsertClients
FROM Client c
LEFT JOIN Client_Address ca ON ca.Client_ID = c.Client_ID
LEFT JOIN Address a ON a.Address_ID = ca.Address_ID
LEFT JOIN Client_Phone cphp ON cphp.Client_ID = c.Client_ID
AND cphp.Phone_Email_Type = 'HP'
AND cphp.Start_Date = (
SELECT MAX(Start_Date)
FROM Client_Phone
WHERE Client_ID = c.Client_ID
)
LEFT JOIN Client_Phone cpcp ON cpcp.Client_ID = c.Client_ID
AND cpcp.Phone_Email_Type = 'CP'
AND cpcp.Start_Date = (
SELECT MAX(Start_Date)
FROM Client_Phone
WHERE Client_ID = c.Client_ID
)
LEFT JOIN Client_Phone cpem ON cpem.Client_ID = c.Client_ID
AND cpem.Phone_Email_Type = 'EM'
AND cpem.Start_Date = (
SELECT MAX(Start_Date)
FROM Client_Phone
WHERE Client_ID = c.Client_ID
)
where c.action ='I'
BEGIN TRY
BEGIN TRAN
INSERT INTO [dbo].[Clients] (
[FName]
,[MiddleInitial]
,[LName]
,[DOB]
,[Address1]
,[Address2]
,[ZipCode]
,[HomePhone]
,[CellPhone]
,[Email]
,[AuditStaffID]
,[AuditDate]
,[DateCreated]
)
Select
First_Name
,CASE when Middle_Name = '' THEN NULL ELSE Middle_Name END
,Last_Name
,DOB
,Address1
,Address2
,ZipCode
,HomePhone
,CellPhone
,EMail
,496 AS [AuditStaffID]
,CURRENT_TIMESTAMP AS [AuditDate]
,CURRENT_TIMESTAMP AS [DateCreated]
FROM
#InsertClients
Where
[ClientAction] = 'I'
INSERT INTO [Client_Mapping]
(
PDATClientID
,CLID
,AUDITDATE
)
SELECT
PDATClientID
,SCOPE_IDENTITY()
,CURRENT_TIMESTAMP
FROM #InsertClients
COMMIT
END TRY
BEGIN CATCH
ROLLBACK
SELECT ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage
END CATCH

you have a hell of a code in your question I can explain you in few line how you can retrieve all the inserted ids.
Yes SCOPE_IDENTITY() gives you the last IDENTITY value generated by the identity column. In you case you will need to use a table variable to get all the generated Identity values during your insert
/* Declare a table Vairable */
DECLARE #NewValues TABLE (IDs INT);
/* In Your Insert Statement use OUTPUT clause */
INSERT INTO Table_Name (Column1,Column2,Column3,....)
OUTPUT inserted.Identity_Column INTO #NewValues (IDs)
SELECT Column1,Column2,Column3,....
FROM Table_Name2
/* Finally Select values from your table variable */
SELECT * FROM #NewValues
Your Query
In your case your insert statement should look something like ...
INSERT INTO [dbo].[Clients] (
[FName]
,[MiddleInitial]
,[LName]
,[DOB]
,[Address1]
,[Address2]
,[ZipCode]
,[HomePhone]
,[CellPhone]
,[Email]
,[AuditStaffID]
,[AuditDate]
,[DateCreated]
)
OUTPUT inserted.[FName], inserted.[MiddleInitial] ,.... INTO #TableVarible(First_Name,[MiddleInitial].....)
Select
First_Name
,CASE when Middle_Name = '' THEN NULL ELSE Middle_Name END
,Last_Name
,DOB
,Address1
,Address2
,ZipCode
,HomePhone
,CellPhone
,EMail
,496 AS [AuditStaffID]
,CURRENT_TIMESTAMP AS [AuditDate]
,CURRENT_TIMESTAMP AS [DateCreated]
FROM
#InsertClients
Where
[ClientAction] = 'I'

Solved the problem by using this example found here: http://sqlserverplanet.com/tsql/match-identity-columns-after-insert. The example from that site is below.
-- Drop temp tables if they already exist
IF OBJECT_ID('TempDB..#source', 'U') IS NOT NULL DROP TABLE #source;
IF OBJECT_ID('TempDB..#target', 'U') IS NOT NULL DROP TABLE #target;
IF OBJECT_ID('TempDB..#xref', 'U') IS NOT NULL DROP TABLE #xref;
GO
-- Create the temp tables
CREATE TABLE #source (id INT PRIMARY KEY IDENTITY(1, 1), DATA INT);
CREATE TABLE #target (id INT PRIMARY KEY IDENTITY(1, 1), DATA INT);
CREATE TABLE #xref (ROW INT PRIMARY KEY IDENTITY(1, 1), source_id INT, target_id INT);
GO
-- If xref table is being reused, make sure to clear data and reseed by truncating.
TRUNCATE TABLE #xref;
-- Fill source table with dummy data (even numbers)
INSERT INTO #source (DATA)
SELECT 2 UNION SELECT 4 UNION SELECT 6 UNION SELECT 8;
GO
-- Fill target table with dummy data (odd numbers) to simulate existing records
INSERT INTO #target (DATA)
SELECT 1 UNION SELECT 3 UNION SELECT 5;
GO
-- Insert source table data into target table. IMPORTANT: Inserted data must be sorted by the source table's primary key.
INSERT INTO #target (DATA)
OUTPUT INSERTED.id INTO #xref (target_id)
SELECT DATA
FROM #source
ORDER BY id ASC;
GO
-- Update the xref table with the PK of the source table. This technique is used for data not captured during the insert.
;WITH src AS (SELECT id, ROW = ROW_NUMBER() OVER (ORDER BY id ASC) FROM #source)
UPDATE x
SET x.source_id = src.id
FROM #xref AS x
JOIN src ON src.ROW = x.ROW;
GO
-- Verify data
SELECT * FROM #source;
SELECT * FROM #target;
SELECT * FROM #xref;
GO
-- Clean-up
IF OBJECT_ID('TempDB..#source', 'U') IS NOT NULL DROP TABLE #source;
IF OBJECT_ID('TempDB..#target', 'U') IS NOT NULL DROP TABLE #target;
IF OBJECT_ID('TempDB..#xref', 'U') IS NOT NULL DROP TABLE #xref;
GO

Related

Update table variable

I have a table variable #searchResult:
DECLARE #searchResult TABLE (
[name_template] NVARCHAR(50),
[record_id] INT,
[record_name] NVARCHAR(50)
);
And table [records]:
CREATE TABLE [records] (
[record_id] INT IDENTITY(1, 1) PRIMARY KEY,
[record_name] NVARCHAR(50)
)
#searchResult contains records with [name_template] filled only. I want to update it with latest [record_id] and [record_name] from [records] table that match [name_template].
I've tried folowing SQL query with no success:
UPDATE #searchResult
SET [record_id] = r.[record_id], [record_name] = r.[record_name]
FROM (
SELECT TOP 1
r.[record_id]
, r.[record_name]
FROM [records] AS r
WHERE r.[record_name] LIKE [name_template]
ORDER BY r.[record_id] DESC
) AS r;
Error message:
Invalid column name 'name_template'.
What is correct syntax to update #searchResult with desired values?
You need to do a CROSS APPLY on the tables.
UPDATE #searchResult
SET [record_id] = r.[record_id],
[record_name] = r.[record_name]
FROM #searchResult SR
CROSS APPLY (
SELECT TOP 1 *
FROM [records]
WHERE [record_name] LIKE [name_template] -- Your wish, but do you really need LIKE matching??
ORDER BY [record_id] DESC
) AS r;
Try this:
UPDATE t
SET [record_id] = r.[record_id],
[record_name] = r.[record_name]
FROM #searchResult t
INNER JOIN
(
SELECT MAX([record_id]) As [record_id]
,[record_name]
FROM [records]
GROUP BY [record_name]
) r
ON r.[record_name] LIKE t.[name_template];
Update:
Seems to be working fine from what I've tested:
Create table and table variable:
CREATE TABLE [records] (
[record_id] INT IDENTITY(1, 1) PRIMARY KEY,
[record_name] NVARCHAR(50)
)
DECLARE #searchResult TABLE (
[name_template] NVARCHAR(50),
[record_id] INT,
[record_name] NVARCHAR(50)
);
Populate with sample data:
INSERT INTO [records] ([record_name]) VALUES('a'), ('a'), ('a'), ('b'), ('b')
INSERT INTO #searchResult ([name_template]) VALUES ('a'), ('b')
Update table variable:
UPDATE t
SET [record_id] = r.[record_id],
[record_name] = r.[record_name]
FROM #searchResult t
INNER JOIN
(
SELECT MAX([record_id]) As [record_id]
,[record_name]
FROM [records]
GROUP BY [record_name]
) r
ON r.[record_name] LIKE t.[name_template];
Check results:
SELECT *
FROM records
SELECT *
FROM #searchResult
DROP TABLE records
Results:
records
record_id record_name
----------- -----------
1 a
2 a
3 a
4 b
5 b
#searchResult
name_template record_id record_name
------------- --------- -----------
a 3 a
b 5 b

"Invalid Column" when using column from table variable

I'm trying to declare a table variable and then join it to a table I created in the database. Every time I try to insert my "NAME" field into my table, I get the error 'Invalid Column Name "NAME"', even though the GNAME field works fine. What am I doing wrong, and how can I join me NAME column?
DECLARE #Names TABLE
(
ID INT,
NAME VARCHAR(100),
GNAME VARCHAR(100)
)
INSERT INTO #Names
(
ID,
NAME,
GNAME
)
SELECT
CName.ID,
Ref.NAME,
Ref.GNAME
FROM
#CurrentPositions AS CName
LEFT OUTER JOIN
dbo.NameField AS Ref
ON
CName.ID = Ref.ID
IF ( OBJECT_ID('dbo.ReportTable', 'U') IS NOT NULL)
DROP TABLE dbo.ReportTable
CREATE TABLE [dbo].[ReportTable]
(
[ID_NUMBER] [INT],
[NAME] [VARCHAR](150)
[GNAME] [VARCHAR](150)
)
INSERT INTO [dbo].[ReportTable]
(
ID_NUMBER,
NAME,
GNAME
)
SELECT
C.ID_NUMBER,
N.NAME,
N.GNAME
FROM
#Names AS N
INNER JOIN
#CurrentPositions AS C
ON N.ID_NUMBER = C.ID_NUMBER
Try using a Temp table :
CREATE TABLE #Names
(
ID INT,
NAME VARCHAR(100),
GNAME VARCHAR(100)
)
INSERT INTO #Names
(
ID,
NAME,
GNAME
)
SELECT
CName.ID,
Ref.NAME,
Ref.GNAME
FROM
#CurrentPositions AS CName
LEFT OUTER JOIN
dbo.NameField AS Ref
ON
CName.ID = Ref.ID
IF ( OBJECT_ID('dbo.ReportTable', 'U') IS NOT NULL)
DROP TABLE dbo.ReportTable
CREATE TABLE [dbo].[ReportTable]
(
[ID_NUMBER] [INT],
[NAME] [VARCHAR](150)
[GNAME] [VARCHAR](150)
)
INSERT INTO [dbo].[ReportTable]
(
ID_NUMBER,
NAME,
GNAME
)
SELECT
C.ID_NUMBER,
N.NAME,
N.GNAME
FROM
#Names AS N
INNER JOIN
#CurrentPositions AS C
ON N.ID_NUMBER = C.ID_NUMBER
I've assumed that you will also change the table variable #CurrentPositions to a temp table
Just remember to drop the tables after you use them.
It is quite possible that all you need to do is wrap your field names in square brackets, e.g.
INSERT INTO #Names
(
[ID],
[NAME],
[GNAME]
)
SELECT
CName.[ID],
Ref.[NAME],
Ref.[GNAME]
FROM
#CurrentPositions AS CName
LEFT OUTER JOIN
dbo.NameField AS Ref
ON
CName.[ID] = Ref.[ID]
If that doesn't fix it, please post the schema of your #CurrentPositions and dbo.NameField tables.

how to insert multiple rows with check for duplicate rows in a short way

I am trying to insert multiple records (~250) in a table (say MyTable) and would like to insert a new row only if it does not exist already.
I am using SQL Server 2008 R2 and got help from other threads like SQL conditional insert if row doesn't already exist.
While I am able to achieve that with following stripped script, I would like to know if there is a better (short) way to do this as I
have to repeat this checking for every row inserted. Since we need to execute this script only once during DB deployment, I am not too much
worried about performance.
INSERT INTO MyTable([Description], [CreatedDate], [CreatedBy], [ModifiedDate], [ModifiedBy], [IsActive], [IsDeleted])
SELECT N'ababab', GETDATE(), 1, NULL, NULL, 1, 0
WHERE NOT EXISTS(SELECT * FROM MyTable WITH (ROWLOCK, HOLDLOCK, UPDLOCK)
WHERE
([InstanceId] IS NULL OR [InstanceId] = 1)
AND [ChannelPartnerId] IS NULL
AND [CreatedBy] = 1)
UNION ALL
SELECT N'xyz', 1, GETDATE(), 1, NULL, NULL, 1, 0
WHERE NOT EXISTS(SELECT * FROM [dbo].[TemplateQualifierCategoryMyTest] WITH (ROWLOCK, HOLDLOCK, UPDLOCK)
WHERE
([InstanceId] IS NULL OR [InstanceId] = 1)
AND [ChannelPartnerId] IS NULL
AND [CreatedBy] = 1)
-- More SELECT statements goes here
You could create a temporary table with your descriptions, then insert them all into the MyTable with a select that will check for rows in the temporary table that is not yet present in your destination, (this trick in implemented by the LEFT OUTER JOIN in conjunction with the IS NULL for the MyTable.Description part in the WHERE-Clause):
DECLARE #Descriptions TABLE ([Description] VARCHAR(200) NOT NULL )
INSERT INTO #Descriptions ( Description )VALUES ( 'ababab' )
INSERT INTO #Descriptions ( Description )VALUES ( 'xyz' )
INSERT INTO dbo.MyTable
( Description ,
CreatedDate ,
CreatedBy ,
ModifiedDate ,
ModifiedBy ,
IsActive ,
IsDeleted
)
SELECT d.Description, GETDATE(), 1, NULL, NULL, 1, 0
FROM #Descriptions d
LEFT OUTER JOIN dbo.MyTable mt ON d.Description = mt.Description
WHERE mt.Description IS NULL

inner join to table depend on values in first table column join second or third table

I have four tables User,Destination,City and visited
as follows
declare #user table
(
UserId int identity(1,1) not null ,
UserName nvarchar(500)
)
insert into #user (UserName) values ('rahul')
insert into #user (UserName) values ('nitin')
insert into #user (UserName) values ('yunus')
declare #destination table
(
destCode nvarchar(50),
destName nvarchar(500)
)
insert into #destination select 'SWDP','ranthambore national park '
insert into #destination select 'BTP','ghana national park '
declare #city table
(
cityId int identity(1,1) not null,
cityname nvarchar(500)
)
insert into #city select 'jaipur'
insert into #city select 'delhi'
-- visited table in which user id with either destCode or cityId
declare #visited table
(
UserId int ,
LocationFrom nvarchar(500),
LocationTo nvarchar(500),
LocType nvarchar(50)
)
insert into #visited select '1','BTP','1','city'
insert into #visited select '1','1','SWDP','dest'
insert into #visited select '2','1','2','city'
insert into #visited select '3','2','SWDP','dest'
insert into #visited select '3','SWDP','BTP','dest'
select * from #user
select * from #destination
select * from #city
select * from #visited
select * from #visited as v
inner join #user as u ON v.UserId=u.UserId
Location type column in visited table denotes the LocationTo type. Its either city or location .
Now i want to join these tables so i can get the data which user started from which city or destination and go to which city and destination .
Expected result is as below image
This is only for one user but i need same for all users .
I am not sure if i understand your table structure correctly
but as i see it the table #destination can be both an origin and an endpoint?
and the LocType only specifies the type of the endpoint (LocationTo in #visited)
Given this i thik you have to assume that a numeric value in #Visited.LocationFrom specifies an Id in #city
then this query might be of use
SELECT u.UserName,
CASE WHEN ISNUMERIC(v.LocationFrom) = 1 THEN (SELECT cityName FROM #city where cityId = cast(v.LocationFrom as int)) ELSE (SELECT destName FROM #destination where destCode = v.LocationFrom) END as LocationFrom,
CASE WHEN ISNUMERIC(v.LocationTo) = 1 THEN (SELECT cityName FROM #city where cityId = cast(v.LocationTo as int)) ELSE (SELECT destName FROM #destination where destCode = v.LocationTo) END as LocationTo
FROM #visited AS v
inner join #user AS u ON v.UserId=u.UserId
This should work for you:
;WITH Locations AS
(SELECT
CAST(CityID as nvarchar(50)) LocationId,
CityName LocationName
FROM
#city
UNION ALL
SELECT
DestCode,
DestName
FROM
#destination)
SELECT
Users.UserName,
LocationsFrom.LocationName LocationFromName,
LocationsTo.LocationName LocationToName
FROM
#visited Visited
INNER JOIN Locations LocationsFrom ON LocationsFrom.LocationID = Visited.LocationFrom
INNER JOIN Locations LocationsTo ON LocationsTo.LocationID = Visited.LocationTo
INNER JOIN #user Users ON Visited.UserID = Users.UserID

Is there a way to do another query within the insert query?

Ok so this is the query I have...I just added the ACCOUNTID and the #accountID portion which is obviosly not working
INSERT INTO Leads (
LEADID,
CREATEUSER,
CREATEDATE,
FIRSTNAME,
MODIFYDATE,
ACCOUNTID
)
SELECT
'Q' + cast(floor(999997 * RAND(convert(varbinary, newid()))) as varchar(20))
,'U6UJ9000S'
,CURRENT_TIMESTAMP
,'U6UJ9000S'
,name
,#accountID
FROM Temp
What I am trying to do is do an insert into the account table first and get that id and add the insert id to this insert into the leads table. Is that even possible
SO basically for each record in the Temp table i need to insert a record in the account table with no values just need the account_id so when i insert in the leads table i have the account id to make that insert
Setup:
USE TempDB;
GO
CREATE TABLE dbo.Leads
(
LeadID VARCHAR(64),
CreateUser VARCHAR(32),
CreateDate DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FirstName VARCHAR(32),
AccountID INT
);
CREATE TABLE dbo.Accounts
(
AccountID INT IDENTITY(1,1),
name VARCHAR(32) /* , ... other columns ... */
);
CREATE TABLE dbo.Temp(name VARCHAR(32));
INSERT dbo.Temp SELECT 'foo'
UNION SELECT 'bar';
Query:
INSERT dbo.Accounts
(
name
)
OUTPUT
'Q' + cast(floor(999997 * RAND(convert(varbinary, newid()))) as varchar(20)),
'U6UJ9000S',
CURRENT_TIMESTAMP,
inserted.name,
inserted.AccountID
INTO dbo.Leads
SELECT name
FROM dbo.Temp;
Check:
SELECT * FROM dbo.Accounts;
SELECT * FROM dbo.Leads;
Cleanup:
USE tempdb;
GO
DROP TABLE dbo.Temp, dbo.Accounts, dbo.Leads;
The problem you will probably end up hitting in practice with Aaron's use of composable DML is that chances are in reality you will have an FK defined to constrain Leads(AccountId) to a valid value in which case you will hit the error.
The target table 'dbo.Leads' of the OUTPUT INTO clause cannot be on
either side of a (primary key, foreign key) relationship. Found
reference constraint 'FK_foo'.
To avoid this issue you can use
INSERT INTO dbo.Leads
EXEC('
INSERT INTO dbo.Accounts
OUTPUT
''Q'' + cast(floor(999997 * RAND(convert(varbinary, newid()))) as varchar(20)),
''U6UJ9000S'',
CURRENT_TIMESTAMP,
inserted.name,
inserted.AccountID
SELECT name
FROM dbo.Temp;
')
It is not working because you are inserting 6 values but you are specifying only 5 columns:
These are 5 columns:
LEADID,
CREATEUSER,
CREATEDATE,
FIRSTNAME,
ACCOUNTID
Ant these are 6 values:
'Q' + cast(floor(999997 * RAND(convert(varbinary, newid()))) as varchar(20))
,'U6UJ9000S'
,CURRENT_TIMESTAMP
,'U6UJ9000S'
,name
,#accountID
I don't know where you get the #accountID from, but I imagine you define it somewhere else above.
You can get #accountID as follows, after you do the insert to the Account table:
select #accountID=scope_identity()
And then execute the insert into the Leads table.
UPDATE: EXAMPLE:
declare #accountID int
INSERT INTO Account (col1,col2,col...)
values ('foo','bar','baz')
select #accountID=SCOPE_IDENTITY()
INSERT INTO Leads (
LEADID,
CREATEUSER,
CREATEDATE,
FIRSTNAME,
ACCOUNTID
)
values
(
'Q' + cast(floor(999997 * RAND(convert(varbinary, newid()))) as varchar(20)) --leadid
,'U6UJ9000S' --createuser
,CURRENT_TIMESTAMP --createdate
,t.name --firstname
,#accountID --accountID
)
set a variable to Scope_identity() (which returns the last id that was created) and use that
With SQL Server 2005 or higher you can use the OUTPUT clause.
CREATE TABLE #Inserted (AccountID, AccountName)
INSERT Account (AccountName)
OUTPUT Inserted.AccountID, Inserted.AccountName
INTO #Inserted
SELECT AccountName
FROM Temp
INSERT Leads (
LEADID,
CREATEUSER,
CREATEDATE,
FIRSTNAME,
ACCOUNTID
)
SELECT
'Q' + cast(floor(999997 * RAND(convert(varbinary, newid()))) as varchar(20))
,'U6UJ9000S'
,CURRENT_TIMESTAMP
,t.name
,i.AccountID
FROM Temp AS t
JOIN #Inserted AS i ON t.AccountName= i.AccountName
You can declare an variable, set it to the desired id and the use the variable in the insert.