2011-10-24 23 views
0

個別の行に値をカンマ区切りスプリット:私はすでにカンマ区切りでユーザーIDの値を持つ後悔することを学んだ私はこのようになりますテーブル持って

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

をので、私は、行を分割したいですアップと私はこのような何かを見ていた

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の何味? SQLサーバー? 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を引用するには:

数値表は本当に貴重です。私は多くのデータを持つテーブル のテーブルに、カーソルのロジックを排除して、多くの他の多くの タスクをシミュレートするために、それらを使わずに非常に難しい作業をすべて 文字列操作に使用します。

私はいくつかの人々が主張したように、数字のテーブルを使っていますか? いいえ、私は効率的に数字のすべてを行う別の方法を表示 テーブルができます。それはスペースを無駄にしますか?いいえ。以下のスクリプトでは、各データベースに約012 KBのディスク容量を使用しています( )。それは絶対に です。数百万回、おそらく数十億回という結果に終わります。 ディスクスペースの投資は、開発の容易さと時間の面では を保存しました。標準SPROCに代わるものとして

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関数が必要になります。 –

関連する問題