Firebirdストアドプロシージャ:区切り文字で行をレコードセットに分割する
これは、Firebird PSQLで開発するすべての開発者が遅かれ早かれ直面するタスクの1つです - デリミタを含む行を個別の値の配列に分割する方法は?次のようなものです:
select * from sp_split_into_words('foo rio bar firebird,mysql,postgresql;mssql;redis/rty');
出力:
######
WORD
==========
foo
rio
bar
firebird
mysql
postgresql
mssql
redis
rty
純粋なSQLでこれを行うことは可能です - 以下のストアドプロシージャコードを使用してください。作者はPavel Zotov氏、Firebird QAエンジニアでありIBSurgeonのリードDBAです。
このプロシージャにはスペースを含む広範なデリミタのリストが含まれていることに注意してください。カンマや二重ドットのみが必要な場合は、変数a_delから他のデリミタを削除できます。
また、戻り値のvarcharのサイズ(戻り変数word)に注意してください - デフォルトではvarchar(50)ですが、必要に応じて調整できます。
set term ^;
create or alter procedure sp_split_into_words(
a_text varchar(2048) character set utf8,
a_dels varchar(20) default ',.<>/?;:''"[]{}`~!@#$%^&*()-_=+\|/',
a_special char(1) default ' '
)
returns (
word varchar(50)
) as
begin
-- 補助SP。oltp_data_filling.sqlでのみ使用され、SIMILAR TOテストで使用する単語のさまざまな組み合わせでPATTERNSテーブルを埋めるために使用されます。
for
with recursive
j as( -- ループ#1: デリミタのリストを行に変換
select s,1 i, substring(s from 1 for 1) del
from(
select replace(:a_dels,:a_special,'') s
from rdb$database
)
UNION ALL
select s, i+1, substring(s from i+1 for 1)
from j
where substring(s from i+1 for 1)<>''
)
,d as(
select :a_text s, :a_special sp from rdb$database
)
,e as( -- ループ#2: 各デリミタを`スペース`に置換
select d.s, replace(d.s, j.del, :a_special) s1, j.i, j.del
from d join j on j.i=1
UNION ALL
select e.s, replace(e.s1, j.del, :a_special) s1, j.i, j.del
from e
-- 注: ここでは2.5の古いビルドで'column unknown: e.i'エラーが発生します。
-- 例: WI-V2.5.2.26540 (Alexey Kovyazin氏からの手紙、2014年8月24日 14:34)
join j on j.i = e.i + 1
)
,f as(
select s1 from e order by i desc rows 1
)
,r as ( -- ループ#3: テキストを個々の単語に分割
select iif(t.k>0, substring(t.s from t.k+1 ), t.s) s,
iif(t.k>0,position( del, substring(t.s from t.k+1 )),-1) k,
t.i,
t.del,
iif(t.k>0,left(t.s, t.k-1),t.s) word
from(
select f.s1 s, d.sp del, position(d.sp, s1) k, 0 i from f cross join d
)t
UNION ALL
select iif(r.k>0, substring(r.s from r.k+1 ), r.s) s,
iif(r.k>0,position(r.del, substring(r.s from r.k+1 )),-1) k,
r.i+1,
r.del,
iif(r.k>0,left(r.s, r.k-1),r.s) word
from r
where r.k>=0
)
select word from r where word>''
into
word
do
suspend;
end
^
set term ;^
commit;