Stored Procedure Loop - sql

I have a stored procedure which generates some xml based on 3 parameters: #property_id, #property_component_id and #return_as_xml. The value for #return_as_xml will be the same for all records which is 0.
I have modified this and now need to run it for multiple Property records based on a specific condition i.e. for active and leased properties. A one-many relationship exists between property and property_component, so a property can have multiple property_components and does have at least one corresponding record.
I some how need to do a select of the records I want based on the active and leased fields in the Property table. Select the corresponding property_component_id from the property_component table where a corresponding property_id exists. I then want to loop through each of the result rows, execute the stored procedure passing in new parameters for each row.
I don't want to modify the stored procedure more than I already have as it is executed from an asp.net console application which requires it to return xml for a single property, ie I don't want alter the stored procedure to return xml for multiple properties.
My stored procedure execution is just the standard generated sql server code:
DECLARE #return_value int
EXEC #return_value = [dbo].[p_CPGUploader__FetchXMLForProperty_RCA]
#property_id = 106016,
#property_component_id = 108382,
#return_as_xml = 0
SELECT 'Return Value' = #return_value
GO
How can I modify this to work for my described scenario, how can I get select a recordset then loop through it executing the stored procedure passing based on values from each row of the select? I come from an asp.net background and my SQL Server skills are extremely limited so any assistance would be greatly appreciated. I am using SQL Server 2008 R2 although I believe the original stored procedure was written in 2004 if that's of any relevance.

If you really can't change the stored procedure, then you can create a loop using a cursor.
I wouldn't advise this course of action unless absolutely necessary though.
declare c cursor local read_only for
select property_component_id from propertiestable
declare #id
open c
fetch from c into #id
while ##fetch_status=0
begin
EXEC #return_value = [dbo].[p_CPGUploader__FetchXMLForProperty_RCA]
#property_id = 106016,
#property_component_id = #id,
#return_as_xml = 0
fetch next from c into #id
end
close c
deallocate c

Related

How to run a query multiple times with different parameters?

