Save Exec results in variable - sql

I already have a stored procedure GET_ROW that is doing a select, according to input ID:
SELECT TOP 1 *
FROM MyTable
WHERE ID = #ID
Now, I want to create another stored procedure that checks if an #ID exists. If it exists, return the existing row. Otherwise, create a new row with the requested #ID and return it.
So, I'm thinking of something like this:
Declare ResRow
begin tran
ResRow = Exec GET_ROW #ID
if exists (ResRow)
return ResRow
else
Insert into ...
return Exec GET_ROW #ID
commit
After executing this code, I want to be sure that only one row with #ID exists in the database (no duplicated row with same ID)

You must create table variable in first and then use following query:
Declare #T Table(Col1 int, Col2 int ,...)
Insert Into #T
Exec Get_Row #ID
Select * From #T

How about this:
CREATE PROCEDURE dbo.InsertOrFetch #ID INT
AS
BEGIN
-- check if it doesn't exist yet
IF NOT EXISTS (SELECT * FROM dbo.MyTable WHERE ID = #ID)
INSERT INTO dbo.MyTable(ID)
VALUES (#ID)
-- now return the row
SELECT TOP (1) *
FROM dbo.MyTable
WHERE ID = #ID
END
If you first check if the row doesn't exist it, and if so, insert the new data - then the SELECT afterwards will always return the row (pre-existing or newly inserted) to you.

Related

Alternative to Iteration for INSERT SELECT UPDATE in a sequence

I have a table with around 17k unique rows for which I need to run these set of statements in sequence
INSERT INTO TABLE1 using MASTERTABLE data (MASTERTABLE have 6 column)
SELECT value of column ID (Primary Key) of newly inserted row from TABLE1
Update that ID value in TABLE2 using a Stored Procedure
I have tried:
while loop: took around 3 hours to complete the execution
cursor: cancelled the query after executing it overnight
In my understanding I can not use JOIN as I need to execute the statements in a sequence
The questions is not detailed enough. The general idea I would like to use something like this
-- create a output table to hold new id, and key columns to join later
DECLARE #OutputTbl TABLE (ID INT, key_Columns in MASTERTABLE)
INSERT INTO TABLE1
OUTPUT INSERTED.ID, MASTERTABLE.key_columns INTO #OutputTbl
SELECT *
FROM MASTERTABLE
UPDATE T2
SET ID = o.ID
FROM TABLE2 t2
INNER JOIN OutputTbl o
ON t2.key_column = o.key_column
Maybe you can consider a TRIGGER on TABLE1 from which to call the stored procedure on TABLE2, and then you can call your INSERT as you wish/need.. one by one or in blocks..
DROP TRIGGER TR_UPD_TABLE2
GO
CREATE TRIGGER TR_UPD_TABLE2 ON TABLE1 AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
DECLARE #columnID INT = NULL
IF (SELECT COUNT(*) FROM INSERTED)=1 BEGIN
-- SINGLE INSERT
SET #columnID = (SELECT columnID FROM INSERTED)
EXEC TableTwoUpdateProcedure #columnID
END ELSE BEGIN
-- MASSIVE INSERT (IF NEEDED)
SET #columnID = 0
WHILE #columnID IS NOT NULL BEGIN
SET #columnID = (SELECT MIN(columnID) FROM INSERTED WHERE columnID > #columnID)
IF #columnID IS NOT NULL BEGIN
EXEC TableTwoUpdateProcedure #columnID
END
END
END
END

Insert Query to insert multiple rows in a table via select and output clause. SQL Server 2008

I have a created a stored procedure (please ignore syntax errors)
alter proc usp_newServerDetails
(#appid int, #envid int, #serType varchar(20), #servName varchar(20))
as
declare #oTbl_sd table (ID int)
declare #outID1
declare #oTbl_cd table (ID int)
declare #outID2
begin Transaction
insert into server_details(envid, servertype, servername)
output inserted.serverid into #oTbl_sd(ID)
values(#envid, #serType, #servName)
select #outID1 = ID from #oTbl_sd
insert into configdetails(serverid, servertype, configpath, configtype)
output inserted.configid into #oTbl_cd(ID)
(select #outID1, cm.servertype, cm.configpath, cm.configtype
from configpthmaster cm
where cm.appid = #appid )
select #outID2 = ID from #oTbl_cd
insert into configkeydetails(confiid, keyname)
output inserted.Keyid into #oTbl_ckd(ID)
(select #outID2, cm.key
from configpthmaster cm
where cm.appid = #appid)
begin
commit
end
server_details table has an identity column ID with is auto-generated ie. #outID1 and first insert query inserts only 1 row.
configpthmaster table is not related to any other table directly and has 2 unique data rows, which I want to fetch to insert data into other tables, one by one during insertion.
The second insert query fetch data from configpthmaster table
and insert 2 rows in configdetails while generating (auto-generated) ID ie. #outID2.
It also has a FK mapped to server_details.
The problem is "#outID2" giving last inserted ID only (ie. if two id generated 100,101 i am getting 101) which eventually on 3rd insertion, inserting 2 rows with same id 101 only but i want the insertion should be linear. i.e one for 100 and other for 101.
If zero rows affected while insertion how to rollback the transaction?
How can I achieve these requirements? Please help.
Change your procedure like below,and try again.
ALTER PROCEDURE usp_newServerDetails(#appid int, #envid int,#serType varchar(20),#servName varchar(20))
AS
BEGIN
BEGIN TRY
DECLARE #Output TABLE (ID int,TableName VARCHAR(50),cmKey VARCHAR(50)) --table variable for keeping Inserted ID's
BEGIN TRAN
IF EXISTS ( SELECT 1 FROM configpthmaster cm WHERE cm.appid = #appid )
AND ( SELECT 1 FROM configkeydetails ck WHERE ck.appid = #appid ) --add a conditon to satisfy the valid insertions
BEGIN
INSERT INTO server_detials(envid,servertype,servername)
OUTPUT inserted.serverid,'server_detials',NULL INTO #Output(ID,TableName,cmKey )
VALUES(#envid ,#serType ,#servName)
INSERT INTO configdetails(serverid,servertype,configpath,configtype)
OUTPUT inserted.configid,'configdetails',cm.Key INTO #Output(ID,TableName,cmKey )
SELECT t.ID,cm.servertype,cm.configpath,cm.configtype
FROM configpthmaster cm
CROSS APPLY (SELECT ID FROM #Output WHERE TableName='server_detials')t
WHERE cm.appid = #appid
INSERT INTO configkeydetails(configId,keyname)
SELECT ID,cmKey FROM #Output
WHERE TableName='configdetails'
END
COMMIT TRAN
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK
END CATCH
END
Could you try this solution?
alter proc usp_newServerDetails(#appid int, #envid int,#serType varchar(20),#servName varchar(20))
as
declare #oTbl_sd table (ID int)
declare #outID1
declare #oTbl_cd table (ID int)
declare #outID2
begin Transaction
insert into server_detials(envid,servertype,servername)
output inserted.serverid into #oTbl_sd(ID)
values(#envid ,#serType ,#servName)
select #outID1 = ID from #oTbl_sd
insert into configdetails(serverid,servertype,configpath,configtype)
output inserted.configid into #oTbl_cd(ID)
(select #outID1 ,cm.servertype,cm.configpath,cm.configtype from configpthmaster cm where cm.appid = #appid )
select #outID2 = ID from #oTbl_cd
insert into configkeydetails(confiid,keyname)
output inserted.Keyid into #oTbl_ckd(ID)
(select isnull(replace(stuff((SELECT inserted.configid FOR xml path('')), 1, 1, ''), '&', '&'), '') ,cm.key, from configpthmaster cm where cm.appid = #appid )
begin
commit
end
I just added STUFF in your code.
The STUFF function inserts a string into another string.
Do take note that using STUFF drastically slows the processing time of the code.
for more information about STUFF

not able to get first inserted rows identity in cursor loop

I have been trying inserting some rows into a temporary table which required identity from table A ...this A table is being inserted from a cursor..
everything is working fine here but only the problem i am getting is that i don't get identity of first inserted row in table A
suppose i insert 3 rows in table A ...i get last 2 rows in temporary table ..not first one..and if i send just one row in table A then temporary table is blank
here is my stored procedure ..table A is tblClients
Create PROCEDURE [dbo].[lsp1_propAdmClnt]
(
#usrprflId bigint,
#preClient tpClient readonly
)
as
declare #err int
CREATE TABLE #tblids(
clntid int,
imgname nvarchar(350)
)
declare #clntid as int
declare #clntname nvarchar(300)
declare #imgname nvarchar(300)
Begin Transaction
declare transfclntid cursor for select clntname, [imgname] from #preClient
open transfclntid
fetch next from transfclntid into #clntname, #imgname
while ##fetch_status=0
begin
insert into tblClients (usrprflId,Name,Img,cdate)
values(#usrprflId, #clntname, #imgname,GETDATE())
SET #clntid = (SELECT SCOPE_IDENTITY())
insert into #tblids (clntid, imgname) values (#clntid, #imgname)
fetch next from transfclntid into #clntname, #imgname
end
close transfclntid
deallocate transfclntid
select * from #tblids
select #err=##TOTAL_ERRORS
if(#err<>0)
Begin
Rollback Transaction
return 0
End
Commit transaction
First, you should replace that cursor with following INSERT SELECT statement:
declare #tblids table
(
clntid INT NOT NULL,
imgname NVARCHAR(350)
);
insert into tblClients (usrprflId, Name, Img, cdate)
output inserted.clntid, inserted.Img into #tblids (clntid, imgname)
select #usrprflId, clntname, [imgname], GETDATE()
from #preClient
Then if you need the first identity value you could use:
SELECT MIN(clntid)
FROM #tblids

SQL - Getting Updated Value

I use Sql Server 2008.
I have a table that generates ID.
I want to retrieve the generated ID and store it in a bigint variable.
How can I do it?
Here is the Stored Proc that gives the ID as result set. But I cannot store it in a bigint variable.
ALTER PROC SCN.TRANSACTION_UNIQUE_ID_SELECT
AS
UPDATE COR.TRANSACTION_UNIQUE_ID
SET ID = ID + 1
OUTPUT INSERTED.ID AS ID
If you want to use output you can;
declare #ID table (ID bigint)
update the_table
set ID = ID + 1
output INSERTED.ID into #ID
declare #bi bigint = (select ID from #ID)
The BigInt should be an identity insert column, this will make SQL Server automatically generate bigints in sequence for you. Just pass the rowID as an OUTPUT parameter and set it before the procedure ends after the insert/update.
Then you can read it coming back and set it as needed.
The stored procedure could look something like this (I've only included the rowID for clarity):
CREATE PROCEDURE [Sample].[Save]
(
#rowID bigint OUTPUT
)
AS
BEGIN
SET NOCOUNT ON
--Do your insert/update here
--Set the RowID
SET #rowID = (SELECT SCOPE_IDENTITY())
END
That UPDATE looks fishy...it will increment every ID in your table by one, no?
Anyway, variables have an #sign, so just SET #myvar = ...whatever...
Use OUTPUT INTO:
DECLARE #TblID TABLE ( ID int )
UPDATE COR.TRANSACTION_UNIQUE_ID
SET ID = ID + 1
OUTPUT INSERTED.ID INTO #TblID (ID) --the output values will be inserted
--into #TblID table-variable
DECLARE #id BIGINT
EXEC #id = SCN.TRANSACTION_UNIQUE_ID_SELECT

IF EXISTS, THEN SELECT ELSE INSERT AND THEN SELECT

How do you say the following in Microsoft SQL Server 2005:
IF EXISTS (SELECT * FROM Table WHERE FieldValue='') THEN
SELECT TableID FROM Table WHERE FieldValue=''
ELSE
INSERT INTO TABLE(FieldValue) VALUES('')
SELECT TableID FROM Table WHERE TableID=SCOPE_IDENTITY()
END IF
What I'm trying to do is to see if there is a blank fieldvalue already, and if there is then return that TableID, else insert a blank fieldvalue and return the corresponding primary key.
You need to do this in transaction to ensure two simultaneous clients won't insert same fieldValue twice:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
DECLARE #id AS INT
SELECT #id = tableId FROM table WHERE fieldValue=#newValue
IF #id IS NULL
BEGIN
INSERT INTO table (fieldValue) VALUES (#newValue)
SELECT #id = SCOPE_IDENTITY()
END
SELECT #id
COMMIT TRANSACTION
you can also use Double-checked locking to reduce locking overhead
DECLARE #id AS INT
SELECT #id = tableID FROM table (NOLOCK) WHERE fieldValue=#newValue
IF #id IS NULL
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT #id = tableID FROM table WHERE fieldValue=#newValue
IF #id IS NULL
BEGIN
INSERT INTO table (fieldValue) VALUES (#newValue)
SELECT #id = SCOPE_IDENTITY()
END
COMMIT TRANSACTION
END
SELECT #id
As for why ISOLATION LEVEL SERIALIZABLE is necessary, when you are inside a serializable transaction, the first SELECT that hits the table creates a range lock covering the place where the record should be, so nobody else can insert the same record until this transaction ends.
Without ISOLATION LEVEL SERIALIZABLE, the default isolation level (READ COMMITTED) would not lock the table at read time, so between SELECT and UPDATE, somebody would still be able to insert. Transactions with READ COMMITTED isolation level do not cause SELECT to lock. Transactions with REPEATABLE READS lock the record (if found) but not the gap.
IF EXISTS (SELECT 1 FROM Table WHERE FieldValue='')
BEGIN
SELECT TableID FROM Table WHERE FieldValue=''
END
ELSE
BEGIN
INSERT INTO TABLE(FieldValue) VALUES('')
SELECT SCOPE_IDENTITY() AS TableID
END
See here for more information on IF ELSE
Note: written without a SQL Server install handy to double check this but I think it is correct
Also, I've changed the EXISTS bit to do SELECT 1 rather than SELECT * as you don't care what is returned within an EXISTS, as long as something is
I've also changed the SCOPE_IDENTITY() bit to return just the identity assuming that TableID is the identity column
You were close:
IF EXISTS (SELECT * FROM Table WHERE FieldValue='')
SELECT TableID FROM Table WHERE FieldValue=''
ELSE
BEGIN
INSERT INTO TABLE (FieldValue) VALUES ('')
SELECT TableID FROM Table WHERE TableID=SCOPE_IDENTITY()
END
You just have to change the structure of the if...else..endif somewhat:
if exists(select * from Table where FieldValue='') then begin
select TableID from Table where FieldValue=''
end else begin
insert into Table (FieldValue) values ('')
select TableID from Table where TableID = scope_identity()
end
You could also do:
if not exists(select * from Table where FieldValue='') then begin
insert into Table (FieldValue) values ('')
end
select TableID from Table where FieldValue=''
Or:
if exists(select * from Table where FieldValue='') then begin
select TableID from Table where FieldValue=''
end else begin
insert into Table (FieldValue) values ('')
select scope_identity() as TableID
end
It sounds like your table has no key. You should be able to simply try the INSERT: if it’s a duplicate then the key constraint will bite and the INSERT will fail. No worries: you just need to ensure the application doesn't see/ignores the error. When you say 'primary key' you presumably mean IDENTITY value. That's all very well but you also need a key constraint (e.g. UNIQUE) on your natural key.
Also, I wonder whether your procedure is doing too much. Consider having separate procedures for 'create' and 'read' actions respectively.
DECLARE #t1 TABLE (
TableID int IDENTITY,
FieldValue varchar(20)
)
--<< No empty string
IF EXISTS (
SELECT *
FROM #t1
WHERE FieldValue = ''
) BEGIN
SELECT TableID
FROM #t1
WHERE FieldValue=''
END
ELSE BEGIN
INSERT INTO #t1 (FieldValue) VALUES ('')
SELECT SCOPE_IDENTITY() AS TableID
END
--<< A record with an empty string already exists
IF EXISTS (
SELECT *
FROM #t1
WHERE FieldValue = ''
) BEGIN
SELECT TableID
FROM #t1
WHERE FieldValue=''
END
ELSE BEGIN
INSERT INTO #t1 (FieldValue) VALUES ('')
SELECT SCOPE_IDENTITY() AS TableID
END
create schema tableName authorization dbo
go
IF OBJECT_ID ('tableName.put_fieldValue', 'P' ) IS NOT NULL
drop proc tableName.put_fieldValue
go
create proc tableName.put_fieldValue(#fieldValue int) as
declare #tableid int = 0
select #tableid = tableid from table where fieldValue=''
if #tableid = 0 begin
insert into table(fieldValue) values('')
select #tableid = scope_identity()
end
return #tableid
go
declare #tablid int = 0
exec #tableid = tableName.put_fieldValue('')