我有一个名为profile
的表格,我想按顺序排列它们。每列都是JSONB列或TEXT列。我并不需要很大程度的确定性,所以通常我订购如下:
SELECT * FROM profile ORDER BY LENGTH(CONCAT(profile.*)) DESC;
但是,这很慢,所以我想创建一个索引。但是,这不起作用:
CREATE INDEX index_name ON profile (LENGTH(CONCAT(*))
也不是
CREATE INDEX index_name ON profile (LENGTH(CONCAT(CAST(* AS TEXT))))
不能说我很惊讶。声明这个索引的正确方法是什么?
答案 0 :(得分:2)
要测量文本表示中行的大小,您只需将整行转换为文本,这比连接各列快得多:
SELECT length(profile::text) FROM profile;
但是这个表达式在索引中存在3个(或4个)问题:
profile::text
不接受语法简写CREATE INDEX
,您需要添加额外的括号或默认为标准语法cast(profile AS text)
仍然存在同样的问题:@jjanes already discussed:在索引表达式中只允许IMMUTABLE
个函数并将行类型转换为text
不会通过此要求。您可以构建假的IMMUTABLE
包装函数,就像Jeff概述的那样。
有一个固有的歧义(也适用于Jeff的答案!):如果你的列名与表名相同(这是常见的情况)你无法在CREATE INDEX
中引用行类型,因为标识符始终首先解析为列名。
与原始文件的细微差别:这会将列分隔符,行装饰器和可能的转义字符添加到text
表示中。对你的用例来说无关紧要。
然而,我会建议一个更激进的替代方案作为行的大小的原始指标:pg_column_size()
。更短更快,避免问题 1 , 3 和 4 :
SELECT pg_column_size(profile) FROM profile;
问题 2 仍然存在,pg_column_size()
也只是STABLE
。您可以创建一个简单而廉价的SQL包装函数:
CREATE OR REPLACE FUNCTION pg_column_size(profile)
RETURNS int LANGUAGE sql IMMUTABLE AS
'SELECT pg_catalog.pg_column_size($1)';
然后按照@jjanes的说明进行操作。更多细节:
请注意,我创建了以行类型profile
作为参数的函数。 Postgres允许函数重载,这就是我们可以使用相同函数名的原因。现在,当我们将匹配的行类型提供给pg_column_size()
时,我们的自定义函数会根据 function type resolution 规则进行更紧密的匹配,而不是多态系统函数。或者,使用单独的名称,并可能使函数具有多态性......
相关:
答案 1 :(得分:1)
您可以声明一个错误标记为“不可变”的函数,并在其上构建索引。
CREATE OR REPLACE FUNCTION len_immut(record)
RETURNS int
LANGUAGE plperl
IMMUTABLE
AS $function$
## This function lies about its immutability.
## Use it with care. It is useful for indexing
## entire table rows.
return length(join ",", values %{$_[0]});
$function$
然后
create index on profile (len_immut(profile));
SELECT * FROM profile ORDER BY len_immut(profile) DESC;
由于该函数被错误地标记为immutable
,如果您执行在表上添加或删除列或更改列类型等操作,索引可能会过期。