SQL Server trigger - get variable from the first Insert stored procedure? - sql-server

I have an insert stored procedure with incremented Id (int). I'll like to take that Id and insert it to another table and also one of variable from the insert stored procedure.
I know you can do select from inserted but if I'm not mistaking that allowed you to select values from the inserted row.
Is there anyway to persevere the variable after executed the stored procedure and pass it to trigger?
Edit: sorry, let me included the stored procedure that I'm using. Table foo have a Id that is auto incremented and I want a trigger to insert the foo's Id and the #mid to another table after this stored procedure is run. However as you can see that #mid is not being insert to table foo.
ALTER PROCEDURE [dbo].[foo]
#userName varchar(50),
#somefoo varbinary(max),
#mid int
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO fooTable(UserName, SomeFoo)
VALUES (#userName, #somefoo);
END

The inserted pseudo-table allows you to select the value that is now in the table. The deleted pseudo-table allows you to select the value that used to be in the table before the insert happened. Since you say insert proc i am assuming it's not an insert/update proc. I am not clear on what you want to do. You want the trigger to copy the identity field and one other column? Easy. You want the trigger to copy the identity field and a proc parameter that is not actually stored in the table? Impossible - but if that's what you want, then you can do it in the proc instead.
1) Trigger:
Insert into foo (a, b)
select my_identity, some_column
from inserted
2) Proc:
..do main insert...
insert into foo (a, b)
select scope_identity(), #some_parameter

You can use your table extended property for your purpose.
step 1 : define a extended property for your table.
step 2 : in stored procedure before insert into your table set defined extended property by your parameter.
EXEC sys.sp_updateextendedproperty #name=N'#ParameterName', #value='YourValue'
step 3 : in trigger get defined extended property and use it.
use sys.extended_properties table for get defined extended property.

Related

Create / Alter procedure referencing non-existing table or view

According to MSDN:
A procedure can reference tables that do not yet exist. At creation
time, only syntax checking is performed. The procedure is not compiled
until it is executed for the first time. Only during compilation are
all objects referenced in the procedure resolved. Therefore, a
syntactically correct procedure that references tables that do not
exist can be created successfully; however, the procedure fails at
execution time if the referenced tables do not exist.
I was always under the impression that referenced tables are checked. For example if you reference an incorrect column in an existing view it complains at compile time:
So what is going on here, why are columns checked within the procedure but not views / tables?
The document is referencing that, at the time to Stored Procedure is created, it isn't validated. Take this batch:
USE Sandbox;
GO
CREATE PROC dbo.MyProc #ID int AS
BEGIN
SELECT *
FROM dbo.MyTable
WHERE ID = #ID;
END;
GO
CREATE TABLE dbo.MyTable (ID int,
SomeValue varchar(20));
INSERT INTO dbo.MyTable
VALUES(1,'asdjka'),
(2,'asdkj');
GO
EXEC dbo.MyProc 1;
GO
DROP TABLE dbo.MyTable;
DROP PROC dbo.MyProc;
Notice that I CREATE dbo.MyProc before dbo.MyTable, even though it references that object. That's because the validity of the objects in the procedure isn't checked at the point the procedure is created or altered.
A VIEW on the other hand, is checked at the time that it is created.
CREATE TABLE dbo.MyTable (ID int,
SomeValue varchar(20));
INSERT INTO dbo.MyTable
VALUES(1,'asdjka'),
(2,'asdkj');
GO
--Fails
CREATE VIEW dbo.MyView AS
SELECT ID,
SomeValue,
SomeInt
FROM dbo.MyTable;
GO
--Fails
CREATE VIEW dbo.MyOtherView AS
SELECT *
FROM dbo.MyOtherTable;
GO
DROP TABLE dbo.MyTable;
Depending on the object type depends on what is validated and isn't when the DDL statement is issued.
Edit: It seems that what the OP is questioning is why do that get an error if the object does exist, but they reference a column that doesn't. For exmaple take the below batch:
USE Sandbox;
GO
CREATE TABLE dbo.MyTable (ID int,
SomeValue varchar(20));
INSERT INTO dbo.MyTable
VALUES(1,'asdjka'),
(2,'asdkj');
GO
CREATE PROC dbo.MyProc #ID int AS
BEGIN
SELECT ID,
SomeValue,
AnotherValue
FROM dbo.MyTable
WHERE ID = #ID;
END;
GO
DROP TABLE dbo.MyTable;
GO
DROP PROC dbo.MyProc;
GO
This fails as the column AnotherValue does not exist. This is actually covered in the very documentation you quote:
A procedure can reference tables that do not yet exist. At creation time, only syntax checking is performed.
It explicitly states you can reference a table that does not exist. It makes no mention of objects that don't, or (more specifically) columns in a table/object. Referencing a table that does exist will be validated at creation time.

SQL Insert along with matching audit at same time from app

