2011-03-07 46 views
0

mytable的結構:ID INT,lookuptablename VARCHAR傳遞動態表名SQL函數返回逗號分隔的字符串

1, 'lookuptable1' 
2, 'lookuptable2' 

lookuptable1:ID INT,項目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 

我一直在苦苦尋找出路以各種方式來實現這一點,但只是無法弄清楚。

+0

你想把ID和ITEM分成單個字符串,用逗號分隔和代字號嗎? – RichardTheKiwi 2011-03-07 20:46:05

+0

你不能用動態表名和UDF來做到這一點。存儲過程,是的,但是存儲過程是一個選項? – RichardTheKiwi 2011-03-07 20:50:10

+0

http://explainextended.com/2010/06/21/group_concat-in-sql-server/ – 2011-03-07 20:51:13

回答

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 
0

爲了後代的緣故,動態SQL一般應該是parameterizea ble(即使這裏沒有必要)並通過sp_executesql執行。在EXEC與sp_executesql的全部細節可以在此(優秀)的文章中找到:

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

結合厄蘭的建議誤差與他寫的動態存儲過程的建議處理,產生類似如下的模板:

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