List of foreign keys in an insert SQL Server - sql

I am trying to set up a stored proc that will have three variables
FK_List
String_of_Info
CreateId
I need to insert into the table one entry per foreign key from the FK_List. I was curious what the best way to structure the stored procedure to do this efficiently.
EDIT: Code snippet added
CREATE PROCEDURE StackOverFlowExample_BulkAdd
#FKList VARCHAR(MAX),
#Notes NVARCHAR(1000),
#CreateId VARCHAR(50)
AS
BEGIN
INSERT INTO [dbo].[StackOverflowTable] WITH (ROWLOCK)
([FKID], [Notes], [CreateId], [UpdateId])
VALUES (#FKList, <---- this is the problem spot
#Notes, #CreateId, #CreateId)
END
GO

Based off your comments, you simply need a slight edit
CREATE PROCEDURE StackOverFlowExample_BulkAdd
#Notes nvarchar(1000),
#CreateId varchar(50)
AS
BEGIN
INSERT INTO [dbo].[StackOverflowTable] WITH (ROWLOCK)
([FKID]
,[Notes]
,[CreateId]
,[UpdateId])
select
someID
,#Notes
,#CreateId
,#CreateId
from FKListTable
END
GO
Here is a simple demo
This will insert a row into your table for each FK reference in the reference table with the parameters you pass in. That's all there is to it!
Here's another demo that may be more clear as I use a GUID for the primary key on the secondary table.
SECOND EDIT
Based off your comments, you will need a string splitter. I have added a common one which was created by Jeff Moden. See the example here
The final proc, after you create the function, will be like below. You need to change the comma in the function to what ever the delimiter is for your application. Also, you should start using table valued parameters.
CREATE PROCEDURE StackOverFlowExample_BulkAdd
#FKList VARCHAR(MAX),
#Notes nvarchar(1000),
#CreateId varchar(50)
AS
BEGIN
INSERT INTO [dbo].[StackOverflowTable] WITH (ROWLOCK)
([FKID]
,[Notes]
,[CreateId]
,[UpdateId])
select item
,#Notes
,#CreateId
,#CreateId
from dbo.DelimitedSplit8K(#FKList,',')
END
And you can call it like so:
declare #FKList varchar(1000) = '1,2,3,4,5,6'
declare #Notes varchar(1000) = 'here is my note'
declare #CreatedId int = 1
exec StackOverFlowExample_BulkAdd #FKList, #Notes, #CreatedId

Related

Set a local variable and use throughout the query batch?

Once I insert default values in the table I store the result of scope identity in a variable
insert into OrderPlaced default values;
declare #id bigint;
set #id = SCOPE_IDENTITY();
After this, I have to run some other pieces of code that change the value of scope identity and after running those pieces of code I have to use the value of #id again but it shows an error saying that I must declare the variable which I have already done above.
EXEC dbo.GetRecieptById #ID = #id;
Unfortunately, I can't just select the whole code block and execute it at once as this is for a presentation and I have to show each individual steps.
Your request is how to persist the variable across batches - not within a batch.
One way would be to use SESSION_CONTEXT
declare #id bigint;
insert into OrderPlaced default values;
set #id = SCOPE_IDENTITY();
EXEC sys.sp_set_session_context #key= N'#id',#value = #id
GO
declare #id bigint = CAST(SESSION_CONTEXT(N'#id') AS BIGINT)
EXEC dbo.GetRecieptById #ID = #id;
What you wanna do ?
If you want to access the latest data in your Order Table, you can access the latest data with this code.
SELECT MAX(ID) FROM OrderPlaced
The local variable can not be used in a separate execution. You have to store all values in a temporary table.
These tables are stored in tempdb. Use local temporary table with one # or global temporary table with two ## at the beginning of the table name as follow:
create table #local_temp_table
(Id bigint not null);
...
insert into #local_temp_table ...
...
select Id from #local_temp_table;
OR
create table ##global_temp_table
(Id bigint not null);
...
insert into ##global_temp_table ...
...
select Id from ##global_temp_table;
They are automatically dropped when they go out of scope, however, you can drop them manually.
Take a look at the following link:
Temporary Tables in SQL Server

I want to write the code I created with the 'Stored procedure' as a function

CREATE PROC add_person
(
#id tinyint,
#name nvarchar(max),
#surname nvarchar(max),
#salary int,
#job nvarchar(max)
)
AS
BEGIN
INSERT INTO information
VALUES(#id,#name,#surname,#salary,#job)
END
I want to write this code as a function. But the concept of "return" confuses me. That's why I couldn't.
I tried to write the code above as a function. This code came out.
CREATE FUNCTION add_person
(
#id tinyint,
#name nvarchar(max),
#surname nvarchar(max),
#salary int,
#job nvarchar(max)
)
RETURNS TABLE
AS
BEGIN
RETURN INSERT INTO information -- not work
VALUES(#id,#name,#surname,#salary,#job)
END
If you want to return the newly created table, you can use the stored procedure to do that. If you're using SQL Server, the code would be:
BEGIN
INSERT INTO information -- not work
VALUES(#id,#name,#surname,#salary,#job);
SELECT * FROM information WHERE id = ##identity; -- this is the primary key just created.
END
Functions are much more limited in their functionality than are stored procedures.
Although insert is allowed, it is only allowed in local variables. As the documentation says:
INSERT, UPDATE, and DELETE statements modifying local table variables.
On the other hand, a stored procedure can return a value. Normally, this is a status code, where 0 means everything succeeded, and any other value means that the process failed.

Insert / update Deadlock with SQL Server

I have a table A (id int, domain nvarchar, status nvarchar) and a trigger A_trigger after insert on table A. The trigger calls a stored procedure and depending on the result of the procedure, updates the status on the newly inserted row.
When I run it in two sessions, I end up with a deadlock issue:
Isolation level: read committed
INSERT statement:
INSERT INTO dbo.TEST_TRIGGER (DOMAIN)
VALUES ('toto')
Trigger:
CREATE TRIGGER dbo.dim_trigger
ON db.dbo.TEST_TRIGGER
AFTER INSERT
AS
DECLARE #status nvarchar(200),
#domain nvarchar(200),
#trackingId int
BEGIN
SET NOCOUNT ON;
-- This code assumes we insert one and only one row at a time.
SELECT #trackingId = id, #domain = domain FROM INSERTED;
DECLARE #toCallProcName NVARCHAR(200);
SET #toCallProcName = 'db.dbo.'+#domain+'_proc';
EXEC #toCallProcName #status out;
UPDATE db.dbo.TEST_TRIGGER
SET status = #status
WHERE id = #trackingId;
END
I tried to:
issue the update statement with WITH (UPDLOCK), but that's not working
Creating an index on (ID) works, but I'm concerned about this solution!!
EDIT1:
Table schema:
CREATE TABLE [dbo].[TEST_TRIGGER]
(
[DOMAIN] [NVARCHAR](200) NOT NULL,
[ID] [BIGINT] IDENTITY(1,1) NOT NULL,
[STATUS] [NVARCHAR](100) NULL
)
Stored procedure:
CREATE PROCEDURE [dbo].[toto_proc]
#res NVARCHAR(200) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
WAITFOR DELAY '00:00:5'
PRINT 'This is me: '+CONVERT(VARCHAR(8), GETDATE(), 108) ;
SET #res = 'OK'
END
Any help?
Thanks
Reason for your issue is, Triggers keep holding the lock on the base table. Due to multiple session and same resource this is bound to happen. Having no index on the table will cause table scan every time for each different session and cause deadlock. Applying the index is the right move, because in this way your update statement will hit the granular level. So this should work. I suggest an additional hint of RowLock in your update statement.
Important : I am assuming, each insert will have only one value to be inserted. Other wise this Trigger has issues. (As mentioned by marc_s)
What you need to do is to create an index on your table
example:
CREATE INDEX IX_Test_Trigger ON dbo.TEST_TRIGGER(Id)
Read this for more info about Index and its impact on locking mechanism:
https://www.mssqltips.com/sqlservertip/2517/using-a-clustered-index-to-solve-a-sql-server-deadlock-issue/

Trigger not calling for huge rows insert

I have one table which consists of one trigger which will be called if any insert or update operation performed on that table.
This trigger will insert a new row in other physical table.
First I am taking the entire data to be inserted into a temporary table and then I am inserting data into my physical table(which has trigger).
After performing insert operation all the records in the temporary table are getting inserted into physical table but the trigger is executing for only first record, for rest of the records it is not executing.
Can anyone please help me with this issue.
NOTE : With cursor it is working fine but for performance issue I don't want to use cursor.
ALTER TRIGGER [dbo].[MY_TRG]
ON [dbo].[T_EMP_DETAILS]
FOR INSERT , UPDATE
AS
BEGIN
IF UPDATE(S_EMPLOYEE_ID)OR UPDATE(S_GRADE_ID)OR UPDATE(D_EFFECTIVE_DATE) OR UPDATE(S_EMPLOYEE_STATUS)
BEGIN
DECLARE #EmpId varchar(6)
DECLARE #HeaderId Int
DECLARE #FYStartYear varchar(4)
DECLARE #EffDate Smalldatetime
DECLARE #UpdatedBy varchar(10)
DECLARE #ActionType varchar(1)
DECLARE #RowCount Int
DECLARE #EmpRowCount Int
DECLARE #AuditRowsCount Int
DECLARE #EMP_STATUS VARCHAR(1)
DECLARE #D_FIN_START_YEAR DATETIME
DECLARE #Food_Count int
SELECT #FYStartYear = CAST(YEAR(D_CURRENT_FY_ST_DATE)AS VARCHAR) FROM dbo.APPLICATION WHERE B_IS_CURRENT_FY = 1
SELECT #UpdatedBy = 'SHARDUL'
select #EmpId = S_EMPLOYEE_ID from inserted
select #HeaderId = N_HEADER_TXN_ID from inserted
select #EffDate = D_EFFECTIVE_DATE from inserted
select #FLEXI_AMT = N_FLEX_BASKET_AMT from inserted
select #EMP_STATUS = S_EMPLOYEE_STATUS from inserted
select #D_FIN_START_YEAR=D_FIN_START_DATE from inserted
SELECT #RowCount = count(*) from T_EMP_DETAILS
WHERE S_EMPLOYEE_ID = #EmpId and
SUBSTRING(CAST(D_EFFECTIVE_DATE AS VARCHAR),1,11) = SUBSTRING(CAST(#EffDate AS VARCHAR),1,11)
BEGIN
exec INSERT_DEFAULT_VALUES #EmpId,#HeaderId,#UpdatedBy
END
That's one of many reasons Bulk is so fast :). Read Bulk Insert syntax and you'll see FIRE_TRIGGERS parameter. Use it.
As I wrote in my comment - you are using inserted in improper way. As written now it will work only for 1 row.
The second one is a WEIRD number of variables, and only few are used, why?
Third - you are using SP in the end of batch, you need to post it's code, I bet there is some insert in it, maybe you could avoid using this SP and insert directly in some table from inserted.

problem with raiseerror()

what I want to do is to create a stored procedure that executes insert statement.There is a possibility the execution to fail because of a check constraint for the table Employee.In that case I want to handle a user-defined error.Obviously the following procedure is not working properly because it always raises my error,but not only when insertion fails.
EXEC sp_addmessage 50001, 16, N'Title must be one of the following - Captain,Engineer,Flight-attendant,Purser,First-officer';
CREATE PROCEDURE InsertIntoEmployee
#firstName nvarchar(30),
#familyName nvarchar(30),
#title nvarchar(50),
#address nvarchar(50),
#chiefID int ,
#salary money ,
#FK_IDCrew int,
#FK_DepartmentID int
AS
BEGIN
declare #err_num int;
declare #err_sev int;
declare #err_msg int;
begin try
insert into Employee(firstName, familyName, title, address, chiefID, salary, FK_IDCrew,
FK_DepartmentID)
values(#firstName, #familyName, #title, #address, #chiefID, #salary, #FK_IDCrew,
#FK_DepartmentID);
raiserror(50001,16,1);
END try
begin catch
set #err_num=ERROR_NUMBER();
set #err_sev=ERROR_SEVERITY();
set #err_msg=ERROR_STATE();
raiserror(#err_num,#err_sev,#err_msg);
end catch
end
GO
In this case:
Title should be a lookup to another table and a foreign key
In the CATCH block you can trap the FK constraint violation separately if you want...
...but you'd only allow rows from the new table in your client so I wouldn't personally
No need for a sys.messages entry
Your code will also always hit the RAISERROR too which doesn't add any value,
I hope that the dimensions mentioned in the parameter list is sycn with table columns length.
Before insertion, You should check take care of following points.
Check the existence of #FK_IDCrew value in it's table.
Check the existence of #FK_DepartmentID value in it's table.
It should be like below.
If Not Exists(Select IDCrewColumnName From Table Where columnName = #FK_IDCrew)
Begin
return here from the stored procedure.
End
In case any of them fails to meet the conditions, you should show some user friendly message to user that
(a) Crew ID, you are going to insert, either deleted or does not exists in the database.
(b) DepartmentID, you are going to insert, either deleted or does not exists in the database.
In this way the probability of error will also come to an end.