Учитывая строку, которая может содержать несколько экземпляров разделителя, я хочу сгенерировать все подстроки, начинающиеся после этого символа.
Например, учитывая строку вроде 'a.b.c.d.e'
(или массив {a,b,c,d,e}
, я полагаю), я хочу создать массив вроде:
{a.b.c.d.e, b.c.d.e, c.d.e, d.e, e}
Предполагается использование в качестве триггера для заполнения столбца, чтобы упростить запрос частей имени домена (т. Е. Найти все q.x.t.com
для запроса t.com
) при записи в другой столбец.
Это кажется странным способом решить эту проблему (и это может быть очень хорошо), но теперь мне любопытно, как такую функцию можно написать в (Postgres ') SQL.
Это доменные имена электронной почты, поэтому трудно сказать, какое максимальное количество элементов есть, но, безусловно, подавляющее большинство будет <5.
источник
Ответы:
Я не думаю, что вам нужна отдельная колонка здесь; это проблема XY. Вы просто пытаетесь выполнить поиск по суффиксу. Есть два основных способа оптимизировать это.
Превратить суффикс-запрос в префиксный запрос
Вы в основном делаете это, полностью изменяя.
Сначала создайте индекс на обратной стороне вашего столбца:
Затем запрос с использованием того же:
Вы можете добавить
UPPER
вызов, если хотите сделать его нечувствительным к регистру:Индексы триграмм
Другой вариант - индексы триграмм. Вы обязательно должны использовать это, если вам нужны инфиксные запросы (
LIKE 'something%something'
илиLIKE '%something%'
запросы типа).Сначала включите расширение индекса триграммы:
(Это должно прийти с PostgreSQL из коробки без какой-либо дополнительной установки.)
Затем создайте индекс триграммы в вашем столбце:
Затем просто выберите:
Опять же, вы можете добавить,
UPPER
чтобы сделать его нечувствительным к регистру, если вам нравится:Ваш вопрос как написано
Индексы триграмм на самом деле работают, используя несколько более общую форму того, что вы просите скрытно. Он разбивает строку на части (триграммы) и строит на их основе индекс. Затем индекс можно использовать для поиска совпадений гораздо быстрее, чем последовательное сканирование, но для запросов с инфиксными, а также суффиксными и префиксными запросами. Всегда старайтесь не изобретать то, что кто-то еще разработал, когда можете.
кредиты
Эти два решения в значительной степени дословны от выбора метода текстового поиска PostgreSQL . Я настоятельно рекомендую прочитать его для подробного анализа доступных вариантов текстового поиска в PotsgreSQL.
источник
Я думаю, что это мой любимый.
РЯДЫ
МАССИВЫ
источник
РЯДЫ
ИЛИ
МАССИВЫ
ИЛИ
источник
Заданный вопрос
Тестовая таблица:
Рекурсивный CTE в ЛАТЕРАЛЬНОМ подзапросе
CROSS JOIN LATERAL
(, LATERAL
Для краткости) является безопасным, так как совокупный результат подзапроса всегда возвращает строку. Ты получаешь ...str = ''
в базовой таблицеstr IS NULL
в базовой таблицеЗавершено использование дешевого конструктора массива в подзапросе, поэтому нет агрегации во внешнем запросе.
Яркий пример возможностей SQL, но издержки rCTE могут помешать максимальной производительности.
Грубая сила для тривиального числа элементов
Для вашего случая с тривиально малым количеством элементов простой подход без подзапроса может быть быстрее:
Предполагая максимум 5 элементов, как вы прокомментировали. Вы можете легко расширить для большего.
Если в данном домене меньше элементов, избыточные
substring()
выражения возвращают NULL и удаляются с помощьюarray_remove()
.На самом деле, выражение from (
right(str, strpos(str, '.')
), вложенное в несколько раз, может быть быстрее (хотя и неудобным для чтения), поскольку функции регулярных выражений стоят дороже.Вилка запроса @ Dudu
Умный запрос @ Dudu может быть улучшен с помощью
generate_subscripts()
:Также используется
LEFT JOIN LATERAL ... ON true
для сохранения возможных строк со значениями NULL.Функция PL / pgSQL
Схожая логика с rCTE. Значительно проще и быстрее, чем у вас есть:
OUT
Параметр автоматически возвращается в конце функции.Там нет необходимости инициализировать
result
, потому чтоNULL::text[] || text 'a' = '{a}'::text[]
.Это работает только с
'a'
правильной типизацией.NULL::text[] || 'a'
(строковый литерал) вызовет ошибку, потому что Postgres выбираетarray || array
оператор.strpos()
возвращает,0
если точка не найдена, поэтомуright()
возвращает пустую строку и цикл заканчивается.Это, наверное, самое быстрое из всех решений здесь.
Все они работают в Postgres 9.3+
(за исключением краткой записи фрагмента массива
arr[3:]
. Я добавил верхнюю границу в скрипте, чтобы она работала в pg 9.3:.arr[3:999]
)SQL Fiddle.
Другой подход к оптимизации поиска
Я с @ jpmc26 (и вы сами): совершенно другой подход будет предпочтительнее. Мне нравится комбинация jpmc26
reverse()
и atext_pattern_ops
.Индекс триграммы будет лучше для частичных или нечетких совпадений. Но так как вас интересуют только целые слова , полнотекстовый поиск является еще одним вариантом. Я ожидаю значительно меньший размер индекса и, следовательно, лучшую производительность.
pg_trgm, а также FTS поддерживают запросы без учета регистра , кстати.
Имена хостов, такие как
q.x.t.com
илиt.com
(слова со встроенными точками), идентифицируются как тип «хост» и рассматриваются как одно слово. Но в FTS также есть сопоставление префиксов (что иногда упускается из виду). Руководство:Используя умную идею @ jpmc26 с
reverse()
, мы можем сделать эту работу:Который поддерживается индексом:
Обратите внимание на
'simple'
конфигурацию. Мы не хотим, чтобы в'english'
конфигурации по умолчанию использовались основа или тезаурус .В качестве альтернативы (с большим количеством возможных запросов) мы могли бы использовать новую возможность поиска по фразе текстового поиска в Postgres 9.6. Примечания к выпуску:
Запрос:
Замените dot (
'.'
) на space (' '
), чтобы синтаксический анализатор не классифицировал t.com как имя хоста, и вместо этого используйте каждое слово в качестве отдельной лексемы.И соответствующий индекс, чтобы пойти с ним:
источник
Я придумал что-то наполовину работоспособное, но я хотел бы получить отзывы о подходе. Я написал очень мало PL / pgSQL, поэтому чувствую, что все, что я делаю, довольно хакерское, и я удивлен, когда это работает.
Тем не менее, это то, где я попал:
Это работает так:
источник
Я использую оконную функцию:
Результат:
источник
Вариант решения от @Dudu Markovitz, который также работает с версиями PostgreSQL, которые еще не распознают [i:]:
источник