文字列処理の比較
FORCIA, Inc. 板垣 貴裕
PostgreSQL は SQL から利用できる関数 (以下、SQL関数) を豊富に提供しています。 今回は文字列の処理に注目して、SQL関数と、プログラミング言語の標準ライブラリが提供する機能を比較してみます。 比較対象のプログラミング言語は Java と JavaScript です。 PostgreSQL での文字列型の使い方全般に関しては、「文字列処理と日本語全文検索」も参照してください。
全般的な比較
SQL, Java, JavaScript すべて、文字列は変更不可能なオブジェクトです。 また、空文字と null は異なることも、すべての言語で共通しています。
違いとしては、文字のインデックス (添字, 文字の位置) の表現方法があります。 SQL関数では、最初の文字のインデックスは 1 です。 一方、Java, Java Script では、0 から始まります。
その他の違いに、Java, JavaScript では「インスタンスのメソッド」がありますが、SQL関数には無いことが挙げられます。 SQL の関数は、すべてクラス・メソッド (スタティック関数) のような扱いになります。
文字列関数の比較
機能ごとに並ぶよう一覧にしました。
操作 | SQL | Java | JavaScript |
---|---|---|---|
長さ | length( str ) | str . length() | str . length |
空? | str = '' | str . isEmpty() | str -- 真偽値への変換 |
位置 | position( sub in str ) | str . indexOf( sub ) | str . indexOf( sub ) str . search( regex ) |
マッチング | str LIKE '%' || sub || '%' str SIMILAR TO pattern str ~ regex |
str . contains( sub ) str . matches( regex ) Pattern.matches( regex, str ) |
str . match( regex ) |
抽出 (位置) | substring( str from pos for len ) substr( str, pos, len ) |
str . substring( pos, pos + len ) | str . substring( pos, pos + len ) str . substr( pos, len ) |
抽出 (左右) | left( str, len ) -- 9.1 right( str, len ) -- 9.1 |
str . substring( 0, len ) str . substring( str.length() - len ) |
str . substring( 0, len ) str . substring( str.length - len ) |
抽出 (パターン) | substring( src from regex ) regexp_matches( src, regex ) |
Pattern.compile( regex ).matcher( str ).group( n ) | str . match( regex ) |
置換 (位置) | overlay( str placing rep from pos for len ) | StringUtils.overlay( str, rep, start, end ) | N/A |
置換 (パターン) | regexp_replace( str, regex, rep, 'g' ) translate( str, from, to ) |
str . replaceAll( regex, rep ) Pattern.compile( regex ).matcher( str ).replaceAll( rep ) |
str . replace( regex, rep ) |
連結 | str1 || str2 concat( str1, str2, ... ) -- 9.1 concat_ws( sep, str1, str2, ... ) -- 9.1 |
str1 + str2 str1 . concat( str2 ) |
同左 |
結合 | array_to_string( array, sep ) string_agg( setof_str, sep ) -- 9.0 |
StringUtils.join( array, sep ) | array . join( sep ) |
分割 | string_to_array( str, sep ) regexp_split_to_array( str, regex, 'g' ) regexp_split_to_table( str, regex, 'g' ) |
str . split( regex ) Pattern.compile( regex ).split( str ) |
str . split( regex ) |
トリミング | trim([leading|trailing|both] trimmed from str) ltrim( str, trimmed ) rtrim( str, trimmed ) |
str . trim() | str . replace(/^\s+|\s+$/g, "") |
繰り返し | repeat( str, n ) | StringUtils.repeat( str, n ) | N/A |
小文字化 | lower( str ) | str . toLowerCase() | 同左 |
大文字化 | upper( str ) | str . toUpperCase() | 同左 |
前後反転 | reverse( str ) -- 9.1 | StringUtils.reverse( str ) | N/A |
書式化 | format( format, arg1, ... ) -- 9.1 | String.format( format, arg1, ... ) | N/A |
※ “9.1” とあるものは、PostgreSQL 9.1 で導入予定の新しい関数です。
ユニコードでのサロゲートペアの扱い
PostgreSQL の文字列の内部表現はマルチバイト文字列ですが、SQL から文字列を操作する限り、文字数は常に文字 (コード・ポイント) 単位で数えます。 例えば UTF-16 ではサロゲートペアで表現される文字 (UTF-8 で 4byte 以上になる) であっても、もちろんデータベースに格納して検索できますし、文字単位で扱う場合には常に1文字として扱えます。 型定義での varchar(n) のような場合でも、n は文字数で数えます。 他のデータベース製品ではバイト数で数えるものもあるので注意しましょう。
一方、Java, JavaScript では文字数は UTF-16 のコード単位 (char) で数えます。 そのため、サロゲートペアで表される文字まで話を広げると、上記の対応表は不正確な部分があります。 例えば「長さ」と正確に一致するのは Java では str.codePointCount() というほうが厳密ではあります。
まとめ
どの言語でも、文字列処理を行う関数を数多く持っており、概ね同様の記述力があることがわかりました。 同じ処理でも、データベース・サーバで行うか、アプリケーション・サーバで行うか、それともブラウザで行うか、さまざまな選択肢があると言えます。 もっとも効率のよい場所で処理できるよう、頭を切り替えられるようにしておきましょう!
関連リンク
- 文字列処理と日本語全文検索
- 文字列関数と演算子 (PostgreSQL 文書)
- パターンマッチ (PostgreSQL 文書)
- String Functions and Operators Compatibility - 各種DBMS間での比較
- java.lang.String (Java SE 6)
- org.apache.commons.lang.StringUtils (Apache Commons)
- ECMAScript Language Specification 5th edition
(2010年11月1日 公開)