2013-02-16 53 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