2013-02-16 57 views
2

Table1,像這樣的列:Split函數2008

+--+------+ 
|ID|Name | 
+--+------+ 
|1 |MSSQL | 
+--+------+ 
|2 |MySQl | 
+--+------+ 
|3 |Oracle| 
+--+------+ 

Table2,我有這樣

+------------+ 
|Databasename| 
+------------+ 
|1,3   | 
+------------+ 
|2   | 
+------------+ 
|1,2   | 
+------------+ 

我的輸出應該是一個列:

+------------+ 
|Databasename| 
+------------+ 
|MSSQL,Oracle| 
+------------+ 
|MySQL  | 
+------------+ 
|MSSQL,MYSQL | 
+------------+ 

我如何得到這個,我需要查詢這個..

+2

在數據庫中存儲逗號分隔的名單是不是一個好的做法。對此的查詢將非常可怕。 – 2013-02-16 14:18:07

+1

搜索'SQL中的分割函數'你會得到很多。 – Kaf 2013-02-16 14:21:35

+0

爲什麼人們一直這樣做?這與使用姓氏作爲唯一鍵一樣是一個基本的錯誤。 – 2013-10-26 10:58:58

回答

5

首先,你最好的解決方案是不將數據存儲在數據庫中的一個逗號分隔的列表。您應該考慮修復表結構。

如果你不能改變表結構,那麼你將需要在列表中的數據拆分行指定正確的名稱。數據分割後,您可以將數據連接回列表中。

有許多不同的split功能,你可以在網上卻發現這裏是一個版本,我通常使用:和

CREATE FUNCTION [dbo].[Split](@String varchar(MAX), @Delimiter char(1))  
returns @temptable TABLE (items varchar(MAX))  
as  
begin  
    declare @idx int  
    declare @slice varchar(8000)  

    select @idx = 1  
     if len(@String)<1 or @String is null return  

    while @idx!= 0  
    begin  
     set @idx = charindex(@Delimiter,@String)  
     if @idx!=0  
      set @slice = left(@String,@idx - 1)  
     else  
      set @slice = @String  

     if(len(@slice)>0) 
      insert into @temptable(Items) values(@slice)  

     set @String = right(@String,len(@String) - @idx)  
     if len(@String) = 0 break  
    end 
return 
end; 

爲了讓您的結果,我會通過應用split函數開始,因爲我一個row_number()沒有看到與每一行關聯的唯一密鑰。如果您對各行的唯一鍵,那麼你將不需要0​​:

;with cte as 
(
    select rn, name, id 
    from 
    (
    select row_number() over(order by (select 1)) rn, 
     databasename 
    from table2 
) t2 
    cross apply dbo.split(t2.databasename, ',') i 
    inner join table1 t1 
    on i.items = t1.id 
) 
select * 
from cte 

此查詢傷了你的逗號分隔的列表分爲以下幾個:

| RN | NAME | ID | 
-------------------- 
| 1 | MSSQL | 1 | 
| 1 | Oracle | 3 | 
| 2 | MySQl | 2 | 
| 3 | MSSQL | 1 | 
| 3 | MySQl | 2 | 

一旦你在多個數據用正確的name行,那麼你可以使用STUFF()FOR XML PATH將其連接成列表。您的完整查詢將與此類似:

;with cte as 
(
    select rn, name, id 
    from 
    (
    select row_number() over(order by (select 1)) rn, 
     databasename 
    from table2 
) t2 
    cross apply dbo.split(t2.databasename, ',') i 
    inner join table1 t1 
    on i.items = t1.id 
) 
select 
    STUFF(
     (SELECT ', ' + c2.name 
      FROM cte c2 
      where c1.rn = c2.rn 
      order by c2.id 
      FOR XML PATH ('')) 
      , 1, 1, '') Databasename 
from cte c1 
group by c1.rn 
order by c1.rn; 

請參閱SQL Fiddle with Demo

完整查詢的結果是:

| DATABASENAME | 
------------------ 
| MSSQL, Oracle | 
|   MySQl | 
| MSSQL, MySQl | 
6

您所要求的分裂功能,但你不必拆你的價值得到你想要的結果。

此查詢使用for xml技巧在相關子查詢中構建逗號分隔名稱列表來連接值。它採用like找出值從Table1Table2每行的。

select (
     select ', '+T1.Name 
     from Table1 as T1 
     where ','+T2.Databasename+',' like '%,'+cast(T1.ID as varchar(10))+',%' 
     for xml path(''), type 
     ).value('substring(text()[1], 3)', 'varchar(max)') as Databasenames 
from Table2 as T2 

SQL Fiddle

+2

