不同的查询,相同的结果(似乎),完全不同的表现......为什么?

时间:2011-03-02 18:32:19

标签: sql postgresql

目前,我有两个不同的查询返回完全相同的结果,但是更改过滤结果的参数会使它们的行为方式完全不同。

搜索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行/ ~430ms;

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行/ ~450ms

搜索car

时的结果

查询#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)"

所以,使用查询#1搜索car似乎需要永远...考虑到SELECT eid FROM t_entidades只需要大约4秒返回所有350k +行,这很有趣......

不同步骤中查询#1的EXPLAIN之间的唯一区别是,对于car,出现以下行:“ - > Materialise(cost = 37712.46..38269.55 rows = 40009 width = 4)“

如果有人愿意解释为什么查询#1需要这么长时间来执行最后一个例子以及解释的每一步到底发生了什么,我将非常感激,因为我似乎永远不会得到它......

4 个答案:

答案 0 :(得分:1)

这是我看到的第一个postgresql执行计划,但看起来第一个计划是在t_entidades上进行表扫描,然后对于每一行,它执行下面的所有操作,包括更多的表扫描。

在第二个计划中,它仍然执行两次内部扫描但是已经对结果进行了分析。

假设您的表中有100行,第一个计划执行201个表扫描,第二个计划执行2.表示: - )

答案 1 :(得分:1)

第一个查询太奇怪了,它只会混淆queryplanner。第一个子查询不应该是子查询,第二个子查询的LEFT JOIN应该是INNER JOIN,但也可以在没有子查询的情况下编写。

第二个查询还有一个LEFT JOIN,它实际上是一个INNER JOIN,检查WHERE条件。

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%'

您是否在aidactividade列上有索引?

答案 2 :(得分:1)

你有连接真的没必要。我来使用经验法则,如果我实际上没有使用字段作为返回集的一部分,我尝试使用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%'
                                    )
                )

答案 3 :(得分:0)

查询#1的计划读给我:

  1. 扫描t_entidades,并为每一行:
    1. 通过从t_entidades扫描实体化子集(临时文件?)来执行子计划1
    2. 通过检查从扫描构建的哈希表来执行子计划t_entidade_actividade
  2. “解析分析”能够告诉您步骤1.1和1.2实际运行的频率...如果步骤1中的每一行的扫描都在步骤1中,那么您的查询时间将是增长O(n ^ 2)其中n是t_entidades中的行数,每次迭代1.1使用的临时空间将随着该表中匹配数的增加而增加。

    你的查询2写的好多了,恕我直言。两组ID中的每一组都以完全不同的方式生成,因此将它们放在单独的查询中并使用UNION在最后将它们合并在一起。它还会在查询1中删除t_entidades的无用外部扫描,它只是从where子句传递ID。 (并不是说它与PostgreSQL有关,但它也清楚地表明两个扫描可以并行运行然后合并,但不要介意。)

    t_entidade_actividade.actividade可能需要索引吗?

相关问题