I'm trying to figure out the best way to get a query to run multiple times with different parameters. I've tried putting it as a stored procedure and running it with cursors, but I'm pretty novice at the cursor concept. Here is the query and my first attempt with cursor.
SELECT
AVG([processingseconds])
FROM [nucor_historical_data].[dbo].[test_Lift_Matrix]
Where ActualGauge between 0 and .21875 and ActualWidth between 0 and 55
and inches between 0 and 120 and MaxLiftWeight between 0 and 10000 and
processingseconds is not null
So the parameters I need to loop through are in the where statement. I have combinations for all these groupings you see in another table.
someone suggested trying this to me earlier from another stack question, so I tested with one parameter but couldn't get it working. Is there a better way to attempt this?
DECLARE #param varchar(200)
-- getting your parameter from the table
DECLARE curs CURSOR LOCAL FAST_FORWARD FOR
SELECT gauge FROM groupings
OPEN curs
FETCH NEXT FROM curs INTO #param
-- executing your stored procedure once for every value of your parameter
WHILE ##FETCH_STATUS = 0 BEGIN
EXEC group_average #param
FETCH NEXT FROM curs INTO #param
END
CLOSE curs
DEALLOCATE curs
A stored procedure is the way to go here - passing the parameters as arguments.
Here is a watered down example of what you are trying to do, that is, run the select statement repeatedly using the values from another table as the inputs. You'll need to adapt to your specifics, I only did a subset of your fields:
DECLARE #UniqueId int
DECLARE #AgMin numeric(10,4)
DECLARE #AgMax numeric(10,4)
DECLARE #tmp TABLE (UniqueId INT, AgMin numeric(10,4), AgMax numeric(10,4))
INSERT #tmp SELECT ID, AGMIN, AGMAX FROM [YOUROTHERTABLEWITHTHESENUMBERS]
SELECT TOP 1 #UniueId=UniqueId, #AGMin=AGMin, #AGMAX=AgMax FROM #tmp
WHILE (##rowcount > 0)
BEGIN
SELECT AVG([processingseconds]) FROM test_Lift_Matrix Where ActualGauge between #AGMIN and #AGMAX (the rest of your conditions...)
DELETE FROM #tmp WHERE UniqueId=#UniqueId
SELECT TOP 1 #UniqueId=UniqueId, #AGMin=AGMin, #AGMAX=AgMax FROM #tmp
END
I think what you're trying to do is a execute a dynamic query (because of the changing where clause) having it behaviour like a static query.
I suppose your query is executed upon criteria coming from client application.
If this is the case try to a parametrized dinamic query string executed through sp_executesql.
This is a very effective technique. See more following:
http://www.sommarskog.se/dyn-search-2005.html

Iterate through XML variable in SQL Server

I have a XML variable in a stored procedure (SQL Server 2008), its sample value is
<parent_node>
<category>Low</category>
<category>Medium</category>
<category>High</category>
</parent_node>
I have to take each category and insert into table as a separate record. How to iterate in XML and take individual node value?
If I want to call a stored procedure and send each category as input parameter, how we can do that? The stored procedure is legacy one, which accept only one category at at time. I am trying to do invoke procedure in this way.
loop fetch single category from xml variable.
invoke stored procedure with current category.
move to next category.
loop until list contain value.
Any help will be appreciated.
Something like this?
DECLARE #XmlVariable XML = '<parent_node>
<category>Low</category>
<category>Medium</category>
<category>High</category>
</parent_node>'
INSERT INTO dbo.YourTargetTable(CategoryColumn)
SELECT
XTbl.Cats.value('.', 'varchar(50)')
FROM
#XmlVariable.nodes('/parent_node/category') AS XTbl(Cats)
Update: if you must use the old legacy stored procedure and cannot change it (that would be my preferred way of doing this), then you would have to do the row-by-agonizing-row (RBAR) looping yourself, e.g. by using a table variable:
-- declare temporary work table
DECLARE #RbarTable TABLE (CategoryName VARCHAR(50))
-- insert values into temporary work table
INSERT INTO #RbarTable(CategoryName)
SELECT
XTbl.Cats.value('.', 'varchar(50)')
FROM
#XmlVariable.nodes('/parent_node/category') AS XTbl(Cats)
-- declare a single category
DECLARE #CategoryNameToBeInserted VARCHAR(50)
-- get the first category
SELECT TOP 1 #CategoryNameToBeInserted = CategoryName FROM #RbarTable
-- as long as we have data
WHILE #CategoryNameToBeInserted IS NOT NULL
BEGIN
-- execute your stored procedure here.....
EXEC sp_executesql N'dbo.YourStoredProcedure #CategoryName',
N'#CategoryName VARCHAR(50)',
#CategoryName = #CategoryNameToBeInserted
-- delete the category we just inserted from the temporary work table
DELETE FROM #RbarTable WHERE CategoryName = #CategoryNameToBeInserted
-- see if we still have more categories to insert
SET #CategoryNameToBeInserted = NULL
SELECT TOP 1 #CategoryNameToBeInserted = CategoryName FROM #RbarTable ORDER BY CategoryName
END
With XML in SQL Server there's always more than one way to do it. Depending on the size of your XML doc and the number of times you're querying it, you could be best off using sp_xml_preparedocument which parses the document, gives you a handle to reference it, and then you can query it as many times and ways as you want to. Here's how you do that:
declare #xml xml = '
<parent_node>
<category>Low</category>
<category>Medium</category>
<category>High</category>
</parent_node>'
declare #xml_handle int
exec sp_xml_preparedocument #xml_handle output, #xml
select value from openxml(#xml_handle, '/parent_node/category', 2) with (value varchar(100) 'text()') x
exec sp_xml_removedocument #xml_handle

Stored procedure in MS SQL Server 2005

Here I got a scenario: When I press a button in client application (developed in Delphi) a stored procedure is activated. The stored procedure first declares a cursor for a select statement which returns two columns-BankID and BankCategoryID.Then I need to fetch each row inside the cursor into a record and check for the BankCategoryID and return a resultset according to the BankCategoryID like:
CASE WHEN fetched_record.BankCategoryID=1 THEN
SELECT STATEMENT1 WHEN fetched_record.BankCategoryID=2 THEN
SELECT STATEMENT2 and so on...
and then I return the result set retrieved from any of the above cases to my client application. Is thi possible?
Perhaps you'd want to use an IF as a control statement within your cursor?
WHILE ##FETCH_STATUS = 0
BEGIN
IF #BankCategoryID=1
BEGIN
SELECT Baz From Bat;
DECLARE #Spam bit;
SELECT #Spam = 0;
END
IF #BankCategoryID=2
BEGIN
SELECT Foo FROM Bar;
END
FETCH NEXT FROM MyCursor INTO #BankID, #BankCategory
END
Here's a sample TSQL cursor. You'd be loading your two column values into 2 variables: #BankID and #BankCategoryID. i.e. FETCH NEXT FROM MyCursor INTO #BankID, #BankCategoryID
Aside: I'm wondering if this could be done all without a cursor? In either case, the above should work for you in implementing more TSQL statements in each iteration of your cursor.

How to Suppress the SELECT Output of a Stored Procedure called from another Stored Procedure in SQL Server?

I'm not talking about doing a "SET NOCOUNT OFF". But I have a stored procedure which I use to insert some data into some tables. This procedure creates a xml response string, well let me give you an example:
CREATE PROCEDURE [dbo].[insertSomeData] (#myParam int) AS
DECLARE #reply varchar(2048)
... Do a bunch of inserts/updates...
SET #reply = '<xml><big /><outputs /></xml>'
SELECT #reply
GO
So I put together a script which uses this SP a bunch of times, and the xml "output" is getting to be too much (it's crashed my box once already).
Is there a way to suppress or redirect the output generated from this stored procedure? I don't think that modifying this stored procedure is an option.
thanks.
I guess i should clarify. This SP above is being called by a T-SQL Update script that i wrote, to be run through enterprise studio manager, etc.
And it's not the most elegant SQL i've ever written either (some psuedo-sql):
WHILE unprocessedRecordsLeft
BEGIN
SELECT top 1 record from updateTable where Processed = 0
EXEC insertSomeData #param = record_From_UpdateTable
END
So lets say the UpdateTable has some 50k records in it. That SP gets called 50k times, writing 50k xml strings to the output window. It didn't bring the sql server to a stop, just my client app (sql server management studio).
The answer you're looking for is found in a similar SO question by Josh Burke:
-- Assume this table matches the output of your procedure
DECLARE #tmpNewValue TABLE ([Id] int, [Name] varchar(50))
INSERT INTO #tmpNewValue
EXEC [ProcedureB]
-- SELECT [Id], [Name] FROM #tmpNewValue
I think I found a solution.
So what i can do now in my SQL script is something like this (sql-psuedo code):
create table #tmp(xmlReply varchar(2048))
while not_done
begin
select top 1 record from updateTable where processed = 0
insert into #tmp exec insertSomeData #param=record
end
drop table #tmp
Now if there was a even more efficient way to do this. Does SQL Server have something similar to /dev/null? A null table or something?
Answering the question, "How do I suppress stored procedure output?" really depends on what you are trying to accomplish. So I want to contribute what I encountered:
I needed to supress the stored procedure (USP) output because I just wanted the row count (##ROWCOUNT) from the output. What I did, and this may not work for everyone, is since my query was already going to be dynamic sql I added a parameter called #silentExecution to the USP in question. This is a bit parameter which I defaulted to zero (0).
Next if #silentExecution was set to one (1) I would insert the table contents into a temporary table, which is what would supress the output and then execute ##ROWCOUNT with no problem.
USP Example:
CREATE PROCEDURE usp_SilentExecutionProc
#silentExecution bit = 0
AS
BEGIN
SET NOCOUNT ON;
DECLARE #strSQL VARCHAR(MAX);
SET #strSQL = '';
SET #strSQL = 'SELECT TOP 10 * ';
IF #silentExecution = 1
SET #strSQL = #strSQL + 'INTO #tmpDevNull ';
SET #strSQL = #strSQL +
'FROM dbo.SomeTable ';
EXEC(#strSQL);
END
GO
Then you can execute the whole thing like so:
EXEC dbo.usp_SilentExecutionProc #silentExecution = 1;
SELECT ##ROWCOUNT;
The purpose behind doing it like this is if you need the USP to be able to return a result set in other uses or cases, but still utilize it for just the rows.
Just wanted to share my solution.
I have recently come across with a similar issue while writing a migration script and since the issue was resolved in a different way, I want to record it.
I have nearly killed my SSMS Client by running a simple while loop for 3000 times and calling a procedure.
DECLARE #counter INT
SET #counter = 10
WHILE #counter > 0
BEGIN
-- call a procedure which returns some resultset
SELECT #counter-- (simulating the effect of stored proc returning some resultset)
SET #counter = #counter - 1
END
The script result was executed using SSMS and default option on query window is set to show “Results to Grid”[Ctrl+d shortcut].
Easy Solution:
Try setting the results to file to avoid the grid to be built and painted on the SSMS client. [CTRL+SHIFT+F keyboard shortcut to set the query results to file].
This issue is related to : stackoverflow query
Man, this is seriously a case of a computer doing what you told it to do instead of what you wanted it to do.
If you don't want it to return results, then don't ask it to return results. Refactor that stored procedure into two:
CREATE PROCEDURE [dbo].[insertSomeData] (#myParam int) AS
BEGIN
DECLARE #reply varchar(2048)
--... Do a bunch of inserts/updates...
EXEC SelectOutput
END
GO
CREATE PROCEDURE SelectOutput AS
BEGIN
SET #reply = '<xml><big /><outputs /></xml>'
SELECT #reply
END
From which client are you calling the stored procedure? Say it was from C#, and you're calling it like:
var com = myConnection.CreateCommand();
com.CommandText = "exec insertSomeData 1";
var read = com.ExecuteReader();
This will not yet retrieve the result from the server; you have to call Read() for that:
read.Read();
var myBigString = read[0].ToString();
So if you don't call Read, the XML won't leave the Sql Server. You can even call the procedure with ExecuteNonQuery:
var com = myConnection.CreateCommand();
com.CommandText = "exec insertSomeData 1";
com.ExecuteNonQuery();
Here the client won't even ask for the result of the select.
You could create a SQL CLR stored procedure that execs this. Should be pretty easy.
I don't know if SQL Server has an option to suppress output (I don't think it does), but the SQL Query Analyzer has an option (under results tab) to "Discard Results".
Are you running this through isql?
You said your server is crashing. What is crashing the application that consumes the output of this SQL or SQL Server itself (assuming SQL Server).
If you are using .Net Framework application to call the stored procedure then take a look at SQLCommand.ExecuteNonQuery. This just executes stored procedure with no results returned. If problem is at SQL Server level then you are going to have to do something different (i.e. change the stored procedure).
You can include in the SP a parameter to indicate if you want it to do the select or not, but of course, you need to have access and reprogram the SP.
CREATE PROCEDURE [dbo].[insertSomeData] (#myParam int, #doSelect bit=1) AS
DECLARE #reply varchar(2048)
... Do a bunch of inserts/updates...
SET #reply = '<xml><big /><outputs /></xml>'
if #doSelect = 1
SELECT #reply
GO
ever tried SET NOCOUNT ON; as an option?

SQL iterating over a list to call EXEC on each item

Attempt to generalize my questions...
I want to execute a stored procedure for each result returned by a SELECT statement.
Mentally I want to try something like
EXEC myStoredProc (SELECT id FROM sometable WHERE cond = #param)
More details related to my specific case...
I have a SaaS application. I would like to delete a tenant from the system. Before I can delete the tenant I must delete all records in the database associated with that tenant.
Tenants own items such as Forms which contain many different types of Fields. I already have a stored proc that deletes a Form and all of its associated items (like Fields). For maintenance reasons (Ie. not wanted to duplicate the logic that determines dependances and associations between records and a form) I'd like to just call that StoredProc for each Form that belongs to a Tenant.
I can retrieve the list of forms by running a query like...
Select formId FROM Forms WHERE Tenant = #TenantId
What I want to do with the result of that query is EXEC my Delete_Form stored procedure.
How can I do this?
In the case where you don't have control of the foreign keys and can't do cascade deletes, you could create a cursor to loop thru and execute the stored proc for each.
declare #formID int
declare FormsCursor cursor fast_forward for Select formId FROM Forms WHERE Tenant = #Tenant
open FormsCursor
fetch next from FormsCursor into #formID
while ##fetch_status = 0
begin
exec Delete_Form #formID
fetch next from FormsCursor into #formID
end
close FormsCursor
deallocate FormsCursor
You could just turn Cascade delete on and deleting the parent record would delete all child records associated with it.
If not you'll have to create a cursor (The link is for sql server, but I would assume that cursors for other RDBMS are similar) and loop through each of the results pulling out the form id and executing the [Delete_Field_Procedure] for each one.