将动态表名传递给SQL函数并返回逗号分隔的字符串

时间:2011-03-07 20:35:19

标签: sql sql-server-2008 dynamic concatenation

mytable结构:id int,lookuptablename varchar

1, 'lookuptable1'
2, 'lookuptable2'

lookuptable1 :id int,item varchar

1, 'item1 from lkt1'
2, 'item2 from lkt1'

lookuptable2 :id int,item varchar

1, 'item1 from lkt2'
2, 'item2 from lkt2'

查询:

SELECT GetDelimitedList(lookuptablename) FROM mytable;

预期结果:

1,2~item1 from lkt1,item2 from lkt1
1,2~item1 from lkt2,item2 from lkt2

我一直在努力寻找以各种方式实现这一目标的方法,但却无法理解。

2 个答案:

答案 0 :(得分:0)

解决方法。 UDF不起作用。可能是CLR,但不是本机SQL UDF。

首先创建此proc,将表格合并为一行

create proc dbo.GetDelimitedList
@tablename sysname
AS
declare @sql nvarchar(max)
set @sql = '
declare @id nvarchar(max), @item nvarchar(max)
select
    @id = isnull(@id+'','','''') + convert(varchar,id),
    @item = isnull(@item+'','','''') + item
from ' + quotename(@tablename) + '
select @id + ''~'' + @item'
exec (@sql)
GO

然后使用此SQL批处理生成相当于SELECT GetDelimitedList(tablename) FROM mytable;

的输出
declare @tmp table(id int, mashed nvarchar(max))
declare @id int, @tablename sysname
-- start at first table
select top 1 @id = id, @tablename = tablename
from mytable
order by id asc
while @@ROWCOUNT > 0
begin
    -- fill our temp table
    insert @tmp (mashed) exec GetDelimitedList 'mytablelist'
    update @tmp set id = @id where id is null
    -- next table
    select top 1 @id=id, @tablename = tablename
    from mytable
    where id > @id
    order by id asc
end
SELECT * FROM @tmp;

使用的样本表:

create table mytable(id int, tablename varchar(100))
insert mytable values (1, 'mytablelist')
insert mytable values (3, 'mytablelist2')

create table mytablelist(id int, item varchar(100))
insert mytablelist values
(1, 'item1'),
(2, 'item2')
GO

create table mytablelist2(id int, item varchar(100))
insert mytablelist2 values
(11, 't2item1'),
(22, 't2item2')
GO

答案 1 :(得分:0)

为了后人的缘故,动态Sql通常应该是可参数化的(即使这里没有必要)并通过sp_executesql执行。有关EXEC与sp_executesql的完整详细信息,请参阅此(优秀)文章:

http://www.sommarskog.se/dynamic_sql.html

将Erland的错误处理建议与他编写动态存储过程的建议相结合,产生类似于以下内容的模板:

CREATE PROCEDURE [dbo].[prc_<>]
(
    @isDebug BIT = 0
)
AS
BEGIN TRY

    SET NOCOUNT ON 
    SET XACT_ABORT ON 

    DECLARE
        @nvQry NVARCHAR(MAX)        = NULL,
        @nvParams NVARCHAR(MAX) = NULL,
        @n NVARCHAR(MAX)                = NCHAR(13) + NCHAR(10)

    -- %% Setup query here %% --

    IF @isDebug = 1
    BEGIN
        PRINT N'DECLARE ' + @n + @nvParams + @n + @n
        PRINT @nvQry
    END
    ELSE
    BEGIN
        EXEC sp_executesql @nvQry, @nvParams, 
    END 

    RETURN 0 --No Error

END TRY
BEGIN CATCH
    -- Indicate that we want to rollback entire transaction stack, 
    --  (all BEGIN TRANSACTION calls increment @@TRANCOUNT and ROLLBACK
    --      TRANSACTION returns @@TRANCOUNT to zero)
    IF @@TRANCOUNT > 0
    BEGIN
        -- Justified here: http://www.sommarskog.se/error-handling-II.html#rollbackornot
        ROLLBACK TRANSACTION
    END

    -- Augement error message and re-raise
  EXECUTE [dbo].[prc_ErrorHandler]

    -- In case this is only a statement-termination
    RETURN -1 -- Arbitrary error return value

END CATCH
GO

在问题的基础上,并以@ RichardTheKiwi的答案为基础,我们最终得到了一个与上面的函数完全相同的程序,它可以调试一点:

CREATE PROCEDURE [dbo].[GetDelimitedList]
(
    @tablename NVARCHAR(128),
    @isDebug BIT = 0
)
AS
BEGIN TRY

    SET NOCOUNT ON 
    SET XACT_ABORT ON 

    DECLARE
        @nvQry NVARCHAR(MAX)        = NULL,
        @nvParams NVARCHAR(MAX) = NULL,
        @n NVARCHAR(MAX)                = NCHAR(13) + NCHAR(10)

    -- %% Setup query here %% --
    SELECT
         @nvQry = 
            N'DECLARE ' + @n +
            N'  @id NVARCHAR(MAX), ' + @n +
            N'  @item NVARCHAR(MAX) ' + @n +
            N' ' + @n +
            N'SELECT ' + @n +
            N'  @id = ISNULL(@id + '','', '''') + CONVERT(VARCHAR, [id]), ' + @n +
            N'  @item = ISNULL(@item + '','', '''') + [item] ' + @n +
            N'FROM ' + QUOTENAME(@tablename) + @n +
            N' ' + @n +
            N'SELECT @id + ''~'' + @item'

    IF @isDebug = 1
    BEGIN
        PRINT @nvQry
    END
    ELSE
    BEGIN
        EXEC sp_executesql @nvQry
    END 

    RETURN 0 --No Error

END TRY
BEGIN CATCH
    -- Indicate that we want to rollback entire transaction stack, 
    --  (all BEGIN TRANSACTION calls increment @@TRANCOUNT and ROLLBACK
    --      TRANSACTION returns @@TRANCOUNT to zero)
    IF @@TRANCOUNT > 0
    BEGIN
        -- Justified here: http://www.sommarskog.se/error-handling-II.html#rollbackornot
        ROLLBACK TRANSACTION
    END

    -- Augement error message and re-raise
  EXECUTE [dbo].[prc_ErrorHandler]

    -- In case this is only a statement-termination
    RETURN -1 -- Arbitrary error return value

END CATCH
GO