I've got an app that will insert lines to table [P_R], which has first field being [PrimaryKey].
Now I have to add another table, [Actions] with fields [PrimaryKey],[P_R_PK],[User],[ActionTime].
When the app inserts a line to [P_R], I don't know what the PrimaryKey will be, but I have to simultaneously insert to [Actions] with the value in [P_R_PK] being the PrimaryKey I just added to [P_R]. How do I get this PrimaryKey value to [P_R_PK]?
For reference, I'm using a vb.net windows form with a SQL Server database.
If you're using a stored procedure to add the records to [P_R], you can call another stored procedure in the first that includes the primary key. For example:
CREATE PROC AddToP_R
#field1 varchar(10),
#field2...10
AS
BEGIN
declare #pk int --primary key that's created upon inserting
--insert into [P_R]
INSERT INTO [P_R]
VALUES (#field1,...)
--set the var we created to be the primary key
SET #pk = SCOPE_IDENTITY()
--call second proc
EXEC Second_Proc #pk
END
If you need other fields in the second stored procedure, include them in the first procedure parameter list.
Another way would be to a wrapper stored procedure that calls both the other two. For this to work, you would need an output variable in the first procedure to return the primary key. For example:
CREATE PROC AddWrapper
#fieldsforfirstproc...,
#fieldsforsecondproc...
AS
BEGIN
declare #outputVar int --primary key
EXEC firstproc #fieldsforfirstproc..., #outputvar output --adds the record to the first table and returns #outputvar as the primary key
EXEC secondproc #fieldsforsecondproc..., #outputvar --adds the record to the second table using #output var
END
I prefer the second option because it removes logic from the first procedure that doesn't need to be there. However, the first procedure would be slightly different to how I showed earlier.
CREATE PROC AddToP_R
#field1 varchar(10),
#field2...10,
#pk int OUTPUT --primary key that's created upon inserting
AS
BEGIN
--insert into [P_R]
INSERT INTO [P_R]
VALUES (#field1,...)
--set the var we created to be the primary key
SET #pk = SCOPE_IDENTITY()
END
You can retrieve it by using SELECT SCOPE_IDENTITY() after the INSERT.
For example:
DECLARE #T table (id int PRIMARY KEY IDENTITY, value int)
INSERT INTO #T (value) VALUES (2)
SELECT SCOPE_IDENTITY() new_pk
I would also consider doing it all in one stored procedure within a transaction. Given that you are inserting into more than one table, a transaction would allow you to roll back should anything go wrong.

Using Same Stored Procedure for Inserting List of Values and Query Results

I have a stored procedure to insert a row, with a list of values which came as input parameters, into a table (tmp).
This table mirrors another table (data), and I'm looking for a way to use the same SP to insert a row from data into tmp.
I've seen Pass result of a query into stored procedure, but I can't put INSERT in a UDF.
Is there an easy way, not requiring
a query which puts all the values into variables and sends the variables?
a table-typing or other additions to our already-too-full database
Edit The desired result would be a way to write something like
EXEC sp_insert (SELECT c1,c2,c3 FROM data)
Maybe you can use variable table as the parameter for the SP (search for this)
It needs to define user defined table type , but I'm using this method.
Table used as a parameter can get values from inserting or selecting from other table.
alter procedure sp_insert
#table your_table_type readonly
AS
BEGIN
--here you take a row (maybe the only one) from #table and insert it to TMP
END

How to get a table identity inserted by instead of insert trigger?

I have a problem described as follows: I have a table with one instead of insert trigger:
create table TMessage (ID int identity(1,1), dscp varchar(50))
GO
Alter trigger tr_tmessage on tmessage
instead of insert
as
--Set NoCount On
insert into tmessage
select dscp from inserted
GO
Alter proc P1
As
--Set NoCount On
insert into tmessage
(dscp)
values('some data')
Select SCOPE_IDENTITY()
GO
When I execute P1 it returns Null for SCOPE_IDENTITY() instead of the identity of the table. I even tried Output clause in the insert statement in the proc. but again the output table Identity field that gets filled from inserted in the Output clause is 0 in this case.
Any help would be appreciated.
Well, you've got yourself quite a pickle there.
From the one hand, you need the instead of insert trigger on your table, but from the other hand, you want to get the identity that this trigger generates back to the stored procedure that activated it.
Since there is no way to send parameters to and from triggers, you will have to do one of 3 things:
Find some way to eliminate the need for that instead of trigger.
This is my best recommendation.
Break your stored procedure to 2 parts: One part will do everything until the insert into statement (including it), thus activating the instead of insert trigger, and the other part that will do all operations needed after the trigger. this way you can use the scope_identity() inside the instead of insert trigger and send it's return value to the second stored procedure as a parameter.
Note: this design means you have to insert records one by one. should you try to insert more then one record, scope_identity() will only return the identity of the last row inserted by the trigger.
Find some way of passing data between the stored procedure and the instead of trigger. Since triggers can't except or return parameters, you will have to use either a temporary table or a regular table. This suggested solution is only suggested as a last resort, since it will complicate your code and probably cause some performance issues as well. Also, you will have to find a way to hold execution of the stored procedure until the instead of trigger will finish it's work. I can give you some pointers on how to share data between the procedure and the trigger, but I really suggest not to choose this solution.
SCOPE_IDENTITY() return the value from current scope and that is stored procedure outside from stored procedure it will be null.use ##identity to get last inserted identity.

insert return data from SP to temp table

I have a stored proc, SP1, which executes and does select on one table. Now i have the need to insert that data in another table. I dont want to duplicate the code so i thought of inserting the data returned by the SP1 in the temp table so i can do some processing on it and save it.
I tried INSERT INTO #tmp; exec dbo.Sp1; but it gives me an error saying Invalid object name '#tmp'.. Isnt there a way i can create this table dynamically? Is there a better solution to this problem?
The temp table has to exist before you can use insert into exec.
This is not such a draw back as it first seems as any changes to the procedure result set will likely brake your code.
first run this:
create proc MySelect
as
begin
select 1 as myColumn1, 2 as mycolumn2
end
and then this:
create table #tmp(
col_1 int,
col_2 int)
insert into #tmp exec MySelect
select * from #tmp

Resources