Arc*_*oob 6 postgresql performance postgresql-9.6 query-performance
我有一个表,其中有一个 jsonb 列,其中包含一个对象数组。
每一行看起来都是这样。
[{grade: 'A', subject: 'MATH'}, {grade: 'B', subject: 'PHY'}...]
Run Code Online (Sandbox Code Playgroud)
现在通过这篇文章查询它/sf/answers/2141445351/。
问题来了,完成查询所有具有grade
IN (A, B, C) 的学生的计数至少需要 2.4 秒。
我想得到一些关于索引的帮助,因为我尝试过的索引没有做任何事情。
DROP INDEX idx_subjects_subject;
DROP INDEX idx_subjects_grade;
CREATE INDEX idx_subjects_subject ON results USING GIN((subjects-> 'subject'));
CREATE INDEX idx_subjects_grade ON results USING GIN((subjects-> 'grade'));
Run Code Online (Sandbox Code Playgroud)
还做了(单独):
DROP INDEX idx_subjects_standard;
CREATE INDEX idx_subjects_standard ON results USING GIN(subjects);
Run Code Online (Sandbox Code Playgroud)
我是这样问他们的。
SELECT COUNT(*)
FROM results
WHERE EXISTS
(
SELECT 1
FROM jsonb_array_elements(subjects) AS j(data)
WHERE (data #>> '{subject}') LIKE '%MATH%'
AND
(data #>> '{grade}') IN ('A', 'B', 'C')
)
AND
"examYear" = '2010'
AND
"examType" = 'CSEE'
;
Run Code Online (Sandbox Code Playgroud)
我也试过这样查询:
SELECT COUNT(*)
FROM results
WHERE EXISTS
(
SELECT 1
FROM jsonb_array_elements(subjects) AS j(data)
WHERE data @> '{"subject": "B/MATH", "grade": "A"}'
OR data @> '{"subject": "B/MATH", "grade": "B"}'
OR data @> '{"subject": "B/MATH", "grade": "C"}'
)
AND
"examYear" = '2010'
AND
"examType" = 'CSEE'
;
Run Code Online (Sandbox Code Playgroud)
但这给了我相反的效果(3 秒查询)。
这是我的解释分析块。
"Aggregate (cost=1888617.19..1888617.20 rows=1 width=8) (actual time=2517.116..2517.117 rows=1 loops=1)"
" -> Bitmap Heap Scan on results (cost=470767.18..1888021.96 rows=238090 width=0) (actual time=680.456..2514.633 rows=24002 loops=1)"
" Recheck Cond: ("examYear" = '2010'::text)"
" Rows Removed by Index Recheck: 557945"
" Filter: (("examType" = 'CSEE'::text) AND (SubPlan 1))"
" Rows Removed by Filter: 500054"
" Heap Blocks: exact=40156 lossy=53452"
" -> Bitmap Index Scan on idx_results_subjects (cost=0.00..470707.66 rows=528405 width=0) (actual time=672.375..672.375 rows=524056 loops=1)"
" Index Cond: ("examYear" = '2010'::text)"
" SubPlan 1"
" -> Function Scan on jsonb_array_elements j (cost=0.00..2.13 rows=1 width=0) (actual time=0.003..0.003 rows=0 loops=458487)"
" Filter: (((data #>> '{subject}'::text[]) ~~ '%MATH%'::text) AND ((data #>> '{grade}'::text[]) = ANY ('{A,B,C}'::text[])))"
" Rows Removed by Filter: 8"
"Planning time: 0.126 ms"
"Execution time: 2517.145 ms"
Run Code Online (Sandbox Code Playgroud)
我在 postgresql: 9.6.1
Mad*_*ist 10
以下代码将能够在 jsonb 列上使用 GIN 索引:
SELECT COUNT(*) FROM results
WHERE subjects @> '[{"subject": "B/MATH", "grade": "A"}]'
Run Code Online (Sandbox Code Playgroud)
与您的示例不同的是,我不解压缩数组,我只是直接查询 jsonb 列。GIN 索引可用于 jsonb 列上的 @> 运算符,但不能用于任意函数。您最终使用了 @> 运算符,但不是在实际的 jsonb 列上,而是在您提取的 json 上。
在开头用通配符索引 LIKE 条件要困难得多。为此,您需要一个三元组索引,但我不知道如何将一个索引用于 jsonb 列中的数组内的数据(如果可能的话)。您应该强烈考虑以更正式的方式存储此信息,而根本不必使用 LIKE。
开头没有通配符的 LIKE'MATH%'
在某些情况下可以使用 btree 索引(有关详细信息,请参阅https://www.postgresql.org/docs/9.5/static/indexes-types.html,您有为此要注意语言环境)。
如果这不是数组,而是 jsonb 列中的普通对象,则可以使用功能索引,例如
CREATE INDEX ON results((subject->>'grade'));
Run Code Online (Sandbox Code Playgroud)
这也许也可以通过 jsonb 中的数组实现,但我现在想不出一个合理的方法来做到这一点。
您当前的架构使所有内容的编写难度增加了一个数量级,并且很难甚至不可能正确利用索引。如果您有选择,请考虑将此数据存储在一个带有主题和成绩列的表中,这会使这个问题变得容易得多。
归档时间: |
|
查看次数: |
10777 次 |
最近记录: |