Способ попробовать несколько SELECT до получения результата?

Что делать, если я хочу искать одну строку в таблице с уменьшающейся точностью, например? например:

SELECT * FROM image WHERE name LIKE 'text' AND group_id = 10 LIMIT 1

Если это не дает мне результата, попробуйте следующее:

SELECT * FROM image WHERE name LIKE 'text' LIMIT 1

И когда это не дает мне результата, попробуйте следующее:

SELECT * FROM image WHERE group_id = 10 LIMIT 1

Можно ли сделать это только с одним выражением?

Также возникает проблема, когда у меня нет двух, но, например, три или более параметров поиска. Есть ли общее решение для этого? Конечно, это пригодится, когда результат поиска будет отсортирован по его релевантности.

Ответ 1

LIKE без подстановочного символа эквивалентен =. Предполагая, что вы на самом деле означали name = 'text'.

Индексы являются ключом к производительности.

Настройка тестирования

CREATE TABLE image (
  image_id serial PRIMARY KEY
, group_id int NOT NULL
, name     text NOT NULL
);

В идеале вы создаете два индекса (помимо первичного ключа):

CREATE INDEX image_name_grp_idx ON image (name, group_id);
CREATE INDEX image_grp_idx ON image (group_id);

Вторые могут не понадобиться, в зависимости от распределения данных и других деталей. Объяснение здесь:

Query

Это должен быть самый быстрый запрос для вашего случая:

SELECT * FROM image WHERE name = 'name105' AND group_id = 10
UNION ALL
SELECT * FROM image WHERE name = 'name105'
UNION ALL
SELECT * FROM image WHERE group_id = 10
LIMIT  1;

SQL Fiddle.

Предложение LIMIT применяется ко всему запросу. Postgres достаточно умный не выполнять более поздние ножки UNION ALL, как только он найдет достаточно строк, чтобы удовлетворить LIMIT. Следовательно, для совпадения в первом SELECT запроса вывод EXPLAIN ANALYZE выглядит следующим образом (прокрутите вправо!):

Limit  (cost=0.00..0.86 rows=1 width=40) (actual time=0.045..0.046 rows=1 loops=1)
  Buffers: local hit=4
  ->  Result  (cost=0.00..866.59 rows=1002 width=40) (actual time=0.042..0.042 rows=1 loops=1)
        Buffers: local hit=4
        ->  Append  (cost=0.00..866.59 rows=1002 width=40) (actual time=0.039..0.039 rows=1 loops=1)
              Buffers: local hit=4
              ->  Index Scan using image_name_grp_idx on image  (cost=0.00..3.76 rows=2 width=40) (actual time=0.035..0.035 rows=1 loops=1)
                    Index Cond: ((name = 'name105'::text) AND (group_id = 10))
                    Buffers: local hit=4
              ->  Index Scan using image_name_grp_idx on image  (cost=0.00..406.36 rows=500 width=40) (never executed)
                    Index Cond: (name = 'name105'::text)
              ->  Index Scan using image_grp_idx on image  (cost=0.00..406.36 rows=500 width=40) (never executed)
                    Index Cond: (group_id = 10)
Total runtime: 0.087 ms

Смелый акцент мой.

Не добавляйте предложение ORDER BY, это лишает эффект. Затем Postgres должен будет рассмотреть все строки перед возвратом верхней строки.

Заключительные вопросы

Существует ли для этого общее решение?

Это общее решение. Добавьте столько операторов SELECT, сколько хотите.

Конечно, это пригодится, когда результат поиска будет отсортирован по его релевантности.

В результате с LIMIT 1 есть только одна строка. Вид сортировки пустот.

Ответ 2

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

Создание числа параметров будет динамичным, что сделает его более сложным. В зависимости от вашей версии PostgreSQL (и расширения, доступного вам) вы можете передать hstore или json и динамически построить запрос.

Может быть, не самый большой ответ SO, но это больше, чем комментарий и, надеюсь, некоторая пища для размышлений.

Ответ 3

Я не думаю, что есть что-то не так, если вы выполняете эти запросы отдельно, пока не найдете нужный результат. Хотя есть способы объединить их в один запрос, они в конечном итоге становятся более сложными и медленными, чего вы не хотели.

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

Ответ 4

SELECT *, 
CASE WHEN name like 'text' AND group_id = 10 THEN 1
WHEN name like 'text' THEN 2
WHEN group_id = 10 THEN 3
ELSE 4
END ImageRank
FROM image
WHERE ImageRank <> 4
ORDER BY ImageRank ASC
LIMIT 1

Это был бы подход с псевдорешением, но я не совсем уверен, разрешил ли ему синтаксис в вашем сценарии