views:

70

answers:

2
+2  Q: 

T-SQL Foreach Loop

Scenario

  • I have a stored procedure written in T-Sql using SQL Server 2005.
  • "SEL_ValuesByAssetName"
  • It accepts a unique string "AssetName".
  • It returns a table of values.

Question

  • Instead of calling the stored procedure multiple times and having to make a database call everytime I do this, I want to create another stored procedure that accepts a list of all the "AssetNames", and calls the stored procedure "SEL_ValueByAssetName" for each assetname in the list, and then returns the ENTIRE TABLE OF VALUES.

Pseudo Code

foreach(value in @AllAssetsList)
{
@AssetName = value
SEL_ValueByAssetName(@AssetName)
UPDATE #TempTable
}

How would I go about doing this?

A: 

In MSSQL 2000 I would make @allAssetsList a Varchar comma separated values list. (and keep in mind that maximum length is 8000)

I would create a temporary table in the memory, parse this string and insert into that table, then do a simple query with the condition where assetName in (select assetName from #tempTable)

I wrote about MSSQL 2000 because I am not sure whether MSSQL 2005 has some new data type like an array that can be passed as a literal to the SP.

Alexander
How do I go about telling the procedure to loop through all the rows in that temporary table of asset names?
Goober
Rather than using a Temporary Table, wouldn't a Table Function be a better choice?
Ardman
@Goober, the internal select statement will select all the rows from that temporary table.@Ardman, reading about it now.
Alexander
read about it, knew about it, just didn't know the name. Actually this form is only good if your procedure is called from another SQL query. You can't form the table variable inside a procedural language that uses MSSQL, or can you?
Alexander
I thought temporary tables was considered harmful...
Regent
@Alexander, what kind of procedural language are you referring to? .NET Framework's [System.Data.SqlClient](http://msdn.microsoft.com/en-us/library/system.data.sqlclient.aspx), for instance, supports populating [table-valued parameters](http://msdn.microsoft.com/en-us/library/bb675163.aspx)
Regent
any other that would require sending parameters inside query text. PHP, Java, Ruby... And what about using LINQ to SQL or EF?
Alexander
@Regent, read this: http://www.sql-server-performance.com/articles/per/temp_tables_necessary_p1.aspxI didn't know that someone was considering them harmful until I read your comment. Made me look for more info. Thanks for your comments.
Alexander
@Alexander, you are right about poor support for table-valued parameters from other languages -- personally I know only ADO.NET supporting it; nothing in PHP and, I guess, in other non-.NET languages. (But even in .NET you need to jump through hoops to make it work with EF or LINQ to SQL.)As for temporary tables (`... FROM #list`), I would vouch prefer using table variables (`... FROM @list`) instead because they have less locking overhead and therefore are faster.
Regent
+2  A: 

It will look quite crippled with using Stored Procedures. But can you use Table-Valued Functions instead?

In case of Table-Valued functions it would look something like:

SELECT al.Value AS AssetName, av.* FROM @AllAssetsList AS al
    CROSS APPLY SEL_ValuesByAssetName(al.Value) AS av

Sample implementation:

First of all, we need to create a Table-Valued Parameter type:

CREATE TYPE [dbo].[tvpStringTable] AS TABLE(Value varchar(max) NOT NULL)

Then, we need a function to get a value of a specific asset:

CREATE FUNCTION [dbo].[tvfGetAssetValue] 
(   
    @assetName varchar(max)
)
RETURNS TABLE 
AS
RETURN 
(
    -- Add the SELECT statement with parameter references here
    SELECT 0 AS AssetValue
    UNION
    SELECT 5 AS AssetValue
    UNION
    SELECT 7 AS AssetValue
)

Next, a function to return a list AssetName, AssetValue for assets list:

CREATE FUNCTION [dbo].[tvfGetAllAssets] 
(   
    @assetsList tvpStringTable READONLY
)
RETURNS TABLE 
AS
RETURN 
(
    -- Add the SELECT statement with parameter references here
    SELECT al.Value AS AssetName, av.AssetValue FROM @assetsList al
        CROSS APPLY tvfGetAssetValue(al.Value) AS av
)

Finally, we can test it:

DECLARE @names tvpStringTable
INSERT INTO @names VALUES ('name1'), ('name2'), ('name3')

SELECT * FROM [Test].[dbo].[tvfGetAllAssets] (@names)
Regent
+1: Table-Valued functions sounds like the ideal solution for this.
Ardman
What datatype would @AllAssetsList be?
Goober
@Goober, you can use [Table-Valued Parameters](http://msdn.microsoft.com/en-us/library/bb510489.aspx)
Regent