Связывание нескольких строк и столбцов для предложения WHERE IN в PostgreSQL - PullRequest
4 голосов
/ 20 марта 2012

Итак, я хочу подготовить запрос примерно так:

SELECT id FROM users WHERE (branch, cid) IN $1;

и затем привязать к нему набор строк переменной длины, например (('a','b'),('c','d')).

Другими словами, что-то вроде:

pg_prepare($users, 'users_query', 'SELECT id FROM users WHERE (branch, cid) IN $1');
$result = pg_execute($users, 'users_query', array("(('a','b'),('c','d'))");

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

1 Ответ

1 голос
/ 21 марта 2012

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

prepare s4 as
select id from users
join (select * from (values ($1,$2),($3,$4)) as v(branch, cid)) as p
using (branch, cid);

explain analyze execute s4('b11','c11','b1234','c1234');
                                                    QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.00..16.65 rows=1 width=4) (actual time=0.199..0.234 rows=2 loops=1)
   ->  Values Scan on "*VALUES*"  (cost=0.00..0.03 rows=2 width=64) (actual time=0.002..0.003 rows=2 loops=1)
   ->  Index Scan using u_i on users  (cost=0.00..8.30 rows=1 width=16) (actual time=0.111..0.112 rows=1 loops=2)
         Index Cond: ((users.branch = "*VALUES*".column1) AND (users.cid = "*VALUES*".column2))
 Total runtime: 0.425 ms

Кажется, ваша реальная проблема заключается в том, как связать динамически определенное количество пар значений с вашим sql. Мой PHP ужасно ржавый, и чтение онлайн-документов напомнило мне, насколько я ненавижу его, но я думаю, что следующее будет делать то, что вы хотите, построив sql формы выше с количеством заполнителей пары значений, динамически созданных на основе количества значений Вы хотите связать. У меня нет удобной среды выполнения php, поэтому я даже не проверил, правильна ли она синтаксически, но вы должны быть в состоянии понять идею и устранить любые тривиальные ошибки в моем примере.

$values = array(
  'a', 'b',
  'c', 'd',
  // etc...
);

$value_placeholders = "";
$sep = "";
for ($i=1; $i <= $count($values); $i+=2) {
  $value_placeholders = $value_placeholders . sprintf("($%u,$%u),", $i, $i+1) . $sep
  $sep = ",";
}

$sql =
  'select id from users ' .
  'join (select * from (values ' . $value_placeholders . ') as v(branch, cid)) as p' .
  'using (branch, cid)';

$result = pg_query_params($dbconn, $sql, $values);

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

create index u_i2 on users ((branch||cid));
prepare sa as select id from users where branch||cid in (select unnest($1::text[]));
explain analyze execute sa(ARRAY['b1c1','b1234c1234']);
                                                      QUERY PLAN                                                      
----------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=12.17..645.78 rows=50000 width=4) (actual time=0.169..0.188 rows=2 loops=1)
   ->  HashAggregate  (cost=0.02..0.03 rows=1 width=32) (actual time=0.018..0.019 rows=2 loops=1)
         ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.010..0.011 rows=2 loops=1)
   ->  Bitmap Heap Scan on users  (cost=12.14..638.25 rows=500 width=16) (actual time=0.082..0.082 rows=1 loops=2)
         Recheck Cond: ((users.branch || users.cid) = (unnest($1)))
         ->  Bitmap Index Scan on u_i2  (cost=0.00..12.02 rows=500 width=0) (actual time=0.078..0.078 rows=1 loops=2)
               Index Cond: ((users.branch || users.cid) = (unnest($1)))
 Total runtime: 0.275 ms

Примечание: я не смог найти индексированный доступ к парам строк. Но если вы создадите функциональный индекс для конкатенации двух полей, а затем предоставите связанный массив таких конкатенаций, вы получите хорошее быстрое сканирование nest-loop-index-scan.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...