2012-03-16 3 views
7

私は入力に2つの文字列 '1,5,6'と同じ要素数(3またはプラス)で '2,89,9'を持っています。 これらの2列は、私はROWNUMBERを割り当てることだと思いきSQL Server登録注文

1 2 
5 89 
6 9 

として「参加縦」作られたとされ

SELECT a.item, b.item FROM 
    (
    SELECT 
    ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS rownumber, 
    * FROM dbo.Split('1,5,6',',') 
) AS a 
    INNER JOIN 
    (
    SELECT 
    ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS rownumber, 
    * FROM dbo.Split('2,89,9',',') 
) AS b ON a.rownumber = b.rownumber 

として設定2の結果間の結合作らたいとのベストプラクティスこれまでですか?

答えて

13

dbo.Split()がデータセットを返すときは、(文字列内の順序に基づいて)必要なrow_numberを絶対確実に割り当てることはできません。 SQL never は、実際にデータに関連するORDER BYのないの注文を保証します。あなたと

トリックしばしばかもしれあなたによって注文する(SELECT 0)を使用するには、正しい値を取得します。おそらく非常にです。しかし、これはです。保証です。あなたはしばらくお待ちください間違った順序を取得します。

dbo.Split()をコード化して、文字列の解析時にrow_numberを割り当てることをお勧めします。 row_numberが実際にリスト内の項目の位置に対応していることを100%確実に知ることができます。

あなたが提案したとおりに参加し、必要な結果を得ることができます。それ以外


、アイデアは私には罰金思えません。 1つのリストが他のリストよりも長くてもよい場合は、FULL OUTER JOINと考えてもかまいません。

+0

はい、正しく言うと思います。そして、それはベストプラクティスです。しかし、私は特定の "ORDER JOIN"があると思っていました:-) –

+0

はい、似たような場合には、順序付けされた派生テーブルから選択するときに 'SELECT 0'が所望の順序で改ざんされます:http://stackoverflow.com/ q/18961789/521799 –

7

あなたはこのようなあなたのスプリット機能を同様

このようにそれを行う考えてみましょうことができます。

CREATE FUNCTION Split 
(
    @delimited nvarchar(max), 
    @delimiter nvarchar(100) 
) RETURNS @t TABLE 
(
    id int identity(1,1), 
    val nvarchar(max) 
) 
AS 
BEGIN 
    declare @xml xml 
    set @xml = N'<root><r>' + replace(@delimited,@delimiter,'</r><r>') + '</r></root>' 

    insert into @t(val) 
    select 
    r.value('.','varchar(5)') as item 
    from @xml.nodes('//root/r') as records(r) 

    RETURN 
END 
GO 

ザ・それは一緒にJOINそれらを単純な作業になります。このように:

SELECT 
    * 
FROM 
    dbo.Split('1,5,6',',') AS a 
    JOIN dbo.Split('2,89,9',',') AS b 
     ON a.id=b.id 

これの利点は、パフォーマンスが再帰的な分割機能を備えた優れているコメントのように任意のROW_NUMBER() OVER(ORDER BY SELECT 0)

編集

を必要としないことです。そのため、おそらくこのような何か:

CREATE FUNCTION dbo.Split (@s varchar(512),@sep char(1)) 
RETURNS table 
AS 
RETURN (
    WITH Pieces(pn, start, stop) AS (
     SELECT 1, 1, CHARINDEX(@sep, @s) 
     UNION ALL 
     SELECT pn + 1, stop + 1, CHARINDEX(@sep, @s, stop + 1) 
     FROM Pieces 
     WHERE stop > 0 
    ) 
    SELECT pn, 
     SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s 
    FROM Pieces 
) 
GO 

そして選択は、このようなものです:アリオンの提案に

SELECT 
    * 
FROM 
    dbo.Split('1,5,6',',') AS a 
    JOIN dbo.Split('2,89,9',',') AS b 
     ON a.pn=b.pn 
+0

良い! ;)ありがとう –

+1

いいえ問題ありません..喜んで – Arion

+1

@Arion - あなたは確かにこのアプローチは、***レコード***が挿入される順序を保証していますか?私の理解は、parrallel処理などと同じ考慮事項が適用されます:ORDER BYを指定していないので、データ*は*表示される順序とは異なる順序で挿入されます文字列にこのような保証がある参照がある場合は、文字列を繰り返し処理し、明示的にID値を作成するこの方法が好きなので、私はそれらを見たいと思っています。私はちょうどそれを保証します*** *** :) – MatBailie

0

感謝。それは私のために非常に便利です。私は、入力文字列のvarchar(max)型をサポートするために関数を少し変更し、区切り文字列の最大長は1000にしました。また、最後のリターンで空文字列が必要かどうかを示すパラメータを追加しました。

MatBailieの質問の場合、これはインライン関数なので、この関数を呼び出す外側のクエリにpnカラムを含めることができます。

CREATE FUNCTION dbo.Split (@s nvarchar(max),@sep nvarchar(1000), @IncludeEmpty bit) 
RETURNS table 
AS 
RETURN (
    WITH Pieces(pn, start, stop) AS (
     SELECT convert(bigint, 1) , convert(bigint, 1), convert(bigint,CHARINDEX(@sep, @s)) 
     UNION ALL 
     SELECT pn + 1, stop + LEN(@sep), CHARINDEX(@sep, @s, stop + LEN(@sep)) 
     FROM Pieces 
     WHERE stop > 0 
    ) 
    SELECT pn, 
     SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE LEN(@s) END) AS s 
    FROM Pieces 
    where start< CASE WHEN stop > 0 THEN stop ELSE LEN(@s) END + @IncludeEmpty 
) 

しかし、返す予定のリストに100を超えるレコードがある場合は、この関数で少し問題が発生しました。だから、純粋に文字列解析関数を使用して別の関数を作成しました。

Create function [dbo].[udf_split] (
    @ListString nvarchar(max), 
    @Delimiter nvarchar(1000), 
    @IncludeEmpty bit) 
Returns @ListTable TABLE (ID int, ListValue varchar(max)) 
AS 
BEGIN 
    Declare @CurrentPosition int, @NextPosition int, @Item nvarchar(max), @ID int 
    Select @ID = 1, 
      @ListString = @Delimiter+ @ListString + @Delimiter, 
      @CurrentPosition = 1+LEN(@Delimiter) 

    Select @NextPosition = Charindex(@Delimiter, @ListString, @CurrentPosition) 
    While @NextPosition > 0 Begin 

     Select @Item = Substring(@ListString, @CurrentPosition, @[email protected]) 
     If  @IncludeEmpty=1 or Len(LTrim(RTrim(@Item)))>0 Begin 
       Insert Into @ListTable (ID, ListValue) Values (@ID, LTrim(RTrim(@Item))) 
       Set @ID = @ID+1 
     End 
     Select @CurrentPosition = @NextPosition+LEN(@Delimiter), 
       @NextPosition = Charindex(@Delimiter, @ListString, @CurrentPosition) 
    End 
    RETURN 
END 

希望します。

関連する問題