その他
    ホーム 技術発信 DoRuby postgresでorder byに複数のカラムを使うとdesc句を使った場合、indexを使用しない件

    postgresでorder byに複数のカラムを使うとdesc句を使った場合、indexを使用しない件

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    Postgresqlに限らず、インデックスを利用する/利用しないは、

    DBを使ったシステムの速度を決める重要な要件だったりします。

    今回は、業務中に突き当たった複数のカラムを指定していた場合

    DESCやASCを使っていた場合、indexを利用しなかったpostgresの

    対応方法について説明します。

    なお、突き当たったバージョンは8.1.9でした。

    対象はたいした事は無いsql文なのですが、こういったsql文です。

    SELECT

    entries.id,entries.title, entries.body, entries.entry_type, entries.created_at, entries.updated_at

    FROM

    entries

    ORDER BY

    entry_type,updated_at DESC

    indexは、 entry_type, updated_atの複合indexを張っています。

    ですがコレがindexを利用しない。

    DESC句を外すと利用するんですけどね。

    というわけで幾つか検索した所、以下の2つの情報を参照して解決しました。

    http://itpro.nikkeibp.co.jp/article/COLUMN/20051117/224817/?P=5

    http://d.hatena.ne.jp/poch-7003/20070913/1189691482

    今回はtimestamp型ですので、postgresから

    rails_db=# ¥df *cmp

    とした結果の

    Schema | Name | Result data type | Argument data types

    ————+———————-+——————+———————————————————-

    pg_catalog | bitcmp | integer | bit, bit

    pg_catalog | bpcharcmp | integer | character, character

    pg_catalog | btabstimecmp | integer | abstime, abstime

    pg_catalog | btarraycmp | integer | anyarray, anyarray

    pg_catalog | btboolcmp | integer | boolean, boolean

    pg_catalog | btbpchar_pattern_cmp | integer | character, character

    pg_catalog | btcharcmp | integer | “char”, “char”

    pg_catalog | btfloat48cmp | integer | real, double precision

    pg_catalog | btfloat4cmp | integer | real, real

    pg_catalog | btfloat84cmp | integer | double precision, real

    pg_catalog | btfloat8cmp | integer | double precision, double precision

    pg_catalog | btint24cmp | integer | smallint, integer

    pg_catalog | btint28cmp | integer | smallint, bigint

    pg_catalog | btint2cmp | integer | smallint, smallint

    pg_catalog | btint42cmp | integer | integer, smallint

    pg_catalog | btint48cmp | integer | integer, bigint

    pg_catalog | btint4cmp | integer | integer, integer

    pg_catalog | btint82cmp | integer | bigint, smallint

    pg_catalog | btint84cmp | integer | bigint, integer

    pg_catalog | btint8cmp | integer | bigint, bigint

    pg_catalog | btname_pattern_cmp | integer | name, name

    pg_catalog | btnamecmp | integer | name, name

    pg_catalog | btoidcmp | integer | oid, oid

    pg_catalog | btoidvectorcmp | integer | oidvector, oidvector

    pg_catalog | btreltimecmp | integer | reltime, reltime

    pg_catalog | bttext_pattern_cmp | integer | text, text

    pg_catalog | bttextcmp | integer | text, text

    pg_catalog | bttintervalcmp | integer | tinterval, tinterval

    pg_catalog | byteacmp | integer | bytea, bytea

    pg_catalog | cash_cmp | integer | money, money

    pg_catalog | date_cmp | integer | date, date

    pg_catalog | interval_cmp | integer | interval, interval

    pg_catalog | macaddr_cmp | integer | macaddr, macaddr

    pg_catalog | network_cmp | integer | inet, inet

    pg_catalog | numeric_cmp | integer | numeric, numeric

    pg_catalog | time_cmp | integer | time without time zone, time without time zone

    pg_catalog | timestamp_cmp | integer | timestamp without time zone, timestamp without time zone

    pg_catalog | timestamptz_cmp | integer | timestamp with time zone, timestamp with time zone

    pg_catalog | timetz_cmp | integer | time with time zone, time with time zone

    pg_catalog | varbitcmp | integer | bit varying, bit varying

    (40 rows)

    Argument data typesに、型が書いてあるので、使いたい型を選びます。

    今回は、updated_atなので、timestamp型となり、timestamp_cmpを利用しました。

    で、その逆の関数を作成します。

    CREATE OR REPLACE FUNCTION timestamp_cmp_rev(TIMESTAMP, TIMESTAMP) RETURNS INTEGER AS $$ SELECT timestamp_cmp($2,$1);$$ LANGUAGE sql

    その後、参考URLに書いてある通り作成しました。

    CREATE OPERATOR CLASS timestamp_desc_ops FOR TYPE TIMESTAMP USING BTREE AS OPERATOR 1 >, OPERATOR 2 >=, OPERATOR 3 =, OPERATOR 4 <=, OPERATOR 5 <,FUNCTION 1 timestamp_cmp(timestamp, timestamp)

    で、indexを作成しました。

    CREATE INDEX index_entries_on_entry_type_and_updated_at ON

    entries(entry_type, updated_at timestamp_desc_ops)

    これで検索するとインデックスを使用してくれました。

    もし「もっと良いやり方あるよ」と言う方いらっしゃいましたら是非教えて下さい。

    なお、8.3からdescで指定できるようです...苦労はなんだったんだろう。