2

次のテーブルがあるとします。

id name   member
1  jacky  a;b;c
2  jason  e
3  kate   i;j;k
4  alex   null

ここで、sql または t-sql を使用して次のテーブルを返したいと考えています。

1 jacky a
1 jacky b
1 jacky c
2 jason e
3 kate  i
......

どうやってするか?MSSQL、MYSQL、および Oracle データベースを使用しています。

4

3 に答える 3

3

これは、考案できる最短読みやすい文字列から行へのスプリッターであり、さらに高速になる可能性があります。

関数の代わりに純粋なCTEを選択するユースケース。たとえば、データベースで関数を作成することが許可されていない場合:-)

関数(ループまたはCTEを使用して実装することもできます)を介して行ジェネレーターを作成するには、横方向結合を使用する必要があります(DB2およびSybaseには、LATERALキーワードを使用してこの機能があります。SQLServerでは、これはCROSSAPPLYおよびOUTERAPPLYに似ています。 )最終的に、関数によって生成された分割された行をメインテーブルに結合します。

純粋なCTEアプローチは、機能アプローチよりも高速である可能性があります。ただし、速度メトリックはプロファイリングにあります。これが実際に高速である場合は、他のソリューションと比較してこの実行プランを確認してください。

with Pieces(theId, pn, start, stop) AS
(
      SELECT id, 1, 1, charindex(';', member)
      from tbl

      UNION ALL

      SELECT id, pn + 1, stop + 1, charindex(';', member, stop + 1)
      from tbl 
      join pieces on pieces.theId = tbl.id 
      WHERE stop > 0
)
select 

      t.id, t.name, 

      word = 
         substring(t.member, p.start,             
           case WHEN stop > 0 THEN p.stop - p.start 
           ELSE 512 
           END) 

from tbl t
join pieces p on p.theId = t.id
order by t.id, p.pn 

出力:

ID  NAME    WORD
1   jacky   a
1   jacky   b
1   jacky   c
2   jason   e
3   kate    i
3   kate    j
3   kate    k
4   alex    (null)

ここで提供される基本ロジック:T-SQL:文字列の連結の反対-文字列を複数のレコードに分割する方法

ライブテスト:http ://www.sqlfiddle.com/#!3 / 2355d / 1

于 2012-05-08T04:37:23.713 に答える
2

さて...最初に、数字の表について教えてくれたアダム・マハニックを紹介しましょう。彼はまた、このNumbersテーブルを使用して非常に高速な分割関数を作成しました。

http://dataeducation.com/counting-occurrences-of-a-substring-within-a-string/

テーブルを返すSplit関数を実装した後、それに対して結合して、必要な結果を得ることができます。

于 2012-05-08T02:41:58.010 に答える
1
IF OBJECT_ID('dbo.Users') IS NOT NULL 
    DROP TABLE dbo.Users;

CREATE TABLE dbo.Users
(
  id INT IDENTITY NOT NULL PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  member VARCHAR(1000)
)
GO

INSERT INTO dbo.Users(name, member) VALUES
  ('jacky', 'a;b;c'),
  ('jason', 'e'),
  ('kate', 'i;j;k'),
  ('alex', NULL);
GO

DECLARE @spliter CHAR(1) = ';';
WITH Base AS
(
    SELECT  1 AS n
    UNION ALL
    SELECT  n + 1
    FROM    Base
    WHERE   n < CEILING(SQRT(1000)) --generate numbers from 1 to 1000, you may change it to a larger value depending on the member column's length.
)
, Nums AS --Numbers Common Table Expression, if your database version doesn't support it, just create a physical table.
(
    SELECT  ROW_NUMBER() OVER(ORDER BY (SELECT  0)) AS n
    FROM    Base AS B1 CROSS JOIN Base AS B2
)
SELECT  id,
        SUBSTRING(member, n, CHARINDEX(@spliter, member + @spliter, n) - n) AS element
FROM    dbo.Users
    JOIN Nums
    ON n <= DATALENGTH(member) + 1
    AND SUBSTRING(@spliter + member, n, 1) = @spliter
ORDER BY id
OPTION (MAXRECURSION 0); --Nums CTE is generated recursively, we don't want to limit recursion count.
于 2012-05-08T02:59:39.890 に答える