2011-10-24 59 views
0

我有一个表,看起来像这样:拆分逗号分隔值到不同的行

id fk_det userid 
3 9 name1,name2 
6 1 name3 
9 2 name4,name5 
12 3 name6,name7 

我已经学会了后悔逗号分隔值的用户ID的价值,所以我想拆行并最终的东西,看起来像

id fk_det userid 
3 9 name1 
x 9 name2 
6 1 name3 
9 2 name4 
x 2 name5 
12 3 name6 
x 3 name7 

我一直在寻找这样的事情:

select fk_det, det, LEFT(userid, CHARINDEX(',',userid+',')-1), 
    STUFF(userid, 1, CHARINDEX(',',userid+','), '') 
from global_permissions 

但我不知道如何使其工作当userid包含超过2项(它可能,有些可能没有,有些可能有多个,只是取决于)

+1

SQL的什么味道? SQLServer的? MySQL的? – MPelletier

+1

对不起,MS SQL Server – Josh

+0

可能重复[如何将逗号分隔值扩展为使用SQL Server 2005的单独行?](http://stackoverflow.com/questions/702968/how-do-i-expand-comma-使用sql-server-2005) – MPelletier

回答

1

这是我倾向于使用:

IF EXISTS (
    SELECT 1 
    FROM dbo.sysobjects 
    WHERE id = object_id(N'[dbo].[ParseString]') 
     AND xtype in (N'FN', N'IF', N'TF')) 
BEGIN 
    DROP FUNCTION [dbo].[ParseString] 
END 
GO 

CREATE FUNCTION dbo.ParseString (@String VARCHAR(8000), @Delimiter VARCHAR(10)) 
RETURNS TABLE 
AS 
/******************************************************************************************************* 
* dbo.ParseString 
* 
* Creator:  magicmike 
* Date:   9/12/2006 
* 
* 
* Outline:  A set-based string tokenizer 
*     Takes a string that is delimited by another string (of one or more characters), 
*     parses it out into tokens and returns the tokens in table format. Leading 
*     and trailing spaces in each token are removed, and empty tokens are thrown 
*     away. 
* 
* 
* Usage examples/test cases: 
       Single-byte delimiter: 
        select * from dbo.ParseString2('|HDI|TR|YUM|||', '|') 
        select * from dbo.ParseString2('HDI| || TR |YUM', '|') 
        select * from dbo.ParseString2(' HDI| || S P A C E S |YUM | ', '|') 
        select * from dbo.ParseString2('HDI|||TR|YUM', '|') 
        select * from dbo.ParseString2('', '|') 
        select * from dbo.ParseString2('YUM', '|') 
        select * from dbo.ParseString2('||||', '|') 
        select * from dbo.ParseString2('HDI TR YUM', ' ') 
        select * from dbo.ParseString2(' HDI| || S P A C E S |YUM | ', ' ') order by Ident 
        select * from dbo.ParseString2(' HDI| || S P A C E S |YUM | ', ' ') order by StringValue 

       Multi-byte delimiter: 
        select * from dbo.ParseString2('HDI and TR', 'and') 
        select * from dbo.ParseString2('Pebbles and Bamm Bamm', 'and') 
        select * from dbo.ParseString2('Pebbles and sandbars', 'and') 
        select * from dbo.ParseString2('Pebbles and sandbars', ' and ') 
        select * from dbo.ParseString2('Pebbles and sand', 'and') 
        select * from dbo.ParseString2('Pebbles and sand', ' and ') 
* 
* 
* Notes: 
        1. A delimiter is optional. If a blank delimiter is given, each byte is returned in it's own row (including spaces). 
         select * from dbo.ParseString3('|HDI|TR|YUM|||', '') 
        2. In order to maintain compatibility with SQL 2000, ident is not sequential but can still be used in an order clause 
        If you are running on SQL2005 or later 
         SELECT Ident, StringValue FROM 
        with 
         SELECT Ident = ROW_NUMBER() OVER (ORDER BY ident), StringValue FROM 
* 
* 
* Modifications 
* 
* 
********************************************************************************************************/ 
RETURN (
SELECT Ident, StringValue FROM 
    (
     SELECT Num as Ident, 
      CASE 
       WHEN DATALENGTH(@delimiter) = 0 or @delimiter IS NULL 
        THEN LTRIM(SUBSTRING(@string, num, 1)) --replace this line with '' if you prefer it to return nothing when no delimiter is supplied. Remove LTRIM if you want to return spaces when no delimiter is supplied 
      ELSE 
       LTRIM(RTRIM(SUBSTRING(@String, 
        CASE 
         WHEN (Num = 1 AND SUBSTRING(@String,num ,DATALENGTH(@delimiter)) <> @delimiter) THEN 1 
         ELSE Num + DATALENGTH(@delimiter) 
        END, 
        CASE CHARINDEX(@Delimiter, @String, Num + DATALENGTH(@delimiter)) 
         WHEN 0 THEN LEN(@String) - Num + DATALENGTH(@delimiter) 
         ELSE CHARINDEX(@Delimiter, @String, Num + DATALENGTH(@delimiter)) - Num - 
          CASE 
           WHEN Num > 1 OR (Num = 1 AND SUBSTRING(@String,num ,DATALENGTH(@delimiter)) = @delimiter) 
             THEN DATALENGTH(@delimiter) 
           ELSE 0 
          END 
         END 
        ))) 
       End AS StringValue 
     FROM dbo.Numbers 
     WHERE Num <= LEN(@String) 
      AND (
        SUBSTRING(@String, Num, DATALENGTH(ISNULL(@delimiter,''))) = @Delimiter 
        OR Num = 1 
        OR DATALENGTH(ISNULL(@delimiter,'')) = 0 
       ) 
    ) R WHERE StringValue <> '' 
) 

你会使用这样的:

SELECT id, pk_det, V.StringValue as userid 
FROM myTable T 
OUTER APPLY dbo.ParseString(T.userId) V 

的UDF需要一个 '符合' 或数表,假设下面的模式:

IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Numbers') 
BEGIN 

    CREATE TABLE dbo.Numbers 
    (
     Num INT NOT NULL 
     CONSTRAINT [PKC__Numbers__Num] PRIMARY KEY CLUSTERED (Num) on [PRIMARY] 
    ) 
    ;WITH Nbrs_3(n) AS (SELECT 1 UNION SELECT 0), 
      Nbrs_2(n) AS (SELECT 1 FROM Nbrs_3 n1 CROSS JOIN Nbrs_3 n2), 
      Nbrs_1(n) AS (SELECT 1 FROM Nbrs_2 n1 CROSS JOIN Nbrs_2 n2), 
      Nbrs_0(n) AS (SELECT 1 FROM Nbrs_1 n1 CROSS JOIN Nbrs_1 n2), 
      Nbrs (n) AS (SELECT 1 FROM Nbrs_0 n1 CROSS JOIN Nbrs_0 n2) 

    INSERT INTO dbo.Numbers(Num) 
    SELECT n 
    FROM (SELECT ROW_NUMBER() OVER (ORDER BY n) 
      FROM Nbrs) D (n) 
     WHERE n <= 50000 ; 
END 

数字表格是您的工具集中非常有价值的补充。引用Adam Machanic:

数字表真的非常宝贵。我一直都在使用它们来处理字符串操作,模拟窗口函数,填充含有大量数据的测试表 ,消除了游标逻辑,还有许多其他 任务,如果没有它们,这将非常困难。

正在使用一个数字表格,因为我见过一些人声称? 编号向我展示另一种方式来高效地完成数字 表中的所有内容。它浪费空间吗?不。下面的脚本将在每个数据库中使用大约900 KB的磁盘空间大约 。这绝对不是 什么都没有。您最终将获得数百万甚至数十亿次的磁盘空间投资回报,因为易于开发和时间节省 。

http://sqlblog.com/blogs/adam_machanic/archive/2006/07/12/you-require-a-numbers-table.aspx

0

作为替代标准的存储过程调用你看到无处不在:

with temp as(
select id,fk_det,cast('<comma>'+replace(userid,',','</comma><comma>')+'</comma>' as XMLcomma 
from global_permissions 
) 

select id,fk_det,a.value('comma[1]','varchar(512)') 
cross apply temp.XMLcomma.nodes('/comma') t(a) 
1

试试这个:)

DECLARE @Name TABLE 
    (
     id INT NULL , 
     fk_det INT NULL , 
     userid NVARCHAR(100) NULL 
    ) 

INSERT INTO @Name 
         (id, fk_det, userid) 
VALUES  (3,9,'name1,name2' ) 

INSERT INTO @Name 
         (id, fk_det, userid) 
VALUES  (6,1,'name3' ) 

INSERT INTO @Name 
         (id, fk_det, userid) 
VALUES  (9,2,'name4,name5' ) 

INSERT INTO @Name 
         (id, fk_det, userid) 
VALUES  (12,3,'name6,name7' ) 

SELECT * 
FROM @Name 

SELECT id,A.fk_det, 
    Split.a.value('.', 'VARCHAR(100)') AS String 
FROM (SELECT id,fk_det, 
     CAST ('<M>' + REPLACE(userid, ',', '</M><M>') + '</M>' AS XML) AS String 
    FROM @Name) AS A CROSS APPLY String.nodes ('/M') AS Split(a); 
+0

简单而实用。有些情况下,需要TRIM函数,因为逗号值往往被“,”等分隔开来。 –