SQLServerCentral is supported by Red Gate Software Ltd.
 
Log in  ::  Register  ::  Not logged in
Search:  
 
 
        
Home       Members    Calendar    Who's On


««12

-dynamic- function to convert several rows of a table to a csv list Expand / Collapse
Author
Message
Posted Friday, July 11, 2008 5:04 AM
SSCommitted

SSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommitted

Group: General Forum Members
Last Login: Wednesday, November 04, 2009 4:58 AM
Points: 1,782, Visits: 4,499

I wonder why people say it is IMPOSSIBLE to call a stored procedure from a function. It cannot be done directly, but, indirectly, YES it is POSSIBLE. I just ran through the Blog of Denis Gobo illustrating the trick to do it.

if your table data is not HUGE, it can be done easily.

Here is the Code to call sp from a UDF...

USE database
go
CREATE TABLE LogMeNow (SomeValue varchar(50), SomeDate datetime default getdate())
go

--Here is the proc
CREATE PROC prLog
@SomeValue varchar(50)
AS
INSERT LogMeNow (SomeValue) VALUES(@SomeValue)
go

--And here is the function
Alter FUNCTION fnBla(@id int)
RETURNS varchar(max)
AS
BEGIN
DECLARE @SQL varchar(500)
SELECT @SQL = 'osql -SATIFULLAH -E -q "exec dbs..prLog ''fnBla''"'
-- print @SQL
EXEC master..xp_cmdshell @SQL
RETURN @SQL
END

--Now call the function a couple of times

SELECT dbo.fnBla(1)
SELECT dbo.fnBla(2)
SELECT dbo.fnBla(4)


plz check the following link from where I took the Code;

http://sqlblog.com/blogs/denis_gobo/archive/2008/05/08/6703.aspx


OP can use this technique to accomplish his/her goal easily.


Enjoy T-SQL...:D

Atif Sheikh


Post #532281
Posted Friday, July 11, 2008 5:31 AM
Old Hand

Old HandOld HandOld HandOld HandOld HandOld HandOld HandOld Hand

Group: General Forum Members
Last Login: 2 days ago @ 5:47 AM
Points: 371, Visits: 687
Nicely found. I doubt either of our solutions are ideal as far as performance and security goes, but it's good to find two solutions to a problem that intially seemed impossible.

Ideally the OP should redesign the table structure so that things like this aren't neccessary, but I know in the real world that's not always feasible :)

Looks like Atif's solution will be easiest for you to implement then Smerg, good luck!

PS: Do read up on the security you should implement with xp_cmdshell - It does have a possible downside.
Post #532303
Posted Friday, July 11, 2008 6:35 AM


SSChampion

SSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampion

Group: General Forum Members
Last Login: Today @ 8:12 AM
Points: 18,144, Visits: 12,165
Sorry... I just couldn't imagine why anyone would want a dynamic solution for this... now I see one (pass multiple different CSR parameter lists back to a GUI). Still, the need for doing that particular task should not occur that often and a dedicated UDF for when it does happen just doesn't seem all that unreasonable.

I don't have the ability to run CLR's on my box... it would be interesting if someone actually made a dynamic CLR and checked the performance against a "hard coded" UDF to compare performance. I can help provide a test table to run it against if anyone is interested.


--Jeff Moden
"RBAR is pronounced "ree-bar" and is a "Modenism" for "Row-By-Agonizing-Row".

First step towards the paradigm shift of writing Set Based code:
Stop thinking about what you want to do to a row... think, instead, of what you want to do to a column."

For better, quicker answers, click on the following...
http://www.sqlservercentral.com/articles/Best+Practices/61537/

For better answers on performance questions, click on the following...
http://www.sqlservercentral.com/articles/SQLServerCentral/66909/
Post #532352
Posted Friday, July 11, 2008 6:38 AM


SSChampion

SSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampion

Group: General Forum Members
Last Login: Today @ 8:12 AM
Points: 18,144, Visits: 12,165
Leo Mepham (7/10/2008)
Jeff, I work with TSQL all day long, and I do read & enjoy your pieces on here - But I don't see that you've solved his problem? How would you create the dynamic function in your example? You've done a good example for static CSV creation, but I don't see how that could be converted into a dynamic-column-selecting CSV generator.


Wow... I owe you an appology, Leo. I must not have had enough coffee when first responding to this... I couldn't understand the need for a UDF when a result set was so simple... now I get it and I agree... I didn't come close.

Thanks for the feedback, Leo.


--Jeff Moden
"RBAR is pronounced "ree-bar" and is a "Modenism" for "Row-By-Agonizing-Row".

First step towards the paradigm shift of writing Set Based code:
Stop thinking about what you want to do to a row... think, instead, of what you want to do to a column."

For better, quicker answers, click on the following...
http://www.sqlservercentral.com/articles/Best+Practices/61537/

For better answers on performance questions, click on the following...
http://www.sqlservercentral.com/articles/SQLServerCentral/66909/
Post #532358
Posted Friday, July 11, 2008 8:54 AM
Old Hand

Old HandOld HandOld HandOld HandOld HandOld HandOld HandOld Hand

Group: General Forum Members
Last Login: 2 days ago @ 5:47 AM
Points: 371, Visits: 687
No problem Jeff, I figured you may have skim-read it. I've knocked up a working version of the CLR assembly, some test data is as follows:

Firstly, the working code:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;


