3

Postgreユーザーの皆様、こんにちは。

短い話ですが、State に関連する Address テーブルがあります (各 Address には State があります)。検索のために次のクエリを実行します。

SELECT "addresses".*, (ts_rank((to_tsvector('simple', coalesce("addresses"."name"::text, '')) || to_tsvector('simple', coalesce(pg_search_85240c410826a2b0e0f0e5.pg_search_442c4ad3183a256248ef8d::text, ''))), (to_tsquery('simple', ''' ' || 'test' || ' ''' || ':*')), 0)) AS pg_search_rank 
FROM "addresses" 
LEFT OUTER JOIN (SELECT "addresses"."id" AS id, string_agg("states"."name"::text, ' ') AS pg_search_442c4ad3183a256248ef8d 
            FROM "addresses" INNER JOIN "states" ON "states"."id" = "addresses"."state_id" GROUP BY "addresses"."id") pg_search_85240c410826a2b0e0f0e5 ON pg_search_85240c410826a2b0e0f0e5.id = "addresses"."id" 
            WHERE (((to_tsvector('simple', coalesce("addresses"."name"::text, '')) || to_tsvector('simple', coalesce(pg_search_85240c410826a2b0e0f0e5.pg_search_442c4ad3183a256248ef8d::text, ''))) @@ (to_tsquery('simple', ''' ' || 'test' || ' ''' || ':*')))) AND (company_id = 2142) 
            ORDER BY pg_search_rank DESC, "addresses"."id" ASC 

まっすぐですよね?これは、良好なマシンと 8581 個のアドレスで約 1.226 ミリ秒かかります。これを改善する必要があるので、この 2 つのインデックスを作成します

CREATE INDEX index_addresses_search_by_name ON addresses USING gin(to_tsvector('simple', COALESCE((public.addresses.name)::text, '')))
CREATE INDEX index_state_search_by_name ON states USING gin(to_tsvector('simple', COALESCE((public.states.name)::text, '')))

