Run A Loop in SQL Server - sql-server

I want to run a stored procedure on each ID return by a SELECT query. Is there a simple way to do something like:
FOREACH (SELECT ID FROM myTABLE WHERE myName='bob') AS id
BEGIN
EXEC #return_value = [dbo].[spMYPROC]
#PARAM1 = id
#PARAM2 = 0
END

Since I just happened to answer a very similar question yesterday, I have this code handy. As others have stated, it may not be the best approach, but still it's nice to learn how to use a while loop anyway.
Assuming a table named "Customer"
declare #Id int
select #Id = MIN(Id)
from Customer c
while(select COUNT(1)
from Customer c
where c.Id >= #Id) > 0
begin
--run your sproc right here
select #Id = MIN(Id)
from Customer c
where c.Id > #Id
end

DECLARE #ID INT, #return_value INT
DECLARE c CURSOR FOR
SELECT
ID
FROM myTABLE
WHERE myName = 'bob'
OPEN c; FETCH NEXT FROM c INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC #return_value = [dbo].[spMYPROC]
#PARAM1 = #ID,
#PARAM2 = 0
FETCH NEXT FROM c INTO #ID
END
CLOSE c; DEALLOCATE c;

You have two option here
Option 1 Using Split Function
Pass a comma deliminated list of IDs and use a Split function Inside your Procedure to make split these values and do whatever you want to do with it.
To
Make it work you will need two thing
1) Create a Function which
accepts a Comma Deliminated string and split them.
2) Modify you
Store Procedure and add this function in there in a way that passed
parameter is passed to the function inside that store procedure and
that function split the values before passing it onto your store
Procedure .
Create this function 1st
Function Definition
CREATE FUNCTION [dbo].[FnSplit]
(
#List nvarchar(2000),
#SplitOn nvarchar(5)
)
RETURNS #RtnValue table (Id int identity(1,1), Value nvarchar(100))
AS
BEGIN
WHILE(Charindex(#SplitOn,#List)>0)
BEGIN
INSERT INTO #RtnValue (value)
SELECT VALUE = ltrim(rtrim(Substring(#List,1,Charindex(#SplitOn,#List)-1)))
SET #List = SUBSTRING(#List,Charindex(#SplitOn,#List)+len(#SplitOn),len(#List))
END
INSERT INTO #RtnValue (Value)
SELECT VALUE = ltrim(rtrim(#List))
RETURN
END
Modify you strored Procedure something like this
Stored Procedure
ALTER Procedure [dbo].[spMYPROC] (#Param1 VARCHAR(1000)= NULL)
AS
BEGIN
SELECT * FROM TableName
where ColumnNAME IN (SELECT Value FROM dbo.FnSplit(#Param1,','))
END
GO
Option 2 Table Type Parameter
Create a Table Type and alter your proc to accept a Table Type Parameter and do whatever you want to do with them values inside your proc.
TABLE TYPE
CREATE TYPE dbo.TYPENAME AS TABLE
(
Value int
)
GO
Stored Procedure to Accept That Type Param
ALTER PROCEDURE [dbo].[spMYPROC]
#TableParam TYPENAME READONLY
AS
BEGIN
SET NOCOUNT ON;
--Temp table to store passed Id values
declare #tmp_values table (value INT );
--Insert passed values to a table variable inside the proc
INSERT INTO #tmp_values (value)
SELECT Value FROM #TableParam
/* Do your stuff here whatever you want to do with Ids */
END
EXECUTE PROC
Declare a variable of that type and populate it with your values.
DECLARE #Table TYPENAME --<-- Variable of this TYPE
INSERT INTO #Table --<-- Populating the variable
SELECT ID FROM myTABLE WHERE myName='bob'
EXECUTE [dbo].[spMYPROC] #Table --<-- Stored Procedure Executed

Related

how to dynamically find and replace the function text

I have 800+ functions in my database. I would need to modify their source databases dynamically and create snapshots.
example of the function:
create function [schema1].[funTest1] (#param1 varchar(50))
returns table as
return
(
select * from [curr_database1].[schema1].[funTest1](#param1)
union
select * from [curr_database2].[schema1].[funTest1](#param1)
)
I want to change the script as:
create or alter function [schema1].[funTest1] (#param1 varchar(50))
returns table as return
(
select * from [new_database2].[schema1].[funTest1](#param1)
union
select * from [new_database3].[schema1].[funTest1](#param1)
)
basically, I got all the functions script using the sys.syscomments. I'm looking for an option to find and replace the database dynamically to create the snapshots.
How can I get it? Thank you!
Here is the sample code that I have developed for sharing. All the database in the functions starts with the same text(for ex. "curr"). Please share your thoughts. Thanks in advance!
create or alter proc test_proc as
begin
set nocount on
-- this piece of code has the new databases
if object_id('tempdb..#dbNames') is not null drop table #dbNames
create table #dbNames (dbName varchar(1000), id int)
insert into #dbNames(dbName, id) values ('new_database2', 1),('new_database3', 2)
insert into #dbNames(dbName, id) values ('new_database8', 3),('new_database9', 4)
-- this one has the sample functions
if object_id('tempdb..#dbFunctions') is not null drop table #dbFunctions
create table #dbFunctions (funText nvarchar(max))
insert into #dbFunctions (funText) values('create function [schema1].[funTest1] (#param1 varchar(50))
returns table as
return
(
select * from [curr_database1].[schema1].[funTest1](#param1)
union
select * from [curr_database2].[schema1].[funTest1](#param1)
)'),
('create function [schema2].[funTest2] (#param1 varchar(50), #param2 varchar(100))
returns table as
return
(
select * from [curr_database4].[schema2].[funTest2](#param1, #param2)
union
select * from [curr_database5].[schema2].[funTest2](#param1, #param2)
)')
-- declare variables and assign value for #frmStr variable (for testing purposes)
declare #str nvarchar(max)
declare #dbName varchar(100)
declare #frmStr varchar(100) = '[curr_database1]'
-- get the total count of the databases and the functions to iterate and replace the string
declare #dbCnt int = (select count(id) from #dbNames)
declare #fnCnt int = (select count(*) from #dbFunctions)
while #dbCnt > 0
begin
set #dbname = (select dbname from #dbnames where id = #dbcnt)
while #fnCnt > 0
begin
-- this is where I would need to replace the code
select #str = replace(funText, #frmStr, #dbName) from #dbFunctions
select #str
set #fnCnt = #fnCnt - 1
end
set #dbCnt = #dbCnt - 1
end
end
Your actual goal isn't clear, but to answer the question you asked, you can use REPLACE functions in the query to syscomments that you used to get the code in the first place:
REPLACE(
REPLACE([FunctionTextColumn],'curr_database1','new_database2')
,'curr_database2','new_database3'
)

Stored procedure to update table in SQL

I have a following issue...
I have three variable, v1,v2,v2 (All are of type java.util.ArrayList). I want to write a stored procedure that will take this variable as an input and update the one table.
How can I loop throw the array list of the variable in sql and update the table?
For instance, the values for v1 (10,11,12), v2(21,22,23), v3(31,32,33). The sql update of the table should happen as follows
Table1:
Row1: 10,21,31
Row2: 11,22,32
Row3: 12,23,33
I will be thankful if someone could get back to me on how to write the store procedure for this.
I have used this approach and it works for me perfectly fine.
Have your stored procedure receive the three variables, each as varchar(max) -- if you know the size you can write the number instead of max. For example:
create procedure usp_testSP
#v1 varchar(max),
#v2 varchar(max),
#v3 varchar(max)
as
begin
declare #v1Values table (number int);
insert into #v1values
select * from dbo.fnSplit(#v1,','); -- the fnSplit function is given below
-- this way you can retrieve all values for other two variables
-- then you can use the corresponding tables, i.e.: #v1Values to complete the steps you need to.
end
Here's the code for dbo.fnSplit(#inputList, #delimiter):
CREATE FUNCTION [dbo].[fnSplit](#sInputList VARCHAR(max), #sDelimiter VARCHAR(10) = ',')
RETURNS #List TABLE (item varchar(100))
BEGIN
DECLARE #sItem varchar(100)
WHILE CHARINDEX(#sDelimiter, #sInputList, 0) <> 0
BEGIN
SELECT #sItem = RTRIM(LTRIM(SUBSTRING(#sInputList, 1, CHARINDEX(#sDelimiter, #sInputList,0) - 1))),
#sInputList = RTRIM(LTRIM(SUBSTRING(#sInputList, CHARINDEX(#sDelimiter, #sInputList, 0) + LEN(#sDelimiter),LEN(#sInputList))))
IF LEN(#sItem) > 0
INSERT INTO #List SELECT #sItem
END
IF LEN(#sInputList) > 0
BEGIN
INSERT INTO #List SELECT #sInputList
END
RETURN
END
And finally, in your java code, you can convert the list to a string and pass it on to the stored procedure call.
List<Integer> v1; //suppose this is the list that contains the values.
String s = String.join("," /*this is the delimiter. It should be the same as the one you use when you call the dbo.fnSplit() function.*/, v1);

SQL Server: how to create a stored procedure

I'm learning sql from a book and I'm trying to write a stored procedure but I don't believe that I'm doing it correctly. Is the following way not valid in Microsoft SQL? If not, when is it valid, if ever?
create procedure dept_count(in dept_name varchar(20), out d_count integer)
begin
select count(*) into d_count
from instructor
where instructor.dept_name=dept_count.dept_name
end
I get the following error
Msg 156, Level 15, State 1, Procedure wine_change, Line 1
Incorrect syntax near the keyword 'in'.
T-SQL
/*
Stored Procedure GetstudentnameInOutputVariable is modified to collect the
email address of the student with the help of the Alert Keyword
*/
CREATE PROCEDURE GetstudentnameInOutputVariable
(
#studentid INT, --Input parameter , Studentid of the student
#studentname VARCHAR (200) OUT, -- Output parameter to collect the student name
#StudentEmail VARCHAR (200)OUT -- Output Parameter to collect the student email
)
AS
BEGIN
SELECT #studentname= Firstname+' '+Lastname,
#StudentEmail=email FROM tbl_Students WHERE studentid=#studentid
END
In T-SQL stored procedures for input parameters explicit 'in' keyword is not required and for output parameters an explicit 'Output' keyword is required. The query in question can be written as:
CREATE PROCEDURE dept_count
(
-- Add input and output parameters for the stored procedure here
#dept_name varchar(20), --Input parameter
#d_count int OUTPUT -- Output parameter declared with the help of OUTPUT/OUT keyword
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Statements for procedure here
SELECT #d_count = count(*)
from instructor
where instructor.dept_name=#dept_name
END
GO
and to execute above procedure we can write as:
Declare #dept_name varchar(20), -- Declaring the variable to collect the dept_name
#d_count int -- Declaring the variable to collect the d_count
SET #dept_name = 'Test'
Execute dept_count #dept_name,#d_count output
SELECT #d_count -- "Select" Statement is used to show the output
I think it can help you:
CREATE PROCEDURE DEPT_COUNT
(
#DEPT_NAME VARCHAR(20), -- Input parameter
#D_COUNT INT OUTPUT -- Output parameter
-- Remember parameters begin with "#"
)
AS -- You miss this word in your example
BEGIN
SELECT COUNT(*)
INTO #D_COUNT -- Into a Temp Table (prefix "#")
FROM INSTRUCTOR
WHERE INSTRUCTOR.DEPT_NAME = DEPT_COUNT.DEPT_NAME
END
Then, you can call the SP like this way, for example:
DECLARE #COUNTER INT
EXEC DEPT_COUNT 'DeptName', #COUNTER OUTPUT
SELECT #COUNTER
Try this:
create procedure dept_count(#dept_name varchar(20),#d_count int)
begin
set #d_count=(select count(*)
from instructor
where instructor.dept_name=dept_count.dept_name)
Select #d_count as count
end
Or
create procedure dept_count(#dept_name varchar(20))
begin
select count(*)
from instructor
where instructor.dept_name=dept_count.dept_name
end
CREATE PROCEDURE [dbo].[USP_StudentInformation]
#S_Name VARCHAR(50)
,#S_Address VARCHAR(500)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Date VARCHAR(50)
SET #Date = GETDATE()
IF EXISTS (
SELECT *
FROM TB_StdFunction
WHERE S_Name = #S_Name
AND S_Address = #S_Address
)
BEGIN
UPDATE TB_StdFunction
SET S_Name = #S_Name
,S_Address = #S_Address
,ModifiedDate = #Date
WHERE S_Name = #S_Name
AND S_Address = #S_Address
SELECT *
FROM TB_StdFunction
END
ELSE
BEGIN
INSERT INTO TB_StdFunction (
S_Name
,S_Address
,CreatedDate
)
VALUES (
#S_Name
,#S_Address
,#date
)
SELECT *
FROM TB_StdFunction
END
END
Table Name : TB_StdFunction
S_No INT PRIMARY KEY AUTO_INCREMENT
S_Name nvarchar(50)
S_Address nvarchar(500)
CreatedDate nvarchar(50)
ModifiedDate nvarchar(50)
Create this way.
Create procedure dept_count(dept_name varchar(20),d_count integer)
begin
select count(*) into d_count
from instructor
where instructor.dept_name=dept_count.dept_name
end
try this:
create procedure dept_count( #dept_name varchar(20), #d_count INTEGER out)
AS
begin
select count(*) into d_count
from instructor
where instructor.dept_name=dept_count.dept_name
end
To Create SQL server Store procedure in SQL server management studio
Expand your database
Expand programmatically
Right-click on Stored-procedure and Select "new Stored Procedure"
Now, Write your Store procedure, for example, it can be something like below
USE DatabaseName;
GO
CREATE PROCEDURE ProcedureName
#LastName nvarchar(50),
#FirstName nvarchar(50)
AS
SET NOCOUNT ON;
//Your SQL query here, like
Select FirstName, LastName, Department
FROM HumanResources.vEmployeeDepartmentHistory
WHERE FirstName = #FirstName AND LastName = #LastName
GO
Where, DatabaseName = name of your database
ProcedureName = name of SP
InputValue = your input parameter value (#LastName and #FirstName) and type = parameter type example nvarchar(50) etc.
Source: Stored procedure in sql server (With Example)
To Execute the above stored procedure you can use sample query as below
EXECUTE ProcedureName #FirstName = N'Pilar', #LastName = N'Ackerman';

Calling a stored proc that returns a recordset from within a stored proc

Working in SQL Server 2005, I have a stored procedure that inserts a record and returns the new ID via SELECT ##IDENTITY; as the last command.
I then want to call this from another stored proc, and get the value of the new ID.
But I can't work out how to get the value returned from the first procedure.
Example:
CREATE PROCEDURE spMyInsert(#Field1 VARCHAR(10)) AS
BEGIN
INSERT INTO tMyTable (Column1) VALUES (#Field1); // ID column implicitly set
SELECT ##IDENTITY ID;
END
CREATE PROCEDURE spMyMain AS
BEGIN
DECLARE #NewID INT;
EXEC spMyInsert 'TEST';
// How do I set #NewID to the value returned from spMyInsert?
END
There is another question that nearly answers my question, but not quite. This explains how to insert the results into another table, but all I want to do is store it in a local variable.
Looking at other similar questions, the general answer is to change to either set an OUTPUT variable or create a function to do it, but I can't do this in my case as other .NET data access stuff uses the same stored proc, and I don't want to have to duplicate all the work of the stored procs as functions as well.
I couple of things that I've tried but all fail are:
SET #NewID = (EXEC spMyInsert 'TEST');
SET #NewID = (SELECT ID FROM (EXEC spMyInsert 'TEST'));
Anybody know how to do this?
Thanks,
Ben
By the way you should probably check that ##identity is what you need as opposed to scope_identity.
If it is what you need then it will still be accessible in the calling stored procedure too.
CREATE PROCEDURE spMyMain
AS
BEGIN
DECLARE #NewID INT;
EXEC spMyInsert 'TEST';
SET #NewID = ##IDENTITY
SELECT #NewID AS '#NewID'
END
The more general solution that would need to be applied if you use scope_identity and don't want to use either output parameters or the procedure return code is
CREATE PROCEDURE spMyMain AS
BEGIN
DECLARE #NewID INT;
DECLARE #IdHolder TABLE
(
id INT
)
INSERT INTO #IdHolder
EXEC spMyInsert 'TEST';
IF ##ROWCOUNT<>1
RAISERROR('Blah',16,1)
SELECT #NewID = id FROM #IdHolder
END
First, don't use ##IDENTITY, use SCOPE_IDENTITY() instead (search this site or Google for the reason why). Then just return the value in an output parameter:
CREATE PROCEDURE spMyInsert(#Field1 VARCHAR(10), #NewID int output) AS
BEGIN
INSERT INTO tMyTable (Column1) VALUES (#Field1);
SET #NewID = scope_identity();
END
go
CREATE PROCEDURE spMyMain AS
BEGIN
DECLARE #NewID INT;
EXEC spMyInsert #Field1 = 'TEST', #NewID = #NewID OUTPUT;
END
go
The issue here is that the spMyInsert returns a Select. When you execute spMyMain it will return the Select from spMyInsert and then the select from spMyMain
I would suggest that you amend spMyInsert to utilise OUTPUT parameters
CREATE PROCEDURE spMyInsert(#Field1 VARCHAR(10), #NewId int output) AS
BEGIN
INSERT INTO tMyTable (Column1) VALUES (#Field1); // ID column implicitly set
SELECT #NewId = ##SCOPE_IDENTITY;
END
and then
CREATE PROCEDURE spMyMain AS
BEGIN
DECLARE #NewID INT;
Set #NewId = 0
EXEC spMyInsert 'TEST', #NewId output;
select #NewId
// How do I set #NewID to the value returned from spMyInsert?
END
Note that I have also changed ##Identity to ##scope_identity It is better to use ##Scope_Identity as that will return the new ID that applies to the current connection.
Try this:
Execute #NewID = spMyInsert 'TEST'
Edit: After reading his question more thoroughly and realizing he was dealing with a select rather than a return: Could you wrap that procedure in a function call and then call the function?
select #NewId = from fnMyInsert('TEST')
An output parameter is the way to go, but if you really can't change the inner SP then, as you say, you can have the inner SP return its results to a table and then get the value out of there.
eg.
declare #NewID int,
#Customer table(CustomerId int);
insert into #Customer
exec spMyInsert 'TEST';
select #NewID = CustomerId from #Customer;

How do I get identity from one sproc and pass it in to another sproc?

Hi all i'm looking for a way to pass in a generated id from a sproc to another sproc.
i.e.
#name varchar(12),
#descript varchar(55)
#test_date date,
#test_result varchar(1)
BEGIN
EXEC ts_create_item #name, #descript
END
if (#test_date is not null) or (isnull(#test_result_id,'')!='')
BEGIN
Exec ts_update_test_results #itemid(generated from the sproc before), #test_date, #test_result
End
Is there a way to do this in sql server?
Thanks
Use an output variable
so you would declare the previous proc like
Create proc SomeProc (#parm1 int, #parm2 int, #id int = null OUTPUT)
as
Begin
...do some insert
select #id = scope_identity()
End
And remember, OUTPUT has to be specified both when declaring and assigning the parameter
ie.
Exec someproc #parm1, #parm2, #id OUTPUT
Alternately you can use a local variable to hold the result
e.g.
create proc somesample(#in int)
as
Begin
select #in * 2
End
declare #var int
exec #var = somesample 1
print #var
2

Resources