Related
I am trying to inserting data from a table to another table. But I need using multiple specific IDs in where clause. And, I need to define these IDs while running the proc. The query is below,
CREATE PROCEDURE insert_hh
#id int
AS
BEGIN
SET IDENTITY_INSERT test_HH ON
INSERT INTO test_HH
(ID,Beneficiary_Name,Family_Members,
[M_50_yrs],
[F_50_yrs],
[M_50_18],
[F_50_18],
[M_18_15],
[F_18_15],
[M_15_5],
[F_15_5],
[M_5_2],
[F_5_2],
[M_2],
[F_2],
Beneficiary_Status,
Reason_of_Rejection)
SELECT ID,Beneficiary_Name,Family_Members,
[M_50_yrs],
[F_50_yrs],
[M_50_18],
[F_50_18],
[M_18_15],
[F_18_15],
[M_15_5],
[F_15_5],
[M_5_2],
[F_5_2],
[M_2],
[F_2],
Beneficiary_Status,
Reason_of_Rejection
FROM HH
WHERE ID IN ((#id))
SET IDENTITY_INSERT test_HH OFF
END
For now, I can insert only one ID for each time. Can you help me to insert multiple IDs in the same proc?
Thanks.
For multiple value change #Id parameter of stored procedure to VARCHAR with desired length.
CREATE PROCEDURE insert_hh
#id varchar(max)
AS
SET #Id = '1,10,20,30'
-- temp table
DECLARE #Id_tbl TABLE ( Id INT )
INSERT INTO #Id_tbl
SELECT
LTRIM(RTRIM(split.a.value('.', 'NVARCHAR(MAX)'))) AS fqdn
FROM
(SELECT
CAST ('<M>' + REPLACE(#Id, ',', '</M><M>') + '</M>' AS XML) AS data ) AS a
CROSS APPLY data.nodes ('/M') AS split(a)
-- INSERT STATEMENT
SELECT <Fields> FROM
<Your Table>
WHERE
ID IN (SELECT Id FROM #Id_tbl)
END
GO
CREATE PROCEDURE insert_hh
#id NVARCHAR(MAX)
AS
BEGIN
SET IDENTITY_INSERT test_HH ON
INSERT INTO test_HH
(ID,Beneficiary_Name,Family_Members,[M_50_yrs],[F_50_yrs],[M_50_18],[F_50_18],
[M_18_15],[F_18_15],[M_15_5],[F_15_5],[M_5_2],[F_5_2],[M_2],[F_2],
Beneficiary_Status,Reason_of_Rejection)
SELECT ID,Beneficiary_Name,Family_Members,[M_50_yrs],[F_50_yrs],[M_50_18],
[F_50_18],[M_18_15],[F_18_15],[M_15_5],[F_15_5],[M_5_2],[F_5_2],[M_2],[F_2],
Beneficiary_Status,Reason_of_Rejection
FROM HH
CROSS APPLY STRING_SPLIT(#id, ',')
WHERE HH.ID = value
SET IDENTITY_INSERT test_HH OFF
END
modelling fiddle
Pay attention - the parameter datatype is changed.
I wrote a SP which will call internally another parameterized SP and output will be store into a physical table.While I am executing Outer SP I am getting following error.
Msg 207, Level 16, State 1, Procedure CBs_LargeExposer, Line 88 [Batch
Start Line 12] Invalid column name 'SlNo'.
I observed, if I execute inner SP in separate window and very next to it if I execute the same outer SP is working fine, after certain time if I execute the same statement(Outer SP) I am getting same error.
ALTER PROCEDURE [dbo].[CBS_GlMapping]
#finYear nvarchar(30)='2019-2020',
#quarter char(5)='Q2',
#Oflag Varchar(6)='O4'
AS
BEGIN
SET NOCOUNT ON;
Declare #QtrStart date,#QtrSEnd date,#FyFrom int,#FyTo int,#BranchId int
select #FyFrom=year(YearBeginDate),#FyTo=Year(YEarEndDate) from BranchTable where BranchCode in(select OrgBankCode from OrgDetails)
IF(#FyFrom < left(#finYear,4) and #FyTo < Right(#finYear,4))
Begin
print 'Sorry, Recods are not available for the financial year ' + #finYear
return
End
If(#quarter='Q1')
Begin
select #QtrStart=YearBeginDate,#QtrSEnd=EOMONTH(DATEADD(MM,2,YearBeginDate)) from BranchTable where BranchCode in(select OrgBankCode from OrgDetails)
End
If(#quarter='Q2')
Begin
select #QtrStart=Dateadd(mm,3,YearBeginDate),#QtrSEnd=EOMONTH(DATEADD(MM,5,YearBeginDate)) from BranchTable where BranchCode in(select OrgBankCode from OrgDetails)
End
If(#quarter='Q3')
Begin
select #QtrStart=Dateadd(mm,6,YearBeginDate),#QtrSEnd=EOMONTH(DATEADD(MM,8,YearBeginDate)) from BranchTable where BranchCode in(select OrgBankCode from OrgDetails)
End
If(#quarter='Q4')
Begin
select #QtrStart=DATEADD(month, DATEDIFF(month, 0,Dateadd(mm,-2,YEarEndDate)), 0),#QtrSEnd=YEarEndDate from BranchTable where BranchCode in(select OrgBankCode from OrgDetails)
End
/* To handel the Financial year */
if(left(#finYear,4)<year(#QtrStart) and #quarter<>'Q4')
begin
set #QtrStart = cast(left(#finYear,4) as varchar(4))+'-'+cast(month(#QtrStart) as Varchar(2))+'-'+Cast(day(#QtrStart) as Varchar(2))
set #QtrSEnd = cast(left(#finYear,4) as varchar(4))+'-'+cast(month(#QtrSEnd) as Varchar(2))+'-'+Cast(day(#QtrSEnd) as Varchar(2))
end
if(right(#finYear,4)<year(#QtrStart) and #quarter='Q4')
begin
set #QtrStart = cast(right(#finYear,4) as varchar(4))+'-'+cast(month(#QtrStart) as Varchar(2))+'-'+Cast(day(#QtrStart) as Varchar(2))
set #QtrSEnd = cast(right(#finYear,4) as varchar(4))+'-'+cast(month(#QtrSEnd) as Varchar(2))+'-'+Cast(day(#QtrSEnd) as Varchar(2))
end
Create table #tempData
(
Bal numeric(15,2)
)
declare #sql varchar(500),#sql2 varchar(500),#day Varchar(10),#Month varchar(2),#Year varchar(4)
select #day= Day(#QtrSEnd)
select #Month= month(#QtrSEnd)
select #Year= Year(#QtrSEnd)
Create table #temp
( slno int,
glcode Varchar(500),
GlLen int,
ColNO varchar(50),
CellNo varchar(50),
Amount Numeric(15,2)
)
declare #tsql varchar(500)
set #tsql=N'
insert into #temp
select ROW_NUMBER() over(order by glcode) slno,glcode,LEN(Glcode) GlLen,ColNo,CellNo,amount
from BsGl'+#Oflag+'
where glcode <>'''' '
exec(#tsql)
declare #LoopStart int,#loopEnd Int,#glcode varchar(500),#amt numeric(15,0)
select #LoopStart=Min(Slno) from #temp
select #loopEnd=MAX(Slno) from #temp
while (#LoopStart <= #loopEnd)
begin
select #glcode= Glcode from #temp where slno = #LoopStart
set #sql='insert into #tempData select Sum(day'+#day+') from DayBal where AcYear='+#Year+' and acmonth='+#Month+'and GlCode in(SELECT glcode FROM dbo.splitstring(convert(varchar(500),'''+#glcode+''')))'
EXEC(#sql)
update #temp set Amount=(select Bal from #tempData) where slno =#LoopStart
set #LoopStart=#LoopStart+1
Truncate table #tempData
end
set #tsql=''
set #tsql='update BsGl'+#Oflag+' set Amount=0.00 where Glcode ='''' '
EXEC(#tsql)
set #tsql=''
set #tsql='update B set B.amount=isnull(round((Case when A.Amount<0 then A.Amount*(-1) else A.Amount end/1000),0),0) from #temp a inner join BsGl'+#Oflag+' B on a.CellNo=B.CellNo and B.ColNo=A.ColNo'
EXEC(#tsql)
IF(#Oflag='O4')
BEGIN
EXEC [DBO].[CBs_LargeExposer] #QtrSEnd
END
Drop table #temp
End
Not sure if you have tried this already. Try specifying the exact same column name "slno" as in CREATE TABLE statement, in the below two SELECT statements, instead of "Slno".
select #LoopStart=Min(Slno) from #temp
select #loopEnd=MAX(Slno) from #temp
I found the Solution for the error. I created same #temporary table in outer sp as well as in inner sp with different columns. Basically a #temporary table's scope is limited to the session but when you are calling a inner Sp inside a outer sp it will consider a single session.
Outer SP TempTable Declaration
Create table #tempData
(
Bal numeric(15,2)
)
Inner SP TempTable Declaration
CREATE TABLE #tempData(
[ID] int identity(1,1) ,
[Funded] [numeric](15, 2) NOT NULL,
[NonFunded] [numeric](2, 2) NOT NULL,
[Limitsanctioned] [numeric](15, 2) NOT NULL
)
So I changed the #tmpData to #tempData1 in outer Sp it's working fine.
I have a stored procedure in SQL Server 2005 that the code inside is something like this :
select Unique_ID as ID
into tmp_table
from table1
Then, after some other statements, I do this update :
Update table1
set Flag = ‘Y’
Where Unique_ID in (select Unique_ID from tmp_table)
As you notice, I've purposely mistaken ID by Unique_ID (the column name in tmp_table) when I wanted to update.
Now the bizarre thing that happens, is that SQL Server doesn’t show an error when I execute the stored procedure.
It ignores the line where the error is :
Where Unique_ID in (select Unique_ID from tmp_table)
And runs just this :
Update table1 set
Flag = ‘Y’
Any ideas?
Thanks a lot.
UPDATE:
Here is the stored procedure code :
SET QUOTED_IDENTIFIER ON
SET ANSI_NULLS ON
GO
CREATE PROCEDURE [dbo].[MyProc]
AS
BEGIN
SET NOCOUNT ON
DECLARE #date VARCHAR(20)
SET #date = REPLACE(CONVERT(VARCHAR(8), GETUTCDATE(), 3), '/', '')
+ REPLACE(CONVERT(VARCHAR(8), GETUTCDATE(), 108), ':', '')
DECLARE #PATH AS VARCHAR(500)
SET #PATH = 'C:\MyPath\'
BEGIN TRY
EXEC master.dbo.xp_create_subdir #PATH
END TRY
BEGIN CATCH
PRINT 'FOLDER NOT CREATED'
END CATCH
IF EXISTS (SELECT name FROM sys.tables WHERE name = 'TMP_TABLE')
DROP TABLE TMP_TABLE
SELECT
UNIQUE_ID [Unique ID] ,
'col1' COL1,
'col2' COL2,
'col3' COL3,
'col4' COL4
INTO
TMP_TABLE
FROM
TABLE1
WHERE
Flag IN 'N'
--Try to generate file, If there was any problem, return from the SP => no update
BEGIN TRY
DECLARE #QUERY VARCHAR(MAX)
SET #QUERY = 'EXEC master..xp_cmdshell ''sqlcmd -E -s"," -W -h-1 -Q "SET NOCOUNT ON;SELECT * FROM dbo.TMP_TABLE" | findstr /V /C:"-" /B > '
+ #PaTH + '\FileName_' + #date + '.csv'',no_output;'
EXEC(#QUERY)
END TRY
BEGIN CATCH
PRINT 'Exception during file generation'
RETURN
END CATCH
PRINT 'File Generated'
UPDATE TABLE1
SET Flag = 'Y',
MODIFICATION_DATE = GETDATE()
WHERE
UNIQUE_ID IN (SELECT UNIQUE_ID FROM TMP_TABLE)
BEGIN TRY
DROP TABLE dbo.TMP_TABLE
PRINT 'TMP table dropped'
END TRY
BEGIN CATCH
PRINT 'TMP table not dropped'
END CATCH
END
GO
The problem is on this line:
WHERE UNIQUE_ID IN (SELECT **UNIQUE_ID** FROM TMP_TABLE)
I should replace UNIQUE_ID with [UNIQUE ID]
But SQL Server doesn't throw an error, it simply updates all records.
And here is the structure of table1 :
CREATE TABLE [dbo].[TABLE1]
(
UNIQUE_ID int NOT NULL IDENTITY(1, 1), --Primary key
Flag [varchar] (1) ,
MODIFICATION_DATE datetime,
)
I assume you had a typo in your query. Your tables look like that?
create table1
( Unique_ID int
, Flag char(1)
, ...
);
create tmp_table
( [Unique ID] int primary key
, ...
);
in that case your query is:
Update table1 t1 set
Flag = ‘Y’
Where t1.Unique_ID in (select t1.Unique_ID from tmp_table)
which is true except your id is null. you just compare the Unique_ID from table1 with itself.
Try to explicitly refer to the column to which you want to compare, by using the table name as well:
UPDATE TABLE1
SET Flag = 'Y',
MODIFICATION_DATE = GETDATE()
WHERE
UNIQUE_ID IN (SELECT TMP_TABLE.UNIQUE_ID FROM TMP_TABLE)
Confusion arises because there's a column named UNIQUE_ID on both tables, so by fully-qualifying it with the table name in the subselect, you can be 100% sure that you are referring to the right column.
Given the following simple structure:
TABLE: Product (ProductId, ProductName)
TABLE: Category (CategoryId, CategoryName)
LINK TABLE: ProductId,CategoryId
I have a table type which I want to pass to a stored procedure to insert the values into another table if they don't exist.
CREATE TYPE StringList_TBLType AS TABLE (s NVARCHAR(255) NOT NULL PRIMARY KEY)
I want to do the following in a stored procedure where I pass in a ProductName, and the StringList_TBLType of Category Names
select all the strings from my StringList_TBLType
Insert the string into the Category TABLE if it does not exist
Get the ID of the inserted or already existing Category
Insert the ProductId, and CategoryId into the LINK TABLE.
I could probably struggle along and get something working, but I have little experience with MS SQL, and stored procedures in general, and am scared that I would end up writing a very inefficient way of doing this.
You can use the MERGE statement to capture the category IDs.
DECLARE #changes TABLE (ChangeID VARCHAR(10), Id INTEGER);
DECLARE #JustSomeRandomVariable INT;
MERGE Category AS TARGET
USING #data AS SOURCE
ON TARGET.Category = SOURCE.s
WHEN NOT MATCHED THEN
INSERT ([Category])
VALUES (SOURCE.s)
WHEN MATCHED THEN
UPDATE SET #JustSomeRandomVariable = 1
OUTPUT $action, inserted.Id INTO #changes;
The random variable in the merge statement makes sure that updates get logged into the #changes table variable.
Now you can use the #changes to update your link table.
INSERT INTO Link SELECT ProductID, ChangeID FROM #changes
Just retrieve the required ProductID with a simple select query.
EDIT:
This could potentially result in double records in the Link table. You might need to tweak it a bit, perhaps use the MERGE statement for inserting into the Link table aswell.
#data is the StringList_TBLType paramter of your procedure.
This is what I would suggest (not tested)
CREATE PROCEDURE AddProductToCategories
#productname nvarchar(255),
#categories StringList_TBLType READONLY
AS
BEGIN
DECLARE #productId bigint --change to datatype of Product.ProductId
SET #productId = (SELECT TOP 1 ProductId FROM Product WHERE ProductName = #productname) --What to do if your product names are not unique?
IF #productId is not NULL
BEGIN
DECLARE cur CURSOR FOR (
SELECT cat.CategoryName, cat.CategoryId, c.s
FROM #categories c
RIGHT OUTER JOIN Category cat on c.s = cat.CategoryName
)
DECLARE #id as bigint --change to datatype of Category.CategoryId
DECLARE #name as nvarchar(255) --change to datatype of Category.CategoryName
DECLARE #categoryNameToAdd as nvarchar(255)
OPEN cur
FETCH NEXT FROM cur INTO #name, #id, #categoryNameToAdd
WHILE ##FETCH_STATUS = 0
BEGIN
IF #id is NULL
BEGIN
--category does not exist yet in table Category
INSERT INTO Category (CategoryName) VALUES (#categoryNameToAdd)
SET #id = SCOPE_IDENTITY()
END
INSERT INTO ProductsCategories --your link table
(ProductId, CategoryId)
VALUES
(#productId, #id)
FETCH NEXT FROM cur INTO #name, #id, #categoryNameToAdd
END --cursor
CLOSE cur
DEALLOCATE cur
END --IF #productId
END --sproc
I have a requirement to insert multiple rows into table1 and at the same time insert a row into table2 with a pkID from table1 and a value that comes from a SP parameter.
I created a stored procedure that performs a batch insert with a table valued parameter which contains the rows to be inserted into table1. But I have a problem with inserting the row into table2 with the corresponding Id (identity) from table1, along with parameter value that I have passed.
Is there anyone who implemented this, or what is the good solution for this?
CREATE PROCEDURE [dbo].[oSP_TV_Insert]
#uID int
,#IsActive int
,#Type int -- i need to insert this in table 2
,#dTableGroup table1 READONLY -- this one is a table valued
AS
DECLARE #SQL varchar(2000)
DECLARE #table1Id int
BEGIN
INSERT INTO dbo.table1
(uID
,Name
,Contact
,Address
,City
,State
,Zip
,Phone
,Active)
SELECT
#uID
,Name
,Contact
,Address
,City
,State
,Zip
,Phone
,Active
,#G_Active
FROM #dTableGroup
--the above query will perform batch insert using the records from dTableGroup which is table valued
SET #table1ID = SCOPE_IDENTITY()
-- this below will perform inserting records to table2 with every Id inserted in table1.
Insert into table2(#table1ID , #type)
You need to temporarily store the inserted identity values and then create a second INSERT statement - using the OUTPUT clause.
Something like:
-- declare table variable to hold the ID's that are being inserted
DECLARE #InsertedIDs TABLE (ID INT)
-- insert values into table1 - output the inserted ID's into #InsertedIDs
INSERT INTO dbo.table1(ID, Name, Contact, Address, City, State, Zip, Phone, Active)
OUTPUT INSERTED.ID INTO #InsertedIDs
SELECT
#ID, Name, Contact, Address, City, State, Zip, Phone, Active, #G_Active
FROM #dTableGroup
and then you can have your second INSERT statement:
INSERT INTO dbo.table2(Table1ID, Type)
SELECT ID, #type FROM #InsertedIDs
See the MSDN docs on the OUTPUT clause for more details on what you can do with the OUTPUT clause - one of the most underused and most "unknown" features of SQL Server these days!
Another approach using OUTPUT clause and only one statement for inserting data in both destination tables:
--Parameters
DECLARE #TableGroup TABLE
(
Name NVARCHAR(100) NOT NULL
,Phone VARCHAR(10) NOT NULL
);
DECLARE #Type INT;
--End Of parameters
--Destination tables
DECLARE #FirstDestinationTable TABLE
(
FirstDestinationTableID INT IDENTITY(1,1) PRIMARY KEY
,Name NVARCHAR(100) NOT NULL
,Phone VARCHAR(10) NOT NULL
);
DECLARE #SecondDestinationTable TABLE
(
SecondDestinationTable INT IDENTITY(2,2) PRIMARY KEY
,FirstDestinationTableID INT NOT NULL
,[Type] INT NOT NULL
,CHECK([Type] > 0)
);
--End of destination tables
--Test1
--initialization
INSERT #TableGroup
VALUES ('Bogdan SAHLEAN', '0721200300')
,('Ion Ionescu', '0211002003')
,('Vasile Vasilescu', '0745600800');
SET #Type = 9;
--execution
INSERT #SecondDestinationTable (FirstDestinationTableID, [Type])
SELECT FirstINS.FirstDestinationTableID, #Type
FROM
(
INSERT #FirstDestinationTable (Name, Phone)
OUTPUT inserted.FirstDestinationTableID
SELECT tg.Name, tg.Phone
FROM #TableGroup tg
) FirstINS
--check records
SELECT *
FROM #FirstDestinationTable;
SELECT *
FROM #SecondDestinationTable;
--End of test1
--Test2
--initialization
DELETE #TableGroup;
DELETE #FirstDestinationTable;
DELETE #SecondDestinationTable;
INSERT #TableGroup
VALUES ('Ion Ionescu', '0210000000')
,('Vasile Vasilescu', '0745000000');
SET #Type = 0; --Wrong value
--execution
INSERT #SecondDestinationTable (FirstDestinationTableID, [Type])
SELECT FirstINS.FirstDestinationTableID, #Type
FROM
(
INSERT #FirstDestinationTable (Name, Phone)
OUTPUT inserted.FirstDestinationTableID
SELECT tg.Name, tg.Phone
FROM #TableGroup tg
) FirstINS
--check records
DECLARE #rc1 INT, #rc2 INT;
SELECT *
FROM #FirstDestinationTable;
SET #rc1 = ##ROWCOUNT;
SELECT *
FROM #SecondDestinationTable;
SET #rc2 = ##ROWCOUNT;
RAISERROR('[Test2 results] #FirstDestinationTable: %d rows; ##SecondDestinationTable: %d rows;',1,1,#rc1,#rc2);
--End of test1
Since you need all inserted identity values, look at the output clause of the insert statement: http://msdn.microsoft.com/en-us/library/ms177564.aspx