Gui*_*iik 7 postgresql performance database-design postgresql-9.6 postgresql-11 postgresql-performance
当一次只能进行一个引用时,我正在寻找在一个表中引用多个表的最有效方法。这意味着表 A 和 B 被表 C 引用,但 A 和 B 不能在 C 的一行中引用,并且我在编写查询时选择要查看的表。
我考虑过 4 种方法:
type和一个列FK,这样我就可以进行像这样的连接type = 'A' AND a.pk = c.a_fk(这就是我们现在使用的)FROM c JOIN a ON a.d_fk = c.d_fk在我尝试的每个解决方案中,查询规划器对于将返回多少行的判断都是错误的。
这是一个例子:我创建了一个简单的数据库,其中有 3 个表,如下所示:
Table "public.a"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+-------------------------------
pk | bigint | | not null | nextval('a_pk_seq'::regclass)
Table "public.b"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+-------------------------------
pk | bigint | | not null | nextval('b_pk_seq'::regclass)
Table "public.c"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+-------------------------------
pk | bigint | | not null | nextval('c_pk_seq'::regclass)
a_fk | bigint | | |
b_fk | bigint | | |
Indexes:
"c_pkey" PRIMARY KEY, btree (pk)
"c_a_fk_idx" btree (a_fk)
"c_b_fk_idx" btree (b_fk)
Foreign-key constraints:
"c_a_fk_fkey" FOREIGN KEY (a_fk) REFERENCES a(pk)
"c_b_fk_fkey" FOREIGN KEY (b_fk) REFERENCES b(pk)
Run Code Online (Sandbox Code Playgroud)
所以当我运行这个查询时:
SELECT * FROM a JOIN c ON a.pk = c.a_fk;
Run Code Online (Sandbox Code Playgroud)
我不希望规划者认为查询将返回 5 行。
相反,这就是我得到的:
Merge Join (cost=1.76..1.94 rows=10000 width=32) (actual time=0.035..0.051 rows=5 loops=1)
Merge Cond: (a.pk = c.a_fk)
-> Sort (cost=1.63..1.68 rows=20 width=8) (actual time=0.024..0.028 rows=15 loops=1)
Sort Key: a.pk
Sort Method: quicksort Memory: 25kB
-> Seq Scan on a (cost=0.00..1.20 rows=20 width=8) (actual time=0.009..0.013 rows=20 loops=1)
-> Index Scan using c_a_fk_idx1 on c (cost=0.13..24.21 rows=10000 width=24) (actual time=0.006..0.012 rows=5 loops=1)
Planning time: 0.297 ms
Execution time: 0.088 ms
Run Code Online (Sandbox Code Playgroud)
我尝试创建部分索引CREATE UNIQUE INDEX ON c (b_fk) WHERE a_fk IS NULL,但它没有改变任何内容。
唯一改进该计划的是在 b_fk 上添加 NULL 检查,如下所示:
SELECT * FROM a JOIN c ON a.pk = c.a_fk WHERE c.a_fk IS NOT NULL;
Run Code Online (Sandbox Code Playgroud)
这给出了这个计划:
Merge Join (cost=1.92..6.05 rows=5 width=32) (actual time=0.031..0.046 rows=5 loops=1)
Merge Cond: (c.a_fk = a.pk)
-> Index Scan using c_a_fk_idx on c (cost=0.29..20.37 rows=5 width=24) (actual time=0.005..0.012 rows=5 loops=1)
Index Cond: (a_fk IS NOT NULL)
-> Sort (cost=1.63..1.68 rows=20 width=8) (actual time=0.022..0.024 rows=15 loops=1)
Sort Key: a.pk
Sort Method: quicksort Memory: 25kB
-> Seq Scan on a (cost=0.00..1.20 rows=20 width=8) (actual time=0.006..0.007 rows=20 loops=1)
Planning time: 0.559 ms
Execution time: 0.086 ms
Run Code Online (Sandbox Code Playgroud)
但它仍然对 A 中的行出错,我认为必须有另一种方法来帮助查询规划器,这种方法不涉及为每个应该为 NULL 的列添加“无用”的 NULL 检查。
我尝试了 Postgres 9.6 和 11,我更喜欢 9.6 的解决方案,因为这是我们使用的,但我会接受 11 的解决方案。
这是使用“类型”解决方案的现实生活示例:
SELECT *
FROM BinDescription b
JOIN RobotDescription r
ON b.physicalContainerType = 'ROBOT'
AND b.physicalContainerFK = r.robotPK
JOIN RobotMissionDescription rm
ON rm.robotFK = r.robotPK
;
Run Code Online (Sandbox Code Playgroud)
这给了我这个计划:
Merge Join (cost=225.12..395.31 rows=12026 width=301) (actual time=4.372..67.206 rows=47293 loops=1)
Merge Cond: (b.physicalcontainerfk = r.robotpk)
-> Nested Loop (cost=222.91..15184.24 rows=88 width=152) (actual time=4.321..44.448 rows=47293 loops=1)
-> Index Scan using bindescription_physicalcontainertype_physicalcontainerfk_key on bindescription b (cost=0.29..13.66 rows=4 width=76) (actual time=0.011..0.017 rows=4 loops=1)
Index Cond: (physicalcontainertype = 'ROBOT'::physicalcontainertype)
-> Bitmap Heap Scan on robotmissiondescription rm (cost=222.62..3672.39 rows=12026 width=76) (actual time=2.431..7.985 rows=11823 loops=4)
Recheck Cond: (robotfk = b.physicalcontainerfk)
Heap Blocks: exact=14246
-> Bitmap Index Scan on idx_robotmissiondescription_robotfk (cost=0.00..219.62 rows=12026 width=0) (actual time=1.629..1.629 rows=11823 loops=4)
Index Cond: (robotfk = b.physicalcontainerfk)
-> Sort (cost=2.20..2.29 rows=34 width=149) (actual time=0.044..4.752 rows=47321 loops=1)
Sort Key: r.robotpk
Sort Method: quicksort Memory: 34kB
-> Seq Scan on robotdescription r (cost=0.00..1.34 rows=34 width=149) (actual time=0.007..0.014 rows=34 loops=1)
Planning time: 1.559 ms
Execution time: 69.480 ms
Run Code Online (Sandbox Code Playgroud)
A 和 B 都从“父”表继承序列,并且 C 具有“父”表的外键,但这在 PostgreSQL 中是不可能的
这对于任何 SQL 数据库都是可能的。为什么你认为 PostgreSQL 不可能?
您可能会将 SQL 表继承与 PostgreSQL 的“表继承”混淆,它们是不同的东西
以下是如何在 SQL 中执行此操作。这称为类表继承:
create table parties (
party_id serial primary key
);
create table individuals (
party_id int primary key references parties(party_id),
given_name text,
...
);
create table organizations (
party_id int primary key references parties(party_id),
organization_name text,
...
);
create table sales_orders (
order_id serial primary key,
customer_id int references parties(party_id),
...
);
Run Code Online (Sandbox Code Playgroud)
如果您想要更快的性能,请使用单表继承:
create table parties (
party_id serial primary key,
party_type text check(party_type in ('Individual','Organization')),
given_name text null,
organization_name text null,
...
/* add some check constraints for party_type vs values for individuals/orgs */
);
Run Code Online (Sandbox Code Playgroud)
另外,如果你给出真实的例子而不是抽象的“A”和“B”,那么帮助你会容易100倍
| 归档时间: |
|
| 查看次数: |
11845 次 |
| 最近记录: |