Postgres - максимально соответствует началу поискового параметра - PullRequest
2 голосов
/ 16 декабря 2011

У меня есть таблица postgres, которая содержит очень большое количество чисел различной длины.Например, первый столбец таблицы будет выглядеть так:

  1. 1
  2. 12
  3. 13
  4. 134
  5. 135
  6. 136
  7. 1362
  8. 1363
  9. ...

Мне нужен postgres для возврата строки, которая соответствуеткак можно больше начала данного числа.Например, цифра «1358302» должна соответствовать строке 5 («135»), а «1362304» должна соответствовать строке 7 («1362»).

Какой SQL-код можно использовать для достижения этой цели?

(цифры еще не отсортированы там постоянно, поэтому, если вам нужно переключить тип данных между целым числом или строкой для простоты / производительности, я все равно могу это сделать!)

Ответы [ 3 ]

2 голосов
/ 16 декабря 2011
SELECT n
FROM your_table
WHERE n IN (
    select $1/(10^i)::int8 from generate_series(0,floor(log($1))::int) i
)
ORDER BY n DESC
LIMIT 1
1 голос
/ 16 декабря 2011

Рассмотрим следующую демонстрацию.

Временная таблица для тестирования:

CREATE TEMP TABLE nr(id int, nr numeric);
INSERT INTO nr VALUES
(1,1),(2,12),(3,13),(4,134),(5,135),
(6,136),(7,1362),(8,1363);

Запрос:

SELECT nr
FROM   nr
WHERE  '1362235' ~~ (nr::text || '%')
ORDER  BY length(nr::text) DESC
LIMIT  1;

Доставляет самое длинное совпадение по запросу. Работа со строками (не числами) упрощает задачу. Вы можете изменить тип данных в своей таблице или привести значения в запросе, как я.

Если вам нужно, чтобы он был fast , измените тип на text и индекс nr. Подобный поиск может использовать простой индекс btree (по умолчанию) и будет очень быстрый.

CREATE INDEX nr_nr_idx ON nr (nr);

Если языковой стандарт вашей базы данных не 'C' или 'POSIX', используйте text_pattern_ops, как прокомментировал @Gavin, или оператор LIKE (~~) не может использовать его даже для запуска столбца, поскольку порядок сортировки зависит от региона.

CREATE INDEX nr_nr_idx ON nr (nr text_pattern_ops);

Если вам нужен числовой тип столбца таблицы, есть еще один вариант. Используйте индекс для выражения , например, так:

CREATE INDEX nr_nr_idx ON nr ((nr::text) text_pattern_ops);

Приведенный выше запрос может использовать такой индекс, поскольку это выражение соответствует выражению индекса.

0 голосов
/ 16 декабря 2011

Грубая сила всегда работает.

select max(n)
from your_table
where n in (1, 13, 136, 1362, 13623, 136230, 1362304);

Вы можете сгенерировать предложение IN в коде приложения или в хранимой процедуре.

Или, как это сделал филиппрем, сгенерируйте его в суб-выборе После загрузки таблицы с миллионом целых чисел и просмотра планов выполнения, кажется, это лучший баланс гибкости и производительности. ORDER BY стоит дорого за большими столами, и я думаю, что в этом нет необходимости; самый длинный матч будет самым большим числом. (Предполагая неотрицательные целые числа.)

SELECT max(n)
FROM numerals
WHERE n IN (
    select 1362304/(10^i)::int8 
    from generate_series(0,floor(log(1362304))::int) i
);

Буквальное значение 1362304 встречается в двух местах. Замените его параметром.

Вероятно, вы можете вдвое сократить время выполнения, выполнив внутреннее объединение подзапроса вместо использования подзапроса в предложении WHERE.

with prefixes as (
  select 1362304/(10^i)::int8 prefix
  from generate_series(0,floor(log(1362304))::int) i
)
select max(n)
from numerals 
inner join prefixes 
        on prefixes.prefix = numerals.n

Эта последняя версия работает за 0,22 мс (не опечатка) на миллион строк.

...