2015-05-18 2 views
2

Мы используем sql в Postgres, используя ltree для обработки некоторых иерархических данных.Cstring in Postgres ltree query

Однако, когда мы используем синтаксис синтаксиса, запрос будет очень медленным, и в плане запроса будет показано, что он фактически накладывается на cstring, а затем lquery. План

explain analyse SELECT DISTINCT 
       subltree(metric, 0, 6) metric, 
       FROM demo 
       WHERE 
        metric ~ ('s.a.b' || '.*')::lquery; 

Запрос:

Unique (cost=144235.79..144273.81 rows=3802 width=100) (actual time=11822.107..11822.107 rows=1 loops=1) 
    -> Sort (cost=144235.79..144245.29 rows=3802 width=100) (actual time=11822.107..11822.107 rows=1 loops=1) 
     Sort Key: (subltree(metric, 0, 6)) 
     Sort Method: quicksort Memory: 25kB 
     -> Seq Scan on demo (cost=0.00..144009.71 rows=3802 width=100) (actual time=1940.149..11822.093 rows=1 loops=1) 
       Filter: (metric ~ ('s.a.b.*'::cstring)::lquery) 
       Rows Removed by Filter: 3714258 
Total runtime: 11822.139 ms 

Однако, когда мы используем SQL ниже, то кажется, что все будет в порядке:

explain analyse SELECT DISTINCT 
       subltree(metric, 0, 6) metric, 
       FROM demo 
       WHERE 
        metric_name ~ (select ('s.a.b' || '.*')::lquery); 

Запрос План:

Unique (cost=13294.81..13313.85 rows=3809 width=76) (actual time=0.122..0.126 rows=6 loops=1) 
InitPlan 1 (returns $0) 
-> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.007..0.007 rows=1 loops=1) 
-> Sort (cost=13294.79..13304.32 rows=3809 width=76) (actual time=0.121..0.122 rows=6 loops=1) 
    Sort Key: metric 
    Sort Method: quicksort Memory: 26kB 
    -> Bitmap Heap Scan on demo (cost=589.93..13068.25 rows=3809 width=76) (actual time=0.103..0.109 rows=6 loops=1) 
      Recheck Cond: (metric ~ $0) 
      -> Bitmap Index Scan on metric_gist_idx (cost=0.00..588.98 rows=3809 width=0) (actual time=0.097..0.097 rows=6 loops=1) 
       Index Cond: (metric ~ $0) 
Total runtime: 0.153 ms 
+0

Листинг 'cstring' выглядит довольно безвредным. Каков план во втором случае? –

+0

@NickBarnes, я обновил план запроса для второго случая, в котором может использоваться индекс gist для ltree. и, таким образом, может быть достигнуто очень быстро. – andy

+0

Я предполагаю, что листинг 'cstring' все еще существует (внутри« InitPlan 1 »), это просто не в выводе' EXPLAIN'. Первый план ссылается на другую таблицу ('tb_metric_map' вместо' demo') ... Это релевантно? –

ответ

3

В основном , это ошибка в ltree расширение.

Основная проблема заключается в том, что процедуры ввода-вывода - функции, отвечающие за преобразование между lquery и строками - были неправильно отмечены как VOLATILE. Поскольку такие функции могут иметь побочные эффекты, Postgres не может оптимизировать любые сравнения с использованием индекса; чтобы гарантировать предсказуемое поведение, планировщик должен удостовериться, что бросок вызывается в каждой строке.

Это совсем другая история с подзапросами. По возможности Postgres будет оценивать подзапрос один раз, независимо от волатильности. Например, сравните вывод

SELECT random() 
FROM generate_series(1,10); 

с

SELECT (SELECT random()) 
FROM generate_series(1,10); 

Во всяком случае, ошибка имеет already been fixed во всех поддерживаемых версиях Postgres, но исправление не повлияет на существующие базы данных. Дамп/восстановление должно обновлять расширение. В качестве альтернативы, это должно иметь такой же эффект:

ALTER FUNCTION ltree_in(cstring) IMMUTABLE; 
ALTER FUNCTION ltree_out(ltree) IMMUTABLE; 
ALTER FUNCTION lquery_in(cstring) IMMUTABLE; 
ALTER FUNCTION lquery_out(lquery) IMMUTABLE; 
ALTER FUNCTION ltxtq_in(cstring) IMMUTABLE; 
ALTER FUNCTION ltxtq_out(ltxtquery) IMMUTABLE; 
ALTER FUNCTION ltree_gist_in(cstring) IMMUTABLE; 
ALTER FUNCTION ltree_gist_out(ltree_gist) IMMUTABLE; 
+0

Спасибо за ваши ответы. Я получаю вашу позицию. Итак, если я все еще использую подзапрос, чтобы преобразовать 'text' в' lquery', это будет законным и правильным. Или, я могу принять ваш предложенный способ для пользователя изменить функцию. Я прав? – andy

+0

@andy: Да, это так. Он также работает, если ваша строка 'lquery' является литералом, то есть' 's.a.b. * ':: lquery' вместо' (' s.a.b '||'. * ') :: lquery'. –