前回に続き、旧ブログ(Seesaa)記事検索の準備中。PostgreSQLに取り込んだHTMLの切り出しに正規表現関数を使ってますが、マッチしなかった行の扱いがsubstring関数とregexp_matches関数で全然違うのを改めて認識しました。ついでに両関数の基本的な違いもメモ。使用バージョンは9.5.3です。
使うテーブル
前回の最後(実行コマンドまとめ)が終わった時点。↓ がテーブル定義で、列htmlに旧ブログ各記事のHTMLが丸ごと入ってます。DBの文字コードはUTF-8。
# \d kenpg_seesaa_list
Table "public.kenpg_seesaa_list"
+-----------+------+-----------+
| Column | Type | Modifiers |
+-----------+------+-----------+
| ymd | date | |
| url | text | |
| title | text | |
| tags | text | |
| html_file | text | |
| html | text | | <- contents
+-----------+------+-----------+
substring関数で、マッチなしの場合
今は知らないけど、自分が書いていた頃のSeesaaブログでは、記事に「追記」を入力すると<a name="more">~</a>内にセットされるようでした。この箇所に一つも入力されてなければHTML抽出時に省けるので、substring関数で調べると ↓
# select ymd, substring(html, '[^<]+')
from kenpg_seesaa_list;
+------------+-----------+
| ymd | substring |
+------------+-----------+
| 2015-05-23 | |
| 2015-04-19 | |
| 2015-04-16 | |
| 2015-04-15 | |
| 2015-04-14 | |
...
マッチしなかった行はNULLが返ります。全体で一つもマッチしなかったか否かは、集約関数を使うか、ORDER BY substring NULLS LASTを足してチェックします(先頭にNULLが来てれば一つもマッチなし)。
なおドキュメントの表に載ってる構文はsubstring(column FROM pattern)ですが、上のようにsubstring(column, pattern)とも書けます。FROM ⇔ カンマが互換になっており、簡単のため今日は後者で。
HTMLだからタグが英大文字の場合も考慮が必要なところ、substring関数ではiオプションを付けたりできません。そこで次にregexp_matches関数を使ってみます。
regexp_matches関数で、マッチなしの場合
形式はregexp_matchs(column, pattern [, option])。実行すると ↓ このように1行も返らず。マッチした行だけが結果に出る仕様です。
# select ymd, regexp_matches(html, '[^<]+', 'i')
from kenpg_seesaa_list;
+-----+----------------+
| ymd | regexp_matches |
+-----+----------------+
+-----+----------------+
(0 rows)
今回のようにマッチ有無だけ知りたければ、これで終わり。substringより簡単、かつオプションも使えるので勝手が良いです。
もし、いくつかの行にマッチした場合は ↓ こうなります。結果が常に配列で返り、部分マッチ(括弧内)があれば配列の各要素になる仕様です。
# select ymd, regexp_matches(html, 'regexp_matches .{10}', 'i')
from kenpg_seesaa_list;
+------------+-----------------------------------------+
| ymd | regexp_matches |
+------------+-----------------------------------------+
| 2015-01-16 | {"regexp_matches の結果が配列でなく複"} |
| 2014-07-03 | {"regexp_matches でファイル名、サイズ"} |
| 2014-07-02 | {"regexp_matches を使い、属性名と値の"} |
| 2014-01-06 | {"regexp_matches を使って次のように書"} |
| 2013-10-30 | {"regexp_matches で一度にマッチさせる"} |
+------------+-----------------------------------------+
(5 rows)
regexp_matches関数で「非マッチ行も結果に含める」場合
このようにsubstringとregexp_matchesは、マッチしなかった行が結果に出る/出ないという大きな違いがあります。ちょっと面倒なのは、regexp_matchesで「非マッチ行も結果に含めたい」場合。関数自体にはそういうオプションがないけど、クエリを工夫すればできます。例えば ↓
# select ymd, reg
from kenpg_seesaa_list
left join regexp_matches(html, 'regexp_matches .{10}')
as r (reg) on true
order by reg nulls last;
+------------+-----------------------------------------+
| ymd | reg |
+------------+-----------------------------------------+
| 2014-07-03 | {"regexp_matches でファイル名、サイズ"} |
| 2013-10-30 | {"regexp_matches で一度にマッチさせる"} |
| 2015-01-16 | {"regexp_matches の結果が配列でなく複"} |
| 2014-07-02 | {"regexp_matches を使い、属性名と値の"} |
| 2014-01-06 | {"regexp_matches を使って次のように書"} |
| 2015-04-13 | |
| 2015-04-12 | |
| 2015-04-11 | |
| 2015-04-10 | |
| 2015-04-09 | |
...
LEFT JOIN ... ON TRUEという変な構文ですけど。regexp_matchesの結果が非マッチだと「行自体がない」ので、こうやって結合しました。
↓ 別の方法。JOINを使わないので理解しやすい反面、SELECT ... FROMの間にもう一つSELECT文が入って見にくいです。
# select ymd, (
select regexp_matches(html, 'regexp_matches .{10}')
) as reg
from kenpg_seesaa_list
order by reg nulls last;
+------------+-----------------------------------------+
| ymd | reg |
+------------+-----------------------------------------+
| 2014-07-03 | {"regexp_matches でファイル名、サイズ"} |
| 2013-10-30 | {"regexp_matches で一度にマッチさせる"} |
| 2015-01-16 | {"regexp_matches の結果が配列でなく複"} |
| 2014-07-02 | {"regexp_matches を使い、属性名と値の"} |
| 2014-01-06 | {"regexp_matches を使って次のように書"} |
| 2015-04-13 | |
| 2015-04-12 | |
| 2015-04-11 | |
| 2015-04-10 | |
| 2015-04-09 | |
...
substringとregexp_matchesの基本的な違い
今回の関心は「POSIX正規表現」で「非マッチ行の扱い」の違いでしたが、それ以前に、この2関数はいろいろ違います。
• 機能
regexp_matchesはPOSIX正規表現専用ですが、substringはPOSIX正規表現だけでなく、文字位置やSQL正規表現での抽出もできます。↓ は前者の例。
# select ymd, substring(html, 100, 200)
from kenpg_seesaa_list;
+------------+-------------------------------------------------------------------------------------------------+
| ymd | substring |
+------------+-------------------------------------------------------------------------------------------------+
| 2015-05-23 | " /> +|
| | +|
| | http://kenpg.bitbucket.org/ に移転しました : 研究に使うポスグレ +|
| |