MySQLでLTSVのデータを扱う 〜ユーザー変数でkey-valueを実現
- 値の読み込み(指定値 sample)
SELECT SUBSTRING_INDEX( SUBSTRING_INDEX( "test:27\tsample:8455\thogehoge:45", CONCAT('sample',':'),-1), "\t",1 )
ただ、これだと完全なるLTSVではないので、ちょっと考えないとまずいですね。例えば、key-valueが、test:test::test1ssa:とかvalue値に入っているとバグります。
- 値の読み込み(指定値 test) 失敗例
SELECT SUBSTRING_INDEX( SUBSTRING_INDEX( "test:test:27::abctest:44\tsample:8455\thogehoge:45", CONCAT('test',':'),-1), "\t",1 )
『test:27::abctest:44』 になるところが『44』になってしまいます。
これをカンマ区切りの応用で、完全にパースを再現すると
SET @key:='test'; SELECT SUBSTRING_INDEX( SUBSTRING_INDEX(SUBSTRING_INDEX(ltsv,"\t",v.n),"\t",-1) ,CONCAT(@key,':'),-(LENGTH(ltsv)-LENGTH(REPLACE(ltsv,concat(@key,':'),@key)))) value FROM (SELECT "test:test:27::abctest:44\tsample:8455\thogehoge:45" ltsv) c, (SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5) v WHERE LENGTH(ltsv)-LENGTH(REPLACE(ltsv,"\t",''))+1 >=v.n AND SUBSTRING_INDEX(SUBSTRING_INDEX(ltsv,"\t",v.n),"\t",-1) LIKE concat(@key,':%')
になるわけですが、冗長なので、stored function化したいところです。
尚、UNION ALLの連結個数は、例によってkey-valueの個数のMAX値になるので要注意です。
.....とはいえ、あまりにも大げさな仕組みになってしまうのと、key-valueの個数に制限があるのはナンセンスなので、一番最初の失敗例を改善するやり方を考えて見ましょう。
SELECT SUBSTRING_INDEX( SUBSTRING_INDEX( CONCAT("\t","test:test:27::abctest:44\tsample:8455\thogehoge:45"), CONCAT("\t",'test',':'),-1), "\t",1 )
検索対象文字列、検索文字列共に、文字先頭にタブ文字を付け加えてみます。
すると、
『test:27::abctest:44』
と正常にパースされました。
- 次に指定キーの置換を行ってみます。
SET @str:="sample:8455\ttest:test:27::abctest:44\thogehoge:45"; SET @key:='test'; SET @replace_str:='replace_str'; SELECT REPLACE( CONCAT("\t",@str,"\t"), CONCAT("\t",@key,':',SUBSTRING_INDEX( SUBSTRING_INDEX( CONCAT("\t",@str), CONCAT("\t",@key,':'),-1), "\t",1 ),"\t"), CONCAT("\t",@key,':',@replace_str,"\t"))
keyからvalueを取得するSQLを応用し、今度は、行頭と行末を付与してその値を置換しています。
substringを使ってもう少し短縮できそうですね。
SET @str:="sample:8455\ttest:test:27::abctest:44\thogehoge:45"; SET @key:='test'; SET @replace_str:='replace_str'; SELECT CONCAT( SUBSTRING( SUBSTRING_INDEX( CONCAT("\t",@str), CONCAT("\t",@key,':'),1 ),2 ),"\t",@key,':',@replace_str, SUBSTRING( @str, LOCATE( "\t", @str, LOCATE( CONCAT("\t",@key,':'), CONCAT("\t",@str) ) ) ) )
まるで短縮できてない...。これはとりあえず実装は前者にしておきましょうか
- 次に指定キーの追加
これは簡単、単純にconcatで行末にappendするだけです。
SET @str:="sample:8455\thogehoge:45"; SET @key:='test'; SET @val:='append_str'; SELECT CONCAT(@str,"\t",@key,':',@val);
- 次に文字列内に指定キーが含まれているかどうか判定
これも、行頭にタブ文字を付与した検索文字列で検索して存在するかどうかを判定
-- キーが存在しない場合 SET @str:="sample:8455\thogehoge:45"; SET @key:='test'; SELECT LOCATE(CONCAT("\t",@key,':'),CONCAT("\t",@str)); -- キーが存在する場合 SET @str:="sample:8455\ttest:test:27::abctest:44\thogehoge:45"; SET @key:='test'; SELECT LOCATE(CONCAT("\t",@key,':'),CONCAT("\t",@str));
上記ができると、ようやく、ユーザー変数を用いてkey-valueの保存が可能になります。