+5爲優秀的解決方案;) – 2013-03-05 11:47:45

0

沒有分裂,也沒有XML的路徑,但達到正確的結果。

;with cte as (
    select *, cast(null as varchar(1024)) as str, cast(0 as int) as ID 
    from Table2 

    union all 

    select DatabaseName, (case when DatabaseName like cast(t.ID as varchar(32)) + ',%' 
            or DatabaseName like '%,' + cast(t.ID as varchar(32)) + ',%' 
            or DatabaseName like '%,' + cast(t.ID as varchar(32)) 
            or DatabaseName = cast(t.ID as varchar(32)) then cast(isnull(str, '') + ',' + t.Name as varchar(1024)) else str end), cte.ID + 1 as ID 
    from cte 
    inner join Table1 t on cte.ID + 1 = t.ID 
) 
select DatabaseName, (case when str like ',%' then substring(str, 2, len(str)) else null end) as str 
from cte c 
where ID = (select max(ID) from cte where DatabaseName = c.DatabaseName) 
0
--Here it goes: 

---------------- 
-- FieldCount -- 
---------------- 
CREATE FUNCTION [dbo].[FieldCount](@S VARCHAR(8000), @Separator VARCHAR(10)) 
    RETURNS INT 
AS 

BEGIN 

    /* 
    @Author: Leonardo Augusto Rezende Santos 
    @Contact: http://www.linkedin.com/pub/leonardo-santos/0/2b1/890 
    */ 

    DECLARE @Ptr INT, @p INT, @LenS INT, @LenSep INT, @Result INT 

    IF @Separator = ' ' 
    BEGIN 
     SET @S = REPLACE(@S, ' ', '|-|') 
     SET @Separator = '|-|' 
    END 

    WHILE CHARINDEX(@Separator + @Separator, @S) > 0 
    SET @S = Replace(@S, @Separator + @Separator, @Separator + '_-_' + @Separator) 
    IF @S <> '' 
    SET @Result = 1 
    ELSE 
    BEGIN 
     SET @Result = 0 
     RETURN(@Result) 
    END 
    SET @Ptr = 0 
    SET @LenS = LEN(@S) 
    SET @LenSep = LEN(@Separator) 
    SET @p = CHARINDEX(@Separator, @S) 
    WHILE @p > 0 
    BEGIN 
     SET @Result = @Result + 1 
     SET @Ptr = @Ptr + @p + @LenSep 
     SET @p = CHARINDEX(@Separator, SUBSTRING(@S, @Ptr, @LenS - @Ptr + 1)) 
    END 

    RETURN(@Result) 

END 

-------------- 
-- GetField -- 
-------------- 
CREATE FUNCTION [dbo].[GetField](@S VARCHAR(8000), @Separator VARCHAR(10), @Field INT) 
    RETURNS VARCHAR(8000) 
AS 

BEGIN 

    /* 
    @Author: Leonardo Augusto Rezende Santos 
    @Contact: http://www.linkedin.com/pub/leonardo-santos/0/2b1/890 
    */ 

    DECLARE @Ptr INT, @p INT, @LenS INT, @LenSep INT, @Fld INT, @Result VARCHAR(8000) 

    IF @Separator = ' ' 
    BEGIN 
     SET @S = REPLACE(@S, ' ', '|-|') 
     SET @Separator = '|-|' 
    END 

    IF @Field > dbo.FieldCount(@S, @Separator) 
    BEGIN 
     SET @Result = '' 
     RETURN(@Result) 
    END 
    SET @Fld = 1 
    SET @Ptr = 1 
    SET @LenS = LEN(@S) 
    SET @LenSep = LEN(@Separator) 
    SET @p = CHARINDEX(@Separator, @S) 
    WHILE (@p > 0) and (@Fld < @Field) 
    BEGIN 
     SET @Fld = @Fld + 1 
     SET @Ptr = @Ptr + @p + @LenSep - 1 
     SET @p = CHARINDEX(@Separator, SUBSTRING(@S, @Ptr, @LenS - @Ptr + 1)) 
    END 
    IF (@p = 0) and (@Fld = @Field) 
    SET @p = @LenS - @Ptr + 2 
    SET @Result = SUBSTRING(@S, @Ptr, @p - 1) 

    RETURN(@Result) 

END 

/* USAGE*/ 

select dbo.FieldCount('A1 A2 A3 A4 A5', ' ') 

--It will return 5 

select dbo.GetField('A1 A2 A3 A4 A5', ' ', 3) 

--It will return 'A3' 

select dbo.GetField('A1/A2/A3/A4/A5', '/', 3) 

--It will return 'A3' 

--Hope it works for you. 

--Leonardo Augusto