Assign values from a table variable to each row of a table? - sql

I have a table (Reference table) that will hold unique values (uniqueidentifer types).
I have two million users and I need to generate a unique value for each one in this Reference table.
As I create them, I would like to capture these unique values and assign them to each user in another table, called a User table.
Below is my attempt: I have created a table variable called #ReferenceIds. How can I take #ReferenceIds and assign each unique value to a separate row in the User table?
create procedure create_reference_ids
#numberOfNewAccounts int
DECLARE #ReferenceIds TABLE (Id uniqueidentifier)
set #numberOfNewAccounts = 2000000
as
begin
while #numberOfNewAccounts > 0
begin
insert into Reference (0,0,#UtcNow,#UtcNow)
OUTPUT Inserted.Id INTO #ReferenceIds
set #numberOfNewAccounts = #numberOfNewAccounts - 1
end
end
exec create_reference_ids

Use a merge statement to insert the exact number of reference values as you have users, and output the new Ids into a table variable which links your new reference ids to your existing user ids. Then carry out an update on your users table.
DECLARE #NewId TABLE (UserId int, ReferenceId int);
-- Using Merge for insert is the only way to get values from columns in the output table which don't exist in the Target table
MERGE INTO dbo.Reference AS Target
USING dbo.[User] AS Source
ON 1 = 0 -- Force an insert regardless
WHEN NOT MATCHED THEN
-- Best practice is to always explicitly name your columns
INSERT (Col1, Col2, Col3, Col4)
VALUES (0, 0, #UtcNow, #UtcNow)
OUTPUT Source.Id, Inserted.Id
INTO #NewId (UserId, ReferenceId);
UPDATE U SET
ReferenceId = I.ReferenceId
FROM dbo.[User] U
INNER JOIN #NewId I on I.UserId = U.Id;

Related

How to insert data from source table (but different columns) into two different tables in SQL Server

I have a staging table titled [Staging]. The data from this table needs to be inserted into two separate tables. Half of the columns go to the first table (we'll call it [Table1]) and the other half go to a second table (we'll call it [Table2])
Both of these tables have a column titled "ChainID". In [Table1] the ChainID is an identity column. In [Table2] it's not.
The ChainID is the one column that links these two tables together for when we need to query this data.
I currently have it set up to where it will do the insert into [Table1] which then generates the new ChainIds. I can use "OUTPUT INSERTED.ChainID" to get the ChainId's that were generated but my problem is tying this back to the original staging table in order to grab the rest of the data for the second table.
DECLARE #Staging TABLE
(
[RowID] [int],
[ChainID] [varchar](50) ,
[LoanNo] [varchar](50) ,
[AssignmentFrom] [varchar](4000),
[AssignmentTo] [varchar](4000),
[CustodianUID] [nvarchar](100) null,
[DocCode] [nvarchar](100) null
)
INSERT
#Staging
SELECT
RowID,
ChainID,
LoanNo,
AssignmentFrom,
AssignmentTo,
CustodianUID,
DocCode
FROM
[MDR_CSV].[dbo].[TblCollateralAssignmentChainImport]
WHERE
UploadID = 1
This is where we do the insert into the first table which generates the new chainIds that will be needed to merge into Table2.
INSERT INTO
Table1
SELECT
LoanNo,
AssignmentFrom,
AssignmentTo,
CustodianUID
FROM
#Assignments AS MDRCA
WHERE
MDRCA.ChainID IS NULL
Now I need to insert the data from the DocCode field into Table2. I can get the list of newly generated ChainIds by doing something such as
OUTPUT INSERTED.ChainID
But that doesn't help being able to tie the newly generated chainId's back to the corresponding data rows from the Staging table in order to do the insert into Table2.
I ended up figuring out a solution. I used a curser to go through each row of data from the staging table one by one. That allowed me to do the insert into the first table (with only the pertinent columns from the staging table) along with doing an OUTPUT.INSTERTED ChainID and stored that newly generated ChainID into a table variable. I then assign that ChainID value to a regular variable by doing
#ChainID = (SELECT TOP 1 * FROM #tableVariable)
I could then use this ChainID to insert into the second table along with the rest of the data from the staging table that pertained to the same row of data (which was possible due to the curser)
Then before the curser loops I drop the table variable and recreate it back at the top of the loop so that the SELECT TOP 1 works correctly each time.
Not sure if it's the best or more efficient way to go about this but at least it worked!
Here's an example showing how I got it to work:
TblCollateralAssignmentChainImport is the Staging Table
temp_tblCollateralAssignment is Table1
temp_CustodianData is Table2
DECLARE #RowID int, #ChainID int;
DECLARE import_cur CURSOR FOR
SELECT RowID
FROM [MDR_CSV].[dbo].[TblCollateralAssignmentChainImport]
WHERE ChainId IS NULL
order by RowID;
OPEN import_cur
FETCH NEXT FROM import_cur
INTO #RowID
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #NewlyCreatedChainId table (nChainId int);
INSERT INTO temp_tblCollateralAssignment
OUTPUT INSERTED.ChainID INTO #NewlyCreatedChainId
SELECT LoanNo, AssignmentFrom, AssignmentTo
FROM TblCollateralAssignmentChainImport
WHERE RowId = #RowID
SET #ChainID = (SELECT TOP 1 nChainId FROM #NewlyCreatedChainId)
INSERT INTO temp_CustodianData (ChainID, LoanNo, CustodianUID, DocCode)
SELECT #ChainID, import.LoanNo, import.CustodianUID, import.DocCode
FROM TblCollateralAssignmentChainImport AS import
WHERE RowId = #RowID
DELETE FROM #NewlyCreatedChainId
FETCH NEXT FROM import_cur
INTO #RowID
END
CLOSE import_cur;
DEALLOCATE import_cur;

Update table column with primary key after insert (in one statement)

Using an update statement, how can i directly set a column value to the primary key of an insert statement? I need this as one statement.
I know the following is wrong, but it helps get my idea through:
update AppNationalities
set CountrySelectionID = (
select [INSERTED.pkID] from
(
insert into CountrySelections
output INSERTED.pkID
values(CountryID, 'test', 0)
)
)
Put the values into a temporary table:
declare #ids table (pkID int);
insert into CountrySelections
output INSERTED.pkID into #ids
values (CountryID, 'test', 0);
update AppNationalities
set CountrySelectionID = i.id
from #ids i;
I don't think the output clause is allowed in the FROM clause of an UPDATE or DELETE statement.

How to get a inserted id in other table from inside a trigger?

I have 3 tables tbl_Users, tbl_Protocol and tbl_ProtocolDetails and inside of my trigger on Users, I have to inserted into Protocol and then insert into ProtocolDetails, but I don't know how work the inserted scope.
Something like that:
CREATE TRIGGER tg_Users ON tbl_Users
AFTER INSERT, UPDATE AS
BEGIN
DECLARE #UserId = Int
DECLARE #ProtocolId = Int
DECLARE #UserDetail = NVARCHAR(255)
SELECT
#UserId = user_id,
#UserDetail = user_detail + '#' + user_explanation
FROM INSERTED
INSERT INTO tbl_Protocol (user_id, inserted_date)
VALUES (#UserId, GetDate())
-- Return Inserted Id from tbl_Protocol into #ProtocolDetail then
INSERT INTO tbl_ProtocolDetails (protocol_id, protocol_details)
VALUES (#ProtocolId, #UserDetail)
END
Your trigger has a MAJOR flaw in that you seems to expect to always have just a single row in the Inserted table - that is not the case, since the trigger will be called once per statement (not once for each row), so if you insert 20 rows at once, the trigger is called only once, and the Inserted pseudo table contains 20 rows.
Therefore, code like this:
Select #UserId = user_id,
#UserDetail = user_detail + '#' + user_explanation
From INSERTED;
will fail, since you'll retrieve only one (arbitrary) row from the Inserted table, and you'll ignore all other rows that might be in Inserted.
You need to take that into account when programming your trigger! You have to do this in a proper, set-based fashion - not row-by-agonizing-row stlye!
Try this code:
CREATE TRIGGER tg_Users ON tbl_Users
AFTER INSERT, UPDATE AS
BEGIN
-- declare an internal table variable to hold the inserted "ProtocolId" values
DECLARE #IdTable TABLE (UserId INT, ProtocolId INT);
-- insert into the "tbl_Protocol" table from the "Inserted" pseudo table
-- keep track of the inserted new ID values in the #IdTable
INSERT INTO tbl_Protocol (user_id, inserted_date)
OUTPUT Inserted.user_id, Inserted.ProtocolId INTO #IdTable(UserId, ProtocolId)
SELECT user_id, SYSDATETIME()
FROM Inserted;
-- insert into the "tbl_ProtocolDetails" table from both the #IdTable,
-- as well as the "Inserted" pseudo table, to get all the necessary values
INSERT INTO tbl_ProtocolDetails (protocol_id, protocol_details)
SELECT
t.ProtocolId,
i.user_detail + '#' + i.user_explanation
FROM
#IdTable t
INNER JOIN
Inserted i ON i.user_id = t.UserId
END
There is nothing in this trigger that would handle a multiple insert/update statement. You will need to either use one scenario that will handle multiple records or check how many records were effected with a IF ##ROWCOUNT = 1 else statement. In your example, I would just use something like
insert into tbl_Protocol(user_id, inserted_date)
select user_id, user_detail + '#' + user_explanation
From INSERTED;
As for your detail table, I see Marc corrected his answer to include the multiple lines and has a simple solution or you can create a second trigger on the tbl_Protocol. Another solution I have used in the past is a temp table for processing when I have very complicated triggers.

SQL Insert ,Update using Merge Store Procedure

I need to do a bulk Insert and/or Update for a target table in a SQL database. I have already tried the Merge stored procedure. In the target table I have a composite key.
I need to generate a pattern for first field (of the key) while inserting. I have tried a user-defined function that returns a pattern key which is unique for each of the rows.
My question is, how do I incorporate this function into a query that would auto-generate the key and insert the respective fields for each record?
TargetTable is my destination table in database and it has two columns - Key1(Bigint),Key2(int). Once again, this is a composite key.
Note: I only want to generate the key for the first field/column.
Here's my stored procedure:
Merge Targettable as T
Using SourceTable as s
ON s.Key1 = T.Key1 and
s.Key2=T.Key2
WHEN MATCHED THEN UPDATE
SET T.UserName = S.UserName
WHEN NOT MATCHED THEN
INSERT (Key1, Key2, UserName)
VALUES (dbo.UserDefiendFunction(#parama1),Key2,userName)
UserDefinedFunction returns the pattern i want.
Here's my User Defined Function:
Create function [dbo].[UserDefinedFunction] (
#Param1 int)
returns BIGINT
AS Begin
DECLARE #ResultVar BIGINT
SET #ResultVar=CONVERT(BIGINT, (SELECT Cast(#Param1 AS VARCHAR(10))
+ '' + Format((SELECT Count(userName)+1 FROM [dbo].[TableName]), '0')) )
RETURN #ResultVar
END
Please help me out with this. Thanks in advance.
IF EXISTS (SELECT * FROM Table1 join table 2 on Column1='SomeValue')
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
ELSE
insert into ods..Table1 (Columname)
select [DatabaseName].[dbo].[UserDefinedFunction] (#paramater) as ColumnName

Get SCOPE_IDENTITY value when inserting bulk records for SQL TableType

I have following table structure, for convenience purpose I am only marking individual columns
Table_A (Id, Name, Desc)
Table_1 (Id this is identity column, Name....)
Table_2 (Id this is identity column, Table_A_Id, Table_1_Id)
The relationship between Table_1 and Table_2 is 1...*
Now I have created a table type for Table_A called TType_Table_A (which only contains Id as column and from my C# app I send multiple records). I have achieved this bulk insert functionality as desired.
What I need is when I insert records into Table_2 from TType_Table_A say with below statements, I would like to capture the Id of Table_2 for each record inserted
declare #count int = (select count(*) from #TType_Table_A); --a variable declared for TType_Table_A
if(#count > 0)
begin
insert into Table_2(Table_A_Id,Table_1_Id)
SELECT #SomeValue, #SomeValueAsParameter FROM #TType_Table_A;
end;
Now say if 2 records are inserted, I would like to capture the Id for each of these 2 records.
Any input/help is appreciated
This is what I know how it can be achieved, but I want to reduce DB calls from my app or user cursor in stored procedure
Insert record in Table_1 and return back the Id Loop.....through records and insert record in Table_2 and return back the Id
OR
Use cursor in stored procedure when inserting/selecting from TableType
I assume this is Sql Server? Then you can make use of the OUTPUT clause, like so:
declare #NewId table (MyNewId INT)
insert into Table_2(Table_A_Id,Table_1_Id)
output INSERTED.MyNewId INTO #TempTable(MyNewID)
SELECT SomeValue, SomeValueAsParameter FROM #TType_Table_A;
SELECT * FROM #NewId