2016-12-15 4 views
1

区切られた文字列内の特定の場所にのみ値を持つレコードを含める必要があります。たとえば、以下の文字列では、column5にデータがあるものだけを含めたいと思います。だから、唯一の例Bは資格:デリミタ付き列内のすべてのデータのSQLフィルタ:

例A:PV1|column1data||column3data|column4data||||column8data

例B:PV1|column1data||column3data||column5data|||column8data

+3

どのDBMSを使用していますか? Postgres?オラクル? –

+1

このデータが最初に適切なテーブルにロードされないのはなぜですか?なぜ文字列を格納するのですか?これに影響を与えますか? –

+0

いいえ、私はこれ以上の影響はありません。 –

答えて

1

常にあなたのデータもし持って9つの項目と8枚のセパレータの同じ形式:

select * from tab where col not like '%|%|%|%|%||%|%|%' 

あなたが使用していることを私たちに知らせるならば、あなたのRDBMSでもっと良いオプションがあるかもしれません。また、複数の項目を1つの列に格納することは、リレーショナルデータベースで最も悪い反パターンの1つです。ここで

select * 
from tab 
where col not like replicate('%|', 5) + replicate('|%', len(col) - len(replace(col, '|', '')) - 5) 

私たちがしている:ギザギザデータ

データ内の要素の数が不明の場合、これは、SQLサーバーに仕事ができるため

更新(空白やANSIの設定に応じて)各値の要素数を計算し、上記のように動作するように動的にlikeパターンを作成します。他のデータベースは、同じロジックを可能にする同様の機能を持つ必要があります。私は答えがより多くの情報を持たずにどれくらい良くなるか分かりません。

+0

これは実際にはデータベースではありません。これはインタフェーストランザクションのクエリです。私は、特定のフィールドにデータを持つトランザクションだけをフィルタリングしようとしています。項目とデリミタの数は固定されていません。 –

+0

@LeslieHarrisonあなたはあなたが使用しているシステムを含めるために質問を展開し、この異種フォーマットのデータの例をさらに含めるべきです。 –

0

LIKEオペレータの処理速度が遅く、数十万レコードに達すると視覚的に遅くなるものになります。この回答はすばらしいものではありませんが、SQL Server CLRは必要ありません。選択したシステムがOracleの場合、同じ構文を使用するには11gが必要です。

あなたはどんな将来の変更のため、だけでなく、スクリプトの柔軟性のために、このための変数をお勧めします...あなたがテーブルを持っている

create table udata (
    ID int primary key identity(1,1) 
    , string varchar(2000) not null 
); 

を仮定。

declare @delimiter varchar(1) = '|' 

次に、すべてをCTEに設定します。

;with parser as (
    select 
      ID 
     , 0 as colNum 
     , substring(d.string, endPos + (2 * delimLen), len(d.string)) as string 
     , startPos 
     , endPos 
    from udata d 
     cross apply (
      select 
        len(@delimiter) as startPos 
       , case charindex(@delimiter,d.string) when 0 then len(d.string) + len(@delimiter) else charindex(@delimiter,d.string) end - len(@delimiter) as endPos 
       , len(@delimiter) as delimLen 
     ) p 
    where id between 2000 and 10000 
    union all 
    select 
      ID 
     , colNum + 1 as colNum 
     , substring(d.string, p.endPos + (2 * delimLen), len(d.string)) as string 
     , d.endPos + (2 * delimLen) as startPos 
     , d.endPos + (delimLen) + p.endPos as endPos 
    from parser d 
     cross apply (
      select 
        len(@delimiter) as startPos 
       , case charindex(@delimiter,d.string) when 0 then len(d.string) + len(@delimiter) else charindex(@delimiter,d.string) end - len(@delimiter) as endPos 
       , len(@delimiter) as delimLen 
     ) p 
    where string != '' 
), selector as (
    select u.id, p.colNum, substring(u.string, p.startPos, p.endPos - p.startPos + len(@delimiter)) as colVal--,u.string, p.startPos, p.endPos 
    from udata u 
     inner join parser p 
      on p.ID = u.ID 
) 

は、これがないと、ソース文字列のうち、セレクタスライスを、開始し、各列の値の終わりの位置をマークする最初のものです。再帰クエリの最初の部分のwhere句に注意してください。where id between 2000 and 10000ここでレコードを制限します。これは、探しているレコードの種類に制限する場合があります。

最後に、読みやすくするためのピボットであなたの列を選択します。

select * 
from selector 
    pivot (
     max(colVal) for colNum in ([1],[2],[3],[4],[5],[6],[7],[8]) 
    ) pv 

元の行ではなく、このようなあなたのオリジナルの基準を使用して返すことができます。

select * 
from udata u 
where exists (
     select top 1 1 
     from selector s 
     where s.colNum = 5 
      and s.colVal ='' 
      and s.ID = u.ID 
    ) 

私のテストデータが含まれています〜160Kの行があり、CTEのID制限のない既存のクエリは、ラップトップハードウェアで実行するのに18秒かかりました。それでも、これはトリックを行う必要があります。

関連する問題