これは、住所にインデックスを作成し、州にインデックスを作成するのに役立ちますが、そうではありません:(クエリは以前と同じように遅く、説明はインデックスが使用されていないことを示しています.

提案をしてください、

編集:

Explain Analyst が表示する内容は次のとおりです。

"Sort  (cost=11.39..11.40 rows=1 width=4824) (actual time=0.947..0.947 rows=0 loops=1)"
"  Sort Key: (ts_rank((to_tsvector('simple'::regconfig, COALESCE((public.addresses.name)::text, ''::text)) || to_tsvector('simple'::regconfig, COALESCE((string_agg((states.name)::text, ' '::text)), ''::text))), '''test'':*'::tsquery, 0)), public.addresses.id"
"  Sort Method: quicksort  Memory: 17kB"
"  ->  Nested Loop Left Join  (cost=6.19..11.38 rows=1 width=4824) (actual time=0.905..0.905 rows=0 loops=1)"
"        Join Filter: (public.addresses.id = public.addresses.id)"
"        Filter: ((to_tsvector('simple'::regconfig, COALESCE((public.addresses.name)::text, ''::text)) || to_tsvector('simple'::regconfig, COALESCE((string_agg((states.name)::text, ' '::text)), ''::text))) @@ '''test'':*'::tsquery)"
"        ->  Seq Scan on addresses  (cost=0.00..5.14 rows=1 width=4792) (actual time=0.904..0.904 rows=0 loops=1)"
"              Filter: (company_id = 2142)"
"        ->  HashAggregate  (cost=6.19..6.20 rows=1 width=520) (never executed)"
"              ->  Hash Join  (cost=1.02..6.18 rows=1 width=520) (never executed)"
"                    Hash Cond: (public.addresses.state_id = states.id)"
"                    ->  Seq Scan on addresses  (cost=0.00..5.11 rows=11 width=8) (never executed)"
"                    ->  Hash  (cost=1.01..1.01 rows=1 width=520) (never executed)"
"                          ->  Seq Scan on states  (cost=0.00..1.01 rows=1 width=520) (never executed)"
"Total runtime: 1.226 ms"

編集2

アドレス ddl

--
-- PostgreSQL database dump
--

SET statement_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;

SET search_path = public, pg_catalog;

SET default_tablespace = '';

SET default_with_oids = false;

--
-- Name: addresses; Type: TABLE; Schema: public; Owner: trucking; Tablespace: 
--

CREATE TABLE addresses (
    id integer NOT NULL,
    name character varying(255),
    street character varying(255),
    city character varying(255),
    zip_code character varying(255),
    primary_phone character varying(255),
    alternate_phone character varying(255),
    fax character varying(255),
    email character varying(255),
    contact character varying(255),
    company_id integer DEFAULT 0 NOT NULL,
    motor_carrier_number character varying(12),
    state_id integer,
    created_at timestamp without time zone,
    updated_at timestamp without time zone,
    alternate_phone2 character varying(12),
    insurance_expires_on date,
    notes text
);


ALTER TABLE public.addresses OWNER TO trucking;

--
-- Name: addresses_id_seq; Type: SEQUENCE; Schema: public; Owner: trucking
--

CREATE SEQUENCE addresses_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


ALTER TABLE public.addresses_id_seq OWNER TO trucking;

--
-- Name: addresses_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: trucking
--

ALTER SEQUENCE addresses_id_seq OWNED BY addresses.id;


--
-- Name: id; Type: DEFAULT; Schema: public; Owner: trucking
--

ALTER TABLE ONLY addresses ALTER COLUMN id SET DEFAULT nextval('addresses_id_seq'::regclass);


--
-- Name: addresses_pkey; Type: CONSTRAINT; Schema: public; Owner: trucking; Tablespace: 
--

ALTER TABLE ONLY addresses
    ADD CONSTRAINT addresses_pkey PRIMARY KEY (id);


--
-- Name: index_addresses_on_company_id; Type: INDEX; Schema: public; Owner: trucking; Tablespace: 
--

CREATE INDEX index_addresses_on_company_id ON addresses USING btree (company_id);


--
-- Name: index_addresses_on_state_id; Type: INDEX; Schema: public; Owner: trucking; Tablespace: 
--

CREATE INDEX index_addresses_on_state_id ON addresses USING btree (state_id);


--
-- Name: index_addresses_search_by_city; Type: INDEX; Schema: public; Owner: trucking; Tablespace: 
--

CREATE INDEX index_addresses_search_by_city ON addresses USING gin (to_tsvector('simple'::regconfig, COALESCE((city)::text, ''::text)));


--
-- Name: index_addresses_search_by_email; Type: INDEX; Schema: public; Owner: trucking; Tablespace: 
--

CREATE INDEX index_addresses_search_by_email ON addresses USING gin (to_tsvector('simple'::regconfig, COALESCE((email)::text, ''::text)));


--
-- Name: index_addresses_search_by_name; Type: INDEX; Schema: public; Owner: trucking; Tablespace: 
--

CREATE INDEX index_addresses_search_by_name ON addresses USING gin (to_tsvector('simple'::regconfig, COALESCE((name)::text, ''::text)));


--
-- Name: index_addresses_search_by_street; Type: INDEX; Schema: public; Owner: trucking; Tablespace: 
--

CREATE INDEX index_addresses_search_by_street ON addresses USING gin (to_tsvector('simple'::regconfig, COALESCE((street)::text, ''::text)));


--
-- PostgreSQL database dump complete
--

州の DDL

--
-- PostgreSQL database dump
--

SET statement_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;

SET search_path = public, pg_catalog;

SET default_tablespace = '';

SET default_with_oids = false;

--
-- Name: states; Type: TABLE; Schema: public; Owner: trucking; Tablespace: 
--

CREATE TABLE states (
    id integer NOT NULL,
    name character varying(255) NOT NULL,
    abbrev character varying(255) NOT NULL,
    country character varying(255)
);


ALTER TABLE public.states OWNER TO trucking;

--
-- Name: states_id_seq; Type: SEQUENCE; Schema: public; Owner: trucking
--

CREATE SEQUENCE states_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


ALTER TABLE public.states_id_seq OWNER TO trucking;

--
-- Name: states_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: trucking
--

ALTER SEQUENCE states_id_seq OWNED BY states.id;


--
-- Name: id; Type: DEFAULT; Schema: public; Owner: trucking
--

ALTER TABLE ONLY states ALTER COLUMN id SET DEFAULT nextval('states_id_seq'::regclass);


--
-- Name: states_pkey; Type: CONSTRAINT; Schema: public; Owner: trucking; Tablespace: 
--

ALTER TABLE ONLY states
    ADD CONSTRAINT states_pkey PRIMARY KEY (id);


--
-- Name: index_states_search_by_abbrev; Type: INDEX; Schema: public; Owner: trucking; Tablespace: 
--

CREATE INDEX index_states_search_by_abbrev ON states USING gin (to_tsvector('simple'::regconfig, COALESCE((abbrev)::text, ''::text)));


--
-- Name: index_states_search_by_name; Type: INDEX; Schema: public; Owner: trucking; Tablespace: 
--

CREATE INDEX index_states_search_by_name ON states USING gin (to_tsvector('simple'::regconfig, COALESCE((name)::text, ''::text)));


--
-- PostgreSQL database dump complete
--

これまでで最高の書き直し

SELECT consolidated_address.id, (ts_rank((to_tsvector('simple', coalesce(consolidated_address.name::text, '')) || to_tsvector('simple', coalesce(consolidated_address.state_name::text, ''))), (to_tsquery('simple', ''' ' || 'Gallaway' || ' ''' || ':*')), 0)) AS pg_search_rank 
FROM (
 SELECT "addresses".id,
    "addresses".name,
    string_agg("states".name::text, ' ') as state_name
    FROM addresses
    LEFT OUTER JOIN "states"
    ON "states".id = "addresses".state_id
    GROUP BY "addresses".id) consolidated_address
WHERE 
    (((to_tsvector('simple', coalesce(consolidated_address.name::text, '')) || to_tsvector('simple', coalesce(consolidated_address.state_name::text, ''))) @@ (to_tsquery('simple', ''' ' || 'Gallaway' || ' ''' || ':*')))) 

少し高速ですが、まだインデックスを使用していません

ありがとうございました、

4

3 に答える 3

1

Explain Analysisの出力を読んだところ、テーブルが小さすぎてインデックスを使用しても利益が得られないということです。テーブルは物理ファイルとしてロードされ、PostgreSQLはOSのプリフェッチなどを利用できるため、PostgreSQLでのシーケンシャルスキャンは比較的安価です。

ディスクから単一のページをロードし、それを順番にスキャンするよりも速い計画はないことに注意してください。インデックスを使用すると、オーバーヘッドが増えるだけです。シーケンシャルスキャンにそれほど時間はかからないので、実際のデータを操作できるようになるまで心配する必要はありません。

PostgreSQLでは、データを取得する前にクエリを最適化することが、解決策よりも多くの問題のレシピであることに注意してください。私の心からの推奨は、実際のデータを返す実際のクエリが得られるまでクエリを分析するのを待つことです。次に、どのインデックスが役立つかどうかを判断できます。

編集:あなたはクエリが遅いと言います。しかし、計画はそれが1msで実行されることを示しています。どれくらいの時間がかかると思いますか?

于 2012-09-30T01:25:19.913 に答える
1

https://github.com/Casecommons/pg_search/issues/51の Github からクロスポストされた回答

私は、このクエリを生成したpg_search Ruby gem の作成者です。


ええ、残念ながら、少なくとも現在実装されている方法では、:associated_against クエリをインデックスに対して機能させる方法を知りません。

これは、:associated_against が、一度に 1 つのレコードではなく、関連付けられたすべてのレコードのテキストを結合して検索するためです。

たとえば、tags テーブルに参加し、3 つのレコード ("foo"、"bar"、および "baz") がある場合、"foo baz" を検索するとそれが見つかることが予想されます。より簡単にインデックス付けできるソリューションは、"foo" または "baz" クエリでのみ機能しますが、"foo baz" では機能しません。

複数のレコードにまたがってインデックスを作成することはできません (少なくとも私の知る限り)。

おそらく、pg_search で、レコードごとに検索を行うオプションを表面化することができます。これは、インデックスを使用できますが、レコード間では一致しません。

于 2012-08-14T17:48:50.487 に答える
0

左外部結合を介して結合しているクエリは、アドレステーブル全体を集計してから、company_idで制限しているため、このクエリは低速です。アドレステーブルにはcompany_idがありますか?where句を内部クエリに移動して、返されるレコードの数を制限してみてください。

于 2012-08-13T23:42:17.587 に答える