在 postgres 中,使用 split_part 函数代替 like 语句好吗
In postgres, is it good to use split_part function instead like statements
有人可以解释或建议我们可以使用 split_part 而不是 喜欢Postgres中的。
在我的用例中,名称列将包含一些中间字符串,这对于特定类别是常见的。比如Vinod.Game1、Vinod.Game2、Vinod.Game3等
现在我想获取 Vinod 玩的游戏数量及其详细信息。
我有两个选择:
select * from games where name like 'Vinod.Game%'
或
select * from games where split_part(name, '.Game', 1) = 'Vinod'
当我检查 200 行的数据时,我看到了 beloe 统计数据
对于喜欢的查询:
Planning time: 120.326 ms
Execution time: 2.878 ms
对于split_part查询:
Planning time: 8.845 ms
Execution time: 3.681 ms
能否请您帮助我理解计划时间在查询中的影响。哪个更好用(split_part vs like), 如果我们有千兆数据库?
Table "public.games"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
------------------+-----------------------+-----------+----------+------------------------+----------+--------------+-------------
id | character varying(32) | | not null | | extended | |
access | character varying(50) | | | | extended | |
deleted | character varying(1) | | | 'N'::character varying | extended | |
timePlayed | character varying(50) | | | | extended | |
description | character varying | | | | extended | |
name | character varying(64) | | | | extended | |
使用 LIKE 的第一个查询将扫描 table(不是索引扫描,因为丑陋的“SELECT *”...)。
带有 split_part 的第二个查询将构建一个内部数据集(split_part 函数),并对结果数据集进行扫描以查找符合条件的行。
在这两种情况下,根本没有索引可以加速您的查询,因为您的谓词不可搜索。
实际上您查询的数据违反了第一范式(atomci 数据)。当这样的 mistqke 完成时,您的数据库根本就不是关系型的,当您没有关系型数据库时,RDBMS 无法用性能来处理它,因为 RDBMS 专门设计用于操纵关系,而不是“cobol”类型的数据结构!
正确的解决方案是修复数据模型。
不要在单个列中存储分隔值。这会在漫长的运行.
中一次次伤害你
但是像这样的问题的答案通常是“我没有创建它,我必须忍受它”,您需要测试这两种方法。
要获得有意义的测试,您必须创建远不止 200 行的行。
我使用这种方法创建了一些假数据:
create table games
(
id character varying(32) not null,
access character varying(50),
deleted character varying(1) default 'N'::character varying ,
timePlayed character varying(50),
description text ,
name character varying(64)
);
insert into games(id, access, timeplayed, description, name)
select g.id::text,
'full',
'all night long',
'some description',
case
when random() < 0.1 then 'Vinod.'
when random() < 0.2 then 'Vivos.'
when random() < 0.3 then 'Doniv.'
when random() < 0.5 then 'Novid.'
when random() < 0.6 then 'Somevid.'
when random() < 0.7 then 'OtherVid.'
when random() < 0.8 then 'Fonod.'
else 'Barnod.'
end || 'Game' || (random() * 999 + 1)::int
from generate_series(1,1e6) as g(id);
create index on games (name varchar_pattern_ops);
create index on games ( (split_part(name, '.', 1)) );
vacuum analyze games;
以上生成了 100 万行,其中 10% 以 Vinod.
开头
请注意,我只在 .
上拆分,而不是在 .Game
上拆分 - 对我来说更有意义:选择第一个由点分隔的元素。
缓存 table 时,LIKE 查询的执行时间约为 70 毫秒,使用 split_part()
的查询的执行时间约为 25 毫秒(Windows 10 台笔记本电脑和 Postgres 13.2)。所以 split_part() 和基于表达式的索引似乎是赢家。
explain (analyze, buffers)
select *
from games
where name like 'Vinod.Game%';
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------
Index Scan using games_name_idx on games (cost=0.42..12490.69 rows=99080 width=59) (actual time=0.018..67.705 rows=100189 loops=1)
Index Cond: (((name)::text ~>=~ 'Vinod.Game'::text) AND ((name)::text ~<~ 'Vinod.Gamf'::text))
Filter: ((name)::text ~~ 'Vinod.Game%'::text)
Buffers: shared hit=99863
Planning Time: 0.669 ms
Execution Time: 70.365 ms
explain (analyze, buffers)
select *
from games
where split_part(name, '.', 1) = 'Vinod'
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Index Scan using games_split_part_idx on games (cost=0.42..11657.32 rows=99400 width=59) (actual time=0.025..20.793 rows=100189 loops=1)
Index Cond: (split_part((name)::text, '.'::text, 1) = 'Vinod'::text)
Buffers: shared hit=11450
Planning Time: 0.098 ms
Execution Time: 23.605 ms
但再次声明:解决问题的正确方法是规范化数据模型。
有人可以解释或建议我们可以使用 split_part 而不是 喜欢Postgres中的。
在我的用例中,名称列将包含一些中间字符串,这对于特定类别是常见的。比如Vinod.Game1、Vinod.Game2、Vinod.Game3等
现在我想获取 Vinod 玩的游戏数量及其详细信息。 我有两个选择:
select * from games where name like 'Vinod.Game%'
或
select * from games where split_part(name, '.Game', 1) = 'Vinod'
当我检查 200 行的数据时,我看到了 beloe 统计数据
对于喜欢的查询:
Planning time: 120.326 ms
Execution time: 2.878 ms
对于split_part查询:
Planning time: 8.845 ms
Execution time: 3.681 ms
能否请您帮助我理解计划时间在查询中的影响。哪个更好用(split_part vs like), 如果我们有千兆数据库?
Table "public.games"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
------------------+-----------------------+-----------+----------+------------------------+----------+--------------+-------------
id | character varying(32) | | not null | | extended | |
access | character varying(50) | | | | extended | |
deleted | character varying(1) | | | 'N'::character varying | extended | |
timePlayed | character varying(50) | | | | extended | |
description | character varying | | | | extended | |
name | character varying(64) | | | | extended | |
使用 LIKE 的第一个查询将扫描 table(不是索引扫描,因为丑陋的“SELECT *”...)。
带有 split_part 的第二个查询将构建一个内部数据集(split_part 函数),并对结果数据集进行扫描以查找符合条件的行。
在这两种情况下,根本没有索引可以加速您的查询,因为您的谓词不可搜索。
实际上您查询的数据违反了第一范式(atomci 数据)。当这样的 mistqke 完成时,您的数据库根本就不是关系型的,当您没有关系型数据库时,RDBMS 无法用性能来处理它,因为 RDBMS 专门设计用于操纵关系,而不是“cobol”类型的数据结构!
正确的解决方案是修复数据模型。
不要在单个列中存储分隔值。这会在漫长的运行.
中一次次伤害你但是像这样的问题的答案通常是“我没有创建它,我必须忍受它”,您需要测试这两种方法。
要获得有意义的测试,您必须创建远不止 200 行的行。
我使用这种方法创建了一些假数据:
create table games
(
id character varying(32) not null,
access character varying(50),
deleted character varying(1) default 'N'::character varying ,
timePlayed character varying(50),
description text ,
name character varying(64)
);
insert into games(id, access, timeplayed, description, name)
select g.id::text,
'full',
'all night long',
'some description',
case
when random() < 0.1 then 'Vinod.'
when random() < 0.2 then 'Vivos.'
when random() < 0.3 then 'Doniv.'
when random() < 0.5 then 'Novid.'
when random() < 0.6 then 'Somevid.'
when random() < 0.7 then 'OtherVid.'
when random() < 0.8 then 'Fonod.'
else 'Barnod.'
end || 'Game' || (random() * 999 + 1)::int
from generate_series(1,1e6) as g(id);
create index on games (name varchar_pattern_ops);
create index on games ( (split_part(name, '.', 1)) );
vacuum analyze games;
以上生成了 100 万行,其中 10% 以 Vinod.
请注意,我只在 .
上拆分,而不是在 .Game
上拆分 - 对我来说更有意义:选择第一个由点分隔的元素。
缓存 table 时,LIKE 查询的执行时间约为 70 毫秒,使用 split_part()
的查询的执行时间约为 25 毫秒(Windows 10 台笔记本电脑和 Postgres 13.2)。所以 split_part() 和基于表达式的索引似乎是赢家。
explain (analyze, buffers)
select *
from games
where name like 'Vinod.Game%';
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------
Index Scan using games_name_idx on games (cost=0.42..12490.69 rows=99080 width=59) (actual time=0.018..67.705 rows=100189 loops=1)
Index Cond: (((name)::text ~>=~ 'Vinod.Game'::text) AND ((name)::text ~<~ 'Vinod.Gamf'::text))
Filter: ((name)::text ~~ 'Vinod.Game%'::text)
Buffers: shared hit=99863
Planning Time: 0.669 ms
Execution Time: 70.365 ms
explain (analyze, buffers)
select *
from games
where split_part(name, '.', 1) = 'Vinod'
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Index Scan using games_split_part_idx on games (cost=0.42..11657.32 rows=99400 width=59) (actual time=0.025..20.793 rows=100189 loops=1)
Index Cond: (split_part((name)::text, '.'::text, 1) = 'Vinod'::text)
Buffers: shared hit=11450
Planning Time: 0.098 ms
Execution Time: 23.605 ms
但再次声明:解决问题的正确方法是规范化数据模型。