Met*_*lis 9 postgresql database-design datatypes composite-types postgresql-10
在 jsonb 列和相同结构的复合类型列之间进行选择需要考虑哪些因素?
例如,考虑 Postgres 文档中使用的类似列:
CREATE TYPE inventory_item AS (
name text,
supplier_id integer,
price numeric
);
Run Code Online (Sandbox Code Playgroud)
这种方法与镜像这种结构的 jsonb 列之间的权衡是什么?
例如,我怀疑复合类型不需要存储每条记录的键名,而 jsonb 类型需要这样做。
其实这不是性能问题
此外,JSON 有一个用例,但对于复合类型,您几乎不想使用它们,因为将它们规范化为它们自己的关系几乎总是更好。但有一个例外——当您要围绕一组数据创建整个库时,复合类型可以使一切变得更好。例如PostGIS 中的stdaddr就是这样的一个例子。该类型表示物理地址,并且多种事物可以采用该类型(例如 PostGIS 地理编码器)。
所以你可以做
CREATE TABLE foo ( foo_id int, address postgis.stdaddr );
Run Code Online (Sandbox Code Playgroud)
现在您可以相对轻松地获取地理编码信息,并且只需传递一个值而不是 15。
问题中定义了TYPE inventory_item (与指南中相同),因此我们只需定义具有复合(ROW)类型的表tc和具有 JSONb 类型的表tj。
-- drop table tc; drop table tj;
CREATE TABLE tc (id serial, x inventory_item);
CREATE TABLE tj (id serial, x JSONb);
EXPLAIN ANALYSE
INSERT INTO tc(x) VALUES
(ROW('fuzzy dice', 42, 1.99)),
(ROW('test pi', 3, 3.1415))
; -- Execution Time: try1 0.386 ms; try2 0.559 ms; try3 0.102 ms; ...
EXPLAIN ANALYSE
INSERT INTO tj(x) VALUES
('{"name":"fuzzy dice", "supplier_id":42, "price":1.99}'::jsonb),
('{"name":"test pi", "supplier_id":3, "price":3.1415}'::jsonb)
; -- Execution Time: try1 0.343; try2 0.355 ms; try3 0.112 ms; ...
Run Code Online (Sandbox Code Playgroud)
当然,我们需要循环等一些复杂的东西来测试......但似乎“可比”时间,没有太大区别。
仅检索原始数据类型。需要良好的基准,但让我们想象一些简单的事情只是为了检查大的差异。
EXPLAIN ANALYSE SELECT x, i FROM tc, generate_series(1,999999) g(i);
EXPLAIN ANALYSE SELECT x, i FROM tj, generate_series(1,999999) g(i);
Run Code Online (Sandbox Code Playgroud)
又没有区别。两者都带有“执行时间:~460”。
EXPLAIN ANALYSE
SELECT i, id, (x).name, (x).supplier_id, (x).price
FROM tc, generate_series(1,999999) g(i)
; -- Execution Time: ~490 ms
EXPLAIN ANALYSE
SELECT i, tj.id, t.*
FROM tj, generate_series(1,999999) g(i),
LATERAL jsonb_populate_record(null::inventory_item, tj.x) t
; -- Execution Time: ~650 ms
Run Code Online (Sandbox Code Playgroud)
看起来将 JSONb 对象转换为 SQL 行非常快!看起来是二进制转换:我们可以假设该函数jsonb_populate_record通过inventory_item内部定义将 JSONb 类型映射到 SQL。
而且它比复合表更快。
EXPLAIN ANALYSE
SELECT i, (x).supplier_id+i, (x).price+0.01
FROM tc, generate_series(1,999999) g(i)
; -- Execution Time: ~800 ms
EXPLAIN ANALYSE
SELECT i, t.supplier_id+i, t.price+0.01
FROM tj, generate_series(1,999999) g(i),
LATERAL jsonb_populate_record(null::inventory_item, tj.x) t
; -- Execution Time: ~620 ms
Run Code Online (Sandbox Code Playgroud)
也许计算需要约 150 毫秒,所以预期时间相同...上面的示例中存在一些错误,需要更好的基准来检查真正的差异。
检查从文本投射的相对时间。
EXPLAIN ANALYSE -- (supposed to) cast from binary
SELECT i, id, x->>'name' as name,
(x->'supplier_id')::int as supplier_id, (x->'price')::float as price
FROM tj, generate_series(1,999999) g(i)
; -- Execution Time: ~1600 ms
EXPLAIN ANALYSE -- cast from text
SELECT i, id, x->>'name' as name,
(x->>'supplier_id')::int as supplier_id, (x->>'price')::float as price
FROM tj, generate_series(1,999999) g(i)
; -- Execution Time: ~1600 ms
Run Code Online (Sandbox Code Playgroud)
时间长且相同。似乎(x->'supplier_id')::int它只是(x->>'supplier_id')::int or的糖语法(x->'supplier_id')::text::int。
PS:这个答案也是对另一个问题的补充,关于“使用 JSONb 进行二进制到二进制转换”。
| 归档时间: |
|
| 查看次数: |
2055 次 |
| 最近记录: |