У меня есть таблица под названием 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))))
Не могу сказать, что я удивлен. Каков правильный способ объявления этого индекса?
Ответ 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
, индекс может устареть, если вы делаете такие вещи, как добавление или удаление столбцов в таблице или изменение типов столбцов.
Ответ 2
Чтобы измерить размер строки в текстовом представлении, вы можете просто передать всю строку в текст, что намного быстрее, чем объединение отдельных столбцов:
SELECT length(profile::text) FROM profile;
Но есть 3 (или 4) вопроса с этим выражением в индексе:
-
Сокращение синтаксиса profile::text
не принимается в CREATE INDEX
, вам нужно добавить дополнительные скобки или по умолчанию стандартный синтаксис cast(profile AS text)
-
По-прежнему та же проблема, что @jjanes уже обсуждался: только функции IMMUTABLE
допускаются в индексных выражениях и отличает тип строки до text
. не пропустите это требование. Вы могли бы создать фальшивую <обложку IMMUTABLE
, как показано на рисунке Джеффа.
-
Существует встроенная неоднозначность (применимая к ответу Джеффа!): если у вас есть имя столбца, такое же, как имя таблицы (это обычный случай), вы не может ссылаться на тип строки в CREATE INDEX
, так как идентификатор всегда сначала разрешает имя столбца.
-
Незначительная разница с вашим оригиналом: добавляет разделители столбцов, декораторы строк и, возможно, escape-символы в представление 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()
, наша пользовательская функция более точно соответствует разрешению типа функции и выбирается вместо функции полиморфной системы. В качестве альтернативы, используйте отдельное имя и, возможно, сделайте также функцию полиморфной...
по теме: