每个表中具有不同类型的列的 JOIN 性能

问题描述 投票:0回答:1

我需要在 Postgres 中连接 2 个表的多个列。每个表中的列都有不同的类型。

这不是一个如何连接表的问题(这个问题已经被问过并回答了好几次),而是如何在充分利用索引的情况下高效地连接它们。这些表有数百万条记录。

如果我要设计一个新表,其中的列始终具有

int
值,但我需要
JOIN
到另一个具有
text
值的现有旧表,什么会带来更好的性能:

  1. 将色谱柱设计为
    int
    并牺牲
    JOIN
    的性能,或
  2. 将列设计为
    text
    以避免强制转换并具有
    JOIN
    “直接”,并牺牲列的大小和索引的性能(我假设
    int
    上的索引比索引
    text
    )。
CREATE TABLE table_new
(
    id1 int NOT NULL,
    id2 int NOT NULL,
    -- other columns
);
CREATE INDEX idx_new_id ON table_new (id1, id2);

CREATE TABLE table_old
(
    id1 text NOT NULL,
    id2 text NOT NULL,
    -- other columns
);
CREATE INDEX idx_old_id ON table_old (id1, id2);

我需要在

table_old
table_new
上加入
id1
id2
。我知道我可以使用各种形式的强制转换(如所述,例如 如果列类型不同,则在 postgresql 中加入两个表)。

我实际上需要实现的目标相当于

SELECT * FROM table_old
    JOIN table_new 
    ON table_old.id1 = table_new.id1 AND table_old.id2 = table_new.id2;

但是,如何使用索引,或者如何创建索引以使联接高效?

我担心在

id1
id2
之间任何形式的铸造
int
text
都会破坏性能。

为了避免 XY 问题,这里有更广泛的背景:

  1. table_old
    是一个遗留表,我不允许更改。
  2. table_new
    是我正在设计的新桌子。
  3. 两个表都可以有数百万条记录。
  4. 我知道在
    table_new
    中,列
    id1
    id2
    always 包含
    int
    值,所以我想让表及其索引尽可能小且尽可能快,因此我将它们设计为
     int
  5. 如果没有合理的方法来有效地
    JOIN
    具有不同类型列的表,我也可以接受列为
    text
    作为解决方案。
  6. 我真正需要快的是
    JOIN
sql postgresql database-design query-optimization
1个回答
0
投票

功能索引来救援!

CREATE TEMP TABLE t_old (tid text PRIMARY KEY);
CREATE TEMP TABLE t_new (iid int NOT NULL);
CREATE INDEX t_old_int_idx ON t_old ((tid::int));
CREATE INDEX t_new_int_idx ON t_new (iid);
INSERT INTO t_old SELECT i::text FROM generate_series(1,99999) i;
INSERT INTO t_new VALUES (10),(20),(30);
ANALYSE t_old;
ANALYSE t_new;
EXPLAIN ANALYSE SELECT t_new.*, t_old.* FROM t_new JOIN t_old ON t_new.iid = t_old.tid::int;
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                                            QUERY PLAN                                                             │
├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Merge Join  (cost=1.69..2.51 rows=3 width=9) (actual time=0.063..0.082 rows=3 loops=1)                                            │
│   Merge Cond: ((t_old.tid)::integer = t_new.iid)                                                                                  │
│   ->  Index Scan using t_old_int_idx on t_old  (cost=0.29..3050.28 rows=99999 width=5) (actual time=0.016..0.028 rows=31 loops=1) │
│   ->  Sort  (cost=1.05..1.06 rows=3 width=4) (actual time=0.021..0.022 rows=3 loops=1)                                            │
│         Sort Key: t_new.iid                                                                                                       │
│         Sort Method: quicksort  Memory: 25kB                                                                                      │
│         ->  Seq Scan on t_new  (cost=0.00..1.03 rows=3 width=4) (actual time=0.009..0.010 rows=3 loops=1)                         │
│ Planning Time: 0.485 ms                                                                                                           │
│ Execution Time: 0.127 ms                                                                                                          │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

If not all the values in your old table are in fact valid integers you might need to make your functional index partial too.
© www.soinside.com 2019 - 2024. All rights reserved.