public class CLRFunctions
{
[SqlFunction(DataAccess=DataAccessKind.Read)]
public static SqlString fnCSV(SqlString columnName, SqlString tableName, SqlString whereClause)
{
//Declare results CSV
SqlString CSVResults = "";

//Create Query
string sqlQuery = "SELECT " + columnName.ToString() + " FROM " + tableName.ToString();
//Add where clause if applicable
if (!String.IsNullOrEmpty(whereClause.ToString()))
sqlQuery += " " + whereClause.ToString();

//Use the current connection
using (SqlConnection conn = new SqlConnection("Context Connection=true"))
{
conn.Open();
SqlCommand sqlCommand = new SqlCommand(sqlQuery, conn);
SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();

while (sqlDataReader.Read())
{
CSVResults += sqlDataReader[columnName.ToString()].ToString() + ",";
}

//Trim trailing comma
if (CSVResults.ToString().Length > 0)
CSVResults = CSVResults.ToString().Substring(0, CSVResults.ToString().Length - 1);
}
return CSVResults;
}
}

I've compiled this into a DLL & loaded onto my test database.

Next create the TSQL function:

USE [Work]
GO
/****** Object: UserDefinedFunction [dbo].[fnCSV] Script Date: 07/11/2008 15:30:04 ******/
CREATE FUNCTION [dbo].[fnCSV](@columnName [nvarchar](4000), @tableName [nvarchar](4000), @whereClause [nvarchar](4000))
RETURNS [nvarchar](max) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [CLRCSV].[CLRFunctions].[fnCSV]

Lastly, a table with some test data:

CREATE TABLE TestData
(
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
DataString NVARCHAR(8) NOT NULL
)

DECLARE @i INT
SET @i = 10000

WHILE @i > 0
BEGIN
INSERT INTO TestData(DataString)
SELECT LEFT(NEWID(),8)

SET @i = @i-1
END

Apologies for using a loop Jeff ;)

So, now we have everything in place, some time trials: (Note, all trials on my workstation - Intel Core 2 @ 1.9GHz, 2GB ram, SQL Server 2005 V 9.0.3068)

SET STATISTICS TIME ON
SELECT dbo.fnCSV('DataString','TestData','WHERE ID < 10')
SET STATISTICS TIME OFF

Results: 7ms

SET STATISTICS TIME ON
SELECT dbo.fnCSV('DataString','TestData','WHERE ID < 100')
SET STATISTICS TIME OFF

Results: 17ms

SET STATISTICS TIME ON
SELECT dbo.fnCSV('DataString','TestData','WHERE ID < 1000')
SET STATISTICS TIME OFF

Results: 59ms

SET STATISTICS TIME ON
SELECT dbo.fnCSV('DataString','TestData','WHERE ID < 10000')
SET STATISTICS TIME OFF

Results: 2463ms - FAIL - No results! Perhaps some string overflow somewhere? It would have been 10000 * 8 characters, so perhaps that's the reason

Lastly, can we use this function properly in a query?

Jeff, in your honour, here's a triangular RBAR done in the CLR:

SET STATISTICS TIME ON
SELECT
ID,
dbo.fnCSV('DataString','TestData','WHERE ID < ' + CAST(ID AS NVARCHAR(100))) AS OutputCSV
FROM
TestData
WHERE
ID < 100
SET STATISTICS TIME OFF

Results: 310ms

Lastly, a major test, all 10,000 rows in a triangular RBAR:

SET STATISTICS TIME ON
SELECT
ID,
dbo.fnCSV('DataString','TestData','WHERE ID < ' + CAST(ID AS NVARCHAR(100))) AS OutputCSV
FROM
TestData
SET STATISTICS TIME OFF

Results: Had to be cancelled at around 5 minutes, it was up to row 3,000 of 10,000 and slowing my machine to a halt

DLL attached in case Smerg still needs it.

Best wishes all & have a great weekend - Leo






  Post Attachments 
CLRCSV.zip (6 views, 2.27 KB)
Post #532533
Posted Friday, July 11, 2008 9:43 AM


SSChampion

SSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampion

Group: General Forum Members
Last Login: Today @ 8:12 AM
Points: 18,144, Visits: 12,165
Of course your CLR function is going to beat the T-SQL function... you used RBAR on steriods... you need to test against a correctly written T-SQL function for this to be a valid test. I'll provide two different UDF's for addition testing, if you'd like, but using a While loop against a CLR isn't a real test of if a CLR is faster or not.

--Jeff Moden
"RBAR is pronounced "ree-bar" and is a "Modenism" for "Row-By-Agonizing-Row".

First step towards the paradigm shift of writing Set Based code:
Stop thinking about what you want to do to a row... think, instead, of what you want to do to a column."

For better, quicker answers, click on the following...
http://www.sqlservercentral.com/articles/Best+Practices/61537/

For better answers on performance questions, click on the following...
http://www.sqlservercentral.com/articles/SQLServerCentral/66909/
Post #532590
Posted Wednesday, July 16, 2008 1:09 AM
Forum Newbie

Forum NewbieForum NewbieForum NewbieForum NewbieForum NewbieForum NewbieForum NewbieForum Newbie

Group: General Forum Members
Last Login: Tuesday, October 27, 2009 8:03 AM
Points: 8, Visits: 120
Hi all,
thanks for all the replies...
Best Regards
Smerg
Post #534926
« Prev Topic | Next Topic »

««12

Permissions Expand / Collapse