PostgreSQL:在所有表字段的长度上创建索引

时间:2015-12-07 21:00:09

标签: performance postgresql database-performance database-indexes

我有一个名为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))))

不能说我很惊讶。声明这个索引的正确方法是什么?

2 个答案:

答案 0 :(得分:2)

要测量文本表示中行的大小,您只需将整行转换为文本,这比连接各列快得多:

SELECT length(profile::text) FROM profile;

但是这个表达式在索引中存在3个(或4个)问题:

  1. profile::text不接受语法简写CREATE INDEX,您需要添加额外的括号或默认为标准语法cast(profile AS text)

  2. 仍然存在同样的问题:@jjanes already discussed:在索引表达式中只允许IMMUTABLE个函数并将行类型转换为text不会通过此要求。您可以构建假的IMMUTABLE包装函数,就像Jeff概述的那样。

  3. 有一个固有的歧义(也适用于Jeff的答案!):如果你的列名与表名相同(这是常见的情况)你无法在CREATE INDEX中引用行类型,因为标识符始终首先解析为列名。

  4. 与原始文件的细微差别:这会将列分隔符,行装饰器和可能的转义字符添加到text表示中。对你的用例来说无关紧要。

  5. 然而,我会建议一个更激进的替代方案作为行的大小的原始指标: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,如果您执行在表上添加或删除列或更改列类型等操作,索引可能会过期。