Разные запросы, один и тот же результат (кажется), совершенно разная производительность ... Почему? - PullRequest
0 голосов
/ 02 марта 2011

В настоящее время у меня есть два разных запроса, которые возвращают абсолютно одинаковые результаты, однако изменение параметра, по которому фильтруются результаты, заставляет их вести себя совсем по-другому.

Результаты при поиске cartography

запрос № 1: 22 строки / ~ 860 мс;

SELECT eid FROM t_entidades 
WHERE  eid IN ( 
            SELECT     eid 
            FROM       t_entidades 
            WHERE      entidade_t LIKE '%cartography%'
)
OR     eid IN (
            SELECT    entidade as eid
            FROM      t_entidade_actividade ea
            LEFT JOIN t_actividades a ON a.aid = ea.actividade
            WHERE     a.actividade LIKE '%cartography%'
)

запрос № 2: 22 строки / ~ 430 мс;

SELECT      eid FROM t_entidades WHERE entidade_t LIKE '%cartography%'
UNION
SELECT      entidade as eid
FROM        t_entidade_actividade ea
LEFT JOIN   t_actividades a ON a.aid = ea.actividade
WHERE       a.actividade LIKE '%cartography%'

Результаты при поиске cart

запрос № 1: 715 строк / ~ 870 мс;

запрос № 2: 715 строк / ~ 450 мс

Результаты при поиске car

query # 1: никогда не ждал достаточно долго ... кажется, что это займет вечность и более 1 с будет слишком много

-- EXPLAIN OUTPUT:
"QUERY PLAN"
"Seq Scan on t_entidades  (cost=44997.40..219177315.47 rows=500127 width=4)"
"  Filter: ((SubPlan 1) OR (hashed SubPlan 2))"
"  SubPlan 1"
"    ->  Materialize  (cost=37712.46..38269.55 rows=40009 width=4)"
"          ->  Seq Scan on t_entidades  (cost=0.00..37515.45 rows=40009 width=4)"
"                Filter: ((entidade_t)::text ~~ '%car%'::text)"
"  SubPlan 2"
"    ->  Hash Join  (cost=36.48..7284.20 rows=298 width=4)"
"          Hash Cond: (ea.actividade = a.aid)"
"          ->  Seq Scan on t_entidade_actividade ea  (cost=0.00..5826.63 rows=378163 width=8)"
"          ->  Hash  (cost=36.46..36.46 rows=1 width=4)"
"                ->  Seq Scan on t_actividades a  (cost=0.00..36.46 rows=1 width=4)"
"                      Filter: ((actividade)::text ~~ '%car%'::text)"

запрос № 2: 23661 строк / ~ 860 мс

-- EXPLAIN OUTPUT:
"QUERY PLAN"
"HashAggregate  (cost=45303.48..45706.55 rows=40307 width=4)"
"  ->  Append  (cost=0.00..45202.72 rows=40307 width=4)"
"        ->  Seq Scan on t_entidades  (cost=0.00..37515.45 rows=40009 width=4)"
"              Filter: ((entidade_t)::text ~~ '%car%'::text)"
"        ->  Hash Join  (cost=36.48..7284.20 rows=298 width=4)"
"              Hash Cond: (ea.actividade = a.aid)"
"              ->  Seq Scan on t_entidade_actividade ea  (cost=0.00..5826.63 rows=378163 width=8)"
"              ->  Hash  (cost=36.46..36.46 rows=1 width=4)"
"                    ->  Seq Scan on t_actividades a  (cost=0.00..36.46 rows=1 width=4)"
"                          Filter: ((actividade)::text ~~ '%car%'::text)"

Итак, поиск car с использованием запроса # 1, кажется, занимает вечность ... Что забавно, учитывая, что SELECT eid FROM t_entidades занимает всего около 4 с, возвращая все 350k + строк ...

Единственное различие между EXPLAIN s для запроса # 1 на разных этапах состоит в том, что для car появляется следующая строка: "-> Материализация (стоимость = 37712.46..38269.55 строк = 40009 ширина = 4)"

Если кто-то захочет объяснить, почему запрос № 1 занимает так много времени для выполнения в последнем примере, и что именно происходит на каждом этапе объяснения, это было бы очень ценно, потому что я, кажется, никогда не получал его ...

Ответы [ 4 ]

1 голос
/ 03 марта 2011

У вас есть объединения, которые действительно не нужны. Я пришел к практическому правилу: если я на самом деле не использую поле как часть возвращаемого набора, я пытаюсь использовать тесты EXISTS вместо JOINING. Что-то вроде:

SELECT  te.[eid]
FROM    [t_entidades] AS te
WHERE   te.[entidade_t] LIKE '%cartography%'
    OR EXISTS   (
                    SELECT  1
                    FROM    [t_entidade_actividade] AS ea
                    WHERE   ea.[entidade]       =   te.[eid]
                        AND EXISTS  (
                                        SELECT  1
                                        FROM    [t_actividades] AS ta
                                        WHERE   ta.[aid]        =       ea.[actividade]
                                            AND ta.[actividade] LIKE    '%cartography%'
                                    )
                )
1 голос
/ 02 марта 2011

Первый запрос настолько странный, что может запутать только планировщик запросов.Первый подзапрос не должен быть подзапросом, а второй подзапрос имеет LEFT JOIN, который должен быть INNER JOIN, но также может быть записан вообще без подзапроса.

Второй запрос также имеет LEFT JOIN, которыйна самом деле ВНУТРЕННЕЕ СОЕДИНЕНИЕ, проверьте состояние ГДЕ.

SELECT      eid FROM t_entidades WHERE entidade_t LIKE '%cartography%'
UNION
SELECT
  entidade as eid
FROM
  t_entidade_actividade ea
    INNER JOIN   t_actividades a ON a.aid = ea.actividade
WHERE       
  a.actividade LIKE '%cartography%'

А у вас есть индексы по столбцам aid и actividade?

1 голос
/ 02 марта 2011

Это первый план выполнения postgresql, который я вижу, но похоже, что первый план выполняет сканирование таблицы в t_entidades, а затем для каждой строки выполняет все перечисленные ниже действия, в том числе для дополнительных проверок таблицы.

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

Таким образом, при условии, что в вашей таблице 100 строк, первый план выполняет 201 сканирование таблицы, а второй - 2. Идет рисунок: -)

0 голосов
/ 03 марта 2011

План для запроса №1 читается как:

  1. scan t_entidades, и для каждой строки:
    1. выполняется подплан 1 путем сканирования материализованного подмножества (временного файла?)из t_entidades
    2. выполнить подплан 2, изучив хеш-таблицу, построенную из сканирования t_entidade_actividade

«Объяснить анализ» сможет сказать вам, как часто шаги 1.1и 1.2 фактически выполнялись для запроса ... если сканирование на шаге 1.1 выполняется для каждой строки с шага 1, то время вашего запроса будет расти O (n ^ 2), где n - количество строк в t_entidades, ивременное пространство, используемое для каждой итерации 1.1, будет увеличиваться с увеличением числа совпадений в этой таблице.

Ваш запрос 2 написан намного лучше, ИМХО.Каждый из двух наборов идентификаторов создается совершенно разными способами, поэтому поместите их в отдельные запросы и в конце объедините их в UNION.Он также исключает бесполезное внешнее сканирование t_entidades в запросе 1, который просто проходит через идентификаторы из предложения where.(Не то, чтобы это относилось к PostgreSQL, но оно также дает понять, что два сканирования могут выполняться параллельно, а затем объединяться, но это не важно).

t_entidade_actividade.actividade может потребоваться индекс?

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