临时表上的索引使用情况

Art*_*rov 8 postgresql performance index execution-plan temporary-tables postgresql-performance

我有两个相当简单的查询。第一个查询

 UPDATE mp_physical SET periodic_number = '' WHERE periodic_number is NULL;
Run Code Online (Sandbox Code Playgroud)

这是计划

 duration: 0.125 ms  plan:
    Query Text: UPDATE mp_physical  SET periodic_number = '' WHERE periodic_number is NULL;
    Update on mp_physical  (cost=0.42..7.34 rows=1 width=801)
      ->  Index Scan using "_I_periodic_number" on mp_physical  (cost=0.42..7.34 rows=1 width=801)
            Index Cond: (periodic_number IS NULL)
Run Code Online (Sandbox Code Playgroud)

第二个:

 UPDATE observations_optical_temp SET designation = '' WHERE periodic_number is NULL;
Run Code Online (Sandbox Code Playgroud)

它的计划是:

duration: 2817.375 ms  plan:
    Query Text: UPDATE observations_optical_temp SET periodic_number = '' WHERE periodic_number is NULL;
    Update on observations_optical_temp  (cost=103.55..9223.01 rows=5049 width=212)
      ->  Bitmap Heap Scan on observations_optical_temp  (cost=103.55..9223.01 rows=5049 width=212)
            Recheck Cond: (periodic_number IS NULL)
            ->  Bitmap Index Scan on "_I_per_num_temp"  (cost=0.00..102.29 rows=5049 width=0)
                  Index Cond: (periodic_number IS NULL)
Run Code Online (Sandbox Code Playgroud)

我希望第二个计划与第一个计划相同。但事实并非如此。为什么?这是表的转储。

CREATE TABLE public.mp_physical(
    id_mpp serial NOT NULL,
    id_comet_parts integer,
    "SPK_id" public.nonnegative_int,
    designation varchar(30),
    name varchar(100),
    prefix varchar,
    "is_NEO" bool,
    "H" double precision,
    "G" double precision,
    diameter public.nonnegative_double,
    extent varchar(30),
    extent_error public.nonnegative_double,
    geometric_albedo public.nonnegative_double,
    rot_per public.nonnegative_double,
    "GM" public.nonnegative_double,
    "BV" public.nonnegative_double,
    "UB" public.nonnegative_double,
    "spec_B" varchar(30),
    "spec_T" varchar(30),
    lca double precision,
    multiplicity public.nonnegative_int,
    polar_ang double precision,
    polar_slope_ang double precision,
    a double precision,
    b double precision,
    mass public.nonnegative_double,
    mp_type public.mp_type NOT NULL,
    periodic_number varchar(5),
    diameter_method_def varchar(200),
    discovery_info text,
    "H_sigma" public.nonnegative_double,
    "G_sigma" public.nonnegative_double,
    diameter_sigma public.nonnegative_double,
    geometric_albedo_sigma public.nonnegative_double,
    rot_per_sigma public.nonnegative_double,
    "GM_sigma" public.nonnegative_double,
    "BV_sigma" public.nonnegative_double,
    "UB_sigma" public.nonnegative_double,
    lca_sigma public.nonnegative_double,
    a_sigma public.nonnegative_double,
    b_sigma public.nonnegative_double,
    polar_ang_sigma public.nonnegative_double,
    mass_sigma public.nonnegative_double,
    CONSTRAINT "_C_id_ap" PRIMARY KEY (id_mpp)
);

CREATE INDEX "_I_name" ON  mp_physical  USING btree (name);
CREATE INDEX "_I_designation" ON mp_physical USING btree(mpp_designation);
CREATE INDEX "_I_periodic_number" ON mp_physical USING btree(periodic_number);
CREATE INDEX "_I_mp_type" ON mp_physical USING btree(mp_type);
Run Code Online (Sandbox Code Playgroud)

  CREATE TEMPORARY TABLE "observations_optical_temp"(note_1,date,"RA","Dec",magnitude,band,id_observatory,id_mpp,"Dec_degree",observatory_code,periodic_number,mpp_designation,mp_type)
AS SELECT note_1,date,"RA","Dec",magnitude,band,id_observatory,id_mpp,"Dec_degree",'1'::varchar(3),'1'::varchar(8),'1'::varchar(30),'A'::public.mp_type FROM observations_optical;

CREATE TABLE observations_optical(
    id_obs_o bigint
    note_1 varchar,
    date timestamp NOT NULL,
    "RA" time NOT NULL,
    "Dec_degree" integer NOT NULL,
    "Dec" time NOT NULL,
    magnitude double precision,
    band varchar,
    id_observatory integer,
    id_mpp integer,
    CONSTRAINT "_PK_id_obs_o" PRIMARY KEY (id_obs_o)
);
CREATE INDEX "_I_temp_1" ON observations_optical_temp USING btree(mpp_designation);
CREATE INDEX "_I_temp_2" ON observations_optical_temp USING btree(periodic_number);
CREATE INDEX "_I_temp_3" ON observations_optical_temp USING btree(mp_type);
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 6

索引扫描和位图索引扫描之间的选择基本上取决于 Postgres 期望检索每个数据页的行数 - 这取决于有关表中数据分布的统计信息和查询谓词的选择性。并且通过随机内存访问的预期成本 - 这是由成本设置定义的,最突出的是random_page_costeffective_cache_size.

如果 Postgres 期望在同一数据页上找到 enpugh 行,它会切换到位图索引扫描,这对于这种物理数据分布更有效。(如果检索到大多数数据页,无论如何,低顺序扫描会更快。)因此,即使您的两个表的列和索引看起来都一样,您仍然可能会根据数据分布和查询谓词的选择性获得这些不同的查询计划.

但是您的表一开始就非常不同。mp_physical具有更宽的行,因此只有少数行位于同一数据页(默认为 8kb)。这非常有利于索引扫描,因为即使在检索所有行的更高百分比时,位图索引扫描也不会购买太多(如果有的话)。

另外,请注意临时表不被覆盖autovacuum,因此不会自动分析。您可能必须手动执行此操作以获得准确的表统计信息,以便查询规划器使用:

ANALYZE observations_optical_temp;
Run Code Online (Sandbox Code Playgroud)

  • 向临时表添加 ANALYZE 将查询时间加快了约 25%,此外,向临时表添加索引可加快 25% 的查询时间。感谢您专门指出这一点。 (2认同)