如何筛选具有多次通过关系的SQL结果


100

假设我有表studentclub以及student_club

student {
    id
    name
}
club {
    id
    name
}
student_club {
    student_id
    club_id
}

我想知道如何找到足球(30)和棒球(50)俱乐部中的所有学生。
虽然此查询不起作用,但这是我到目前为止最接近的东西:

SELECT student.*
FROM   student
INNER  JOIN student_club sc ON student.id = sc.student_id
LEFT   JOIN club c ON c.id = sc.club_id
WHERE  c.id = 30 AND c.id = 50

Answers:


145

我很好奇 众所周知,好奇心以杀死猫而闻名。

那么,哪一种最快的方法是给猫皮呢?

此测试的精确蒙皮环境:

  • Debian Squeeze上的PostgreSQL 9.0,具有不错的RAM和设置。
  • 6.000名学生,24.000个俱乐部会员资格(数据从类似的数据库复制而来,带有真实生活的数据。)
  • 从问题中的命名模式稍微转移:“ student.id是” student.stud_id和“ club.idclub.club_id这里”。
  • 我在该线程中以查询的作者命名,在其中有两个索引。
  • 我运行了所有查询几次以填充缓存,然后通过EXPLAIN ANALYZE选择了5个最好的查询。
  • 相关指标(应该是最佳的-只要我们不具备将查询哪些俱乐部的先知知识):

    ALTER TABLE student ADD CONSTRAINT student_pkey PRIMARY KEY(stud_id );
    ALTER TABLE student_club ADD CONSTRAINT sc_pkey PRIMARY KEY(stud_id, club_id);
    ALTER TABLE club       ADD CONSTRAINT club_pkey PRIMARY KEY(club_id );
    CREATE INDEX sc_club_id_idx ON student_club (club_id);
    

    club_pkey这里的大多数查询都不需要。
    主键在PostgreSQL中自动实现唯一索引。
    最后一个索引是为了弥补PostgreSQL 上多列索引的已知缺点:

可以将多列B树索引用于涉及该索引列的任何子集的查询条件,但是当前导(最左边)列受到约束时,该索引效率最高。

结果:

EXPLAIN ANALYZE的总运行时间。

1)马丁2:44.594毫秒

SELECT s.stud_id, s.name
FROM   student s
JOIN   student_club sc USING (stud_id)
WHERE  sc.club_id IN (30, 50)
GROUP  BY 1,2
HAVING COUNT(*) > 1;

2)Erwin 1:33.217毫秒

SELECT s.stud_id, s.name
FROM   student s
JOIN   (
   SELECT stud_id
   FROM   student_club
   WHERE  club_id IN (30, 50)
   GROUP  BY 1
   HAVING COUNT(*) > 1
   ) sc USING (stud_id);

3)马丁1:31.735毫秒

SELECT s.stud_id, s.name
   FROM   student s
   WHERE  student_id IN (
   SELECT student_id
   FROM   student_club
   WHERE  club_id = 30
   INTERSECT
   SELECT stud_id
   FROM   student_club
   WHERE  club_id = 50);

4)Derek:2.287毫秒

SELECT s.stud_id,  s.name
FROM   student s
WHERE  s.stud_id IN (SELECT stud_id FROM student_club WHERE club_id = 30)
AND    s.stud_id IN (SELECT stud_id FROM student_club WHERE club_id = 50);

5)Erwin 2:2.181毫秒

SELECT s.stud_id,  s.name
FROM   student s
WHERE  EXISTS (SELECT * FROM student_club
               WHERE  stud_id = s.stud_id AND club_id = 30)
AND    EXISTS (SELECT * FROM student_club
               WHERE  stud_id = s.stud_id AND club_id = 50);

6)肖恩:2.043毫秒

SELECT s.stud_id, s.name
FROM   student s
JOIN   student_club x ON s.stud_id = x.stud_id
JOIN   student_club y ON s.stud_id = y.stud_id
WHERE  x.club_id = 30
AND    y.club_id = 50;

后三个的表现几乎相同。4)和5)得出相同的查询计划。

后期添加:

花哨的SQL,但性能跟不上。

7)超级立方体1:148.649毫秒

SELECT s.stud_id,  s.name
FROM   student AS s
WHERE  NOT EXISTS (
   SELECT *
   FROM   club AS c 
   WHERE  c.club_id IN (30, 50)
   AND    NOT EXISTS (
      SELECT *
      FROM   student_club AS sc 
      WHERE  sc.stud_id = s.stud_id
      AND    sc.club_id = c.club_id  
      )
   );

8)ypercube 2:147.497毫秒

SELECT s.stud_id,  s.name
FROM   student AS s
WHERE  NOT EXISTS (
   SELECT *
   FROM  (
      SELECT 30 AS club_id  
      UNION  ALL
      SELECT 50
      ) AS c
   WHERE NOT EXISTS (
      SELECT *
      FROM   student_club AS sc 
      WHERE  sc.stud_id = s.stud_id
      AND    sc.club_id = c.club_id  
      )
   );

不出所料,这两个的表现几乎相同。查询计划会导致表扫描,而计划者在这里找不到使用索引的方法。


9)Wildplasser 1:49.849毫秒

WITH RECURSIVE two AS (
   SELECT 1::int AS level
        , stud_id
   FROM   student_club sc1
   WHERE  sc1.club_id = 30
   UNION
   SELECT two.level + 1 AS level
        , sc2.stud_id
   FROM   student_club sc2
   JOIN   two USING (stud_id)
   WHERE  sc2.club_id = 50
   AND    two.level = 1
   )
SELECT s.stud_id, s.student
FROM   student s
JOIN   two USING (studid)
WHERE  two.level > 1;

精美的SQL,CTE的性能不错。非常奇特的查询计划。
同样,有趣的是9.1如何处理这个问题。我将很快将此处使用的数据库集群升级到9.1。也许我会重新运行整个shebang ...


10)Wildplasser 2:36.986毫秒

WITH sc AS (
   SELECT stud_id
   FROM   student_club
   WHERE  club_id IN (30,50)
   GROUP  BY stud_id
   HAVING COUNT(*) > 1
   )
SELECT s.*
FROM   student s
JOIN   sc USING (stud_id);

查询2的CTE变体。出乎意料的是,它可能会导致使用完全相同的数据的查询计划略有不同。我在上找到了顺序扫描student,其中子查询变量使用了索引。


11)超级立方体3:101.482毫秒

另一个后期添加@ypercube。到底有多少种方式,真是令人惊讶。

SELECT s.stud_id, s.student
FROM   student s
JOIN   student_club sc USING (stud_id)
WHERE  sc.club_id = 10                 -- member in 1st club ...
AND    NOT EXISTS (
   SELECT *
   FROM  (SELECT 14 AS club_id) AS c  -- can't be excluded for missing the 2nd
   WHERE  NOT EXISTS (
      SELECT *
      FROM   student_club AS d
      WHERE  d.stud_id = sc.stud_id
      AND    d.club_id = c.club_id
      )
   )

12)欧文3:2.377毫秒

@ypercube的11)实际上只是这个更简单的变体的令人费解的反向方法,它也仍然缺少。执行几乎与顶级猫一样快。

SELECT s.*
FROM   student s
JOIN   student_club x USING (stud_id)
WHERE  sc.club_id = 10                 -- member in 1st club ...
AND    EXISTS (                        -- ... and membership in 2nd exists
   SELECT *
   FROM   student_club AS y
   WHERE  y.stud_id = s.stud_id
   AND    y.club_id = 14
   )

13)欧文4:2.375毫秒

难以置信,但这是另一个全新的变体。我认为有超过两个成员的潜力,但它也只有两个而跻身顶级猫之列。

SELECT s.*
FROM   student AS s
WHERE  EXISTS (
   SELECT *
   FROM   student_club AS x
   JOIN   student_club AS y USING (stud_id)
   WHERE  x.stud_id = s.stud_id
   AND    x.club_id = 14
   AND    y.club_id = 10
   )

俱乐部会员动态数量

换句话说:数量不同的过滤器。这个问题要求两个俱乐部的会员资格。但是许多用例必须为数量不一的事情做准备。

在此相关的稍后答案中进行详细讨论:


1
Brandstetter,非常棒的工作。我开始悬赏这个问题,以给您额外的荣誉(但我必须等待24小时)。无论如何,我想知道当您开始添加多个club_id而不是两个时,这些查询会如何...
Xeoncross 2011年

@Xeoncross:对您的大方手势表示敬意。:)随着club_ids的增加,我怀疑1)和2)的速度会越来越接近,但必须更大一些才能推高排名。
Erwin Brandstetter,

如果您有多个俱乐部,则创建另一个包含这些俱乐部的表。然后加入您选择的表格。
保罗·摩根

@Erwin:Thnx(用于基准测试)。不是挑剔,但也许您可以尝试使用(student_id, club_id)(或反向)索引的那些查询(我的意思是全部,而不仅仅是我的)。
ypercubeᵀᴹ

3
考虑到所涉及的域和样本大小,我认为200 ms以下的任何性能都是可以接受的,这是错误的吗?出于个人利益,我在SQL Server 2008 R2上使用相同的结构索引和(我认为)数据分布进行了自己的测试,但可扩展到一百万名学生(对于给定的领域,这是一个相当大的数目),但现在仍然国际海事组织没有太多区别不同方法的方法。当然,基于关系划分的那些可以针对基表,从而赋予它们“可扩展性”的优势。
2011年

18
SELECT s.*
FROM student s
INNER JOIN student_club sc_soccer ON s.id = sc_soccer.student_id
INNER JOIN student_club sc_baseball ON s.id = sc_baseball.student_id
WHERE 
 sc_baseball.club_id = 50 AND 
 sc_soccer.club_id = 30

10
select *
from student
where id in (select student_id from student_club where club_id = 30)
and id in (select student_id from student_club where club_id = 50)

该查询工作正常,但令我困扰的是不得不要求RDBMS检查这么多的索引*俱乐部的数量。
Xeoncross 2011年

6
我最喜欢此查询,因为它类似于简洁的样式,类似于sql中的python。我很乐意为这种代码交易0.44ms(与Sean的查询有所不同)。
MGP

5

如果您只想要student_id,则:

    Select student_id
      from student_club
     where club_id in ( 30, 50 )
  group by student_id
    having count( student_id ) = 2

如果您还需要学生的姓名,则:

Select student_id, name
  from student s
 where exists( select *
                 from student_club sc
                where s.student_id = sc.student_id
                  and club_id in ( 30, 50 )
             group by sc.student_id
               having count( sc.student_id ) = 2 )

如果club_selection表中有两个以上的俱乐部,则:

Select student_id, name
  from student s
 where exists( select *
                 from student_club sc
                where s.student_id = sc.student_id
                  and exists( select * 
                                from club_selection cs
                               where sc.club_id = cs.club_id )
             group by sc.student_id
               having count( sc.student_id ) = ( select count( * )
                                                   from club_selection ) )

前两个是包含在/中,与我的查询1相同。但是第三个是@Xeoncross在上面的注释中添加的问题。我会为没有骗子的那部分投票。
Erwin Brandstetter,

感谢您的评论,但我还将演示一些格式。我将按原样保留它。
保罗·摩根

4
SELECT *
FROM   student
WHERE  id IN (SELECT student_id
              FROM   student_club
              WHERE  club_id = 30
              INTERSECT
              SELECT student_id
              FROM   student_club
              WHERE  club_id = 50)  

或更通用的解决方案更易于扩展到n俱乐部,并且避免了INTERSECT(在MySQL中不可用)和IN(因为这种性能在MySQL中很糟糕

SELECT s.id,
       s.name
FROM   student s
       join student_club sc
         ON s.id = sc.student_id
WHERE  sc.club_id IN ( 30, 50 )
GROUP  BY s.id,
          s.name
HAVING COUNT(DISTINCT sc.club_id) = 2  

毫无疑问,对于由代码生成的查询,第二个答案是最好的。我是否会认真编写10个联接或子查询以找到10个条件的关系划分?哎呀,我将使用这个出色的解决方案。感谢您教我HAVING在MySQL中做什么。
埃里克·L.

4

另一个CTE。它看起来很干净,但是可能会与普通子查询中的groupby生成相同的计划。

WITH two AS (
    SELECT student_id FROM tmp.student_club
    WHERE club_id IN (30,50)
    GROUP BY student_id
    HAVING COUNT(*) > 1
    )
SELECT st.* FROM tmp.student st
JOIN two ON (two.student_id=st.id)
    ;

对于那些想测试的人来说,我生成testdata的副本很重要:

DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp;

CREATE TABLE tmp.student
    ( id INTEGER NOT NULL PRIMARY KEY
    , sname VARCHAR
    );

CREATE TABLE tmp.club
    ( id INTEGER NOT NULL PRIMARY KEY
    , cname VARCHAR
    );

CREATE TABLE tmp.student_club
    ( student_id INTEGER NOT NULL  REFERENCES tmp.student(id)
    , club_id INTEGER NOT NULL  REFERENCES tmp.club(id)
    );

INSERT INTO tmp.student(id)
    SELECT generate_series(1,1000)
    ;

INSERT INTO tmp.club(id)
    SELECT generate_series(1,100)
    ;

INSERT INTO tmp.student_club(student_id,club_id)
    SELECT st.id  , cl.id
    FROM tmp.student st, tmp.club cl
    ;

DELETE FROM tmp.student_club
WHERE random() < 0.8
    ;

UPDATE tmp.student SET sname = 'Student#' || id::text ;
UPDATE tmp.club SET cname = 'Soccer' WHERE id = 30;
UPDATE tmp.club SET cname = 'Baseball' WHERE id = 50;

ALTER TABLE tmp.student_club
    ADD PRIMARY KEY (student_id,club_id)
    ;

是的,实际上就像我的第一个版本一样,它只是带有group by的子查询。相同的查询计划+ CTE开销导致相同的性能+一点CTE。不错的测试设置。
Erwin Brandstetter

我不知道是否有CTE费用。测试数据的分发非常重要。统计信息的可用性也是如此:在VACUUM ANALYZE之后,运行时间从67.4毫秒变为1.56毫秒。QP中仅涉及哈希和位图。
wildplasser 2011年

在您的情况下,这很特殊,在删除一个大表的80%并进行大量更新之后,死元组比其他任何东西都多。难怪,真空分析有很大帮助。我使用和不使用CTE都运行了这两种变体,令人惊讶的是查询计划并不相同。或者更好的是,我将为此打开一个聊天室。
Erwin Brandstetter,

别担心,我知道80%的死行...我认为统计数据也很重要。但是直方图相当“平坦”,可以随机删除。可能仅仅是所需页面的估计值发生了变化,足以使计划者决定切换计划。
wildplasser 2011年

3

因此,有多种方法可以为猫皮
我将再添加两个以使其更加完善。

1)首先组,然后加入

假设一个理智的数据模型,其中(student_id, club_id)唯一student_club。马丁·史密斯(Martin Smith)的第二个版本有些相似,但他首先加入,后来加入。这应该更快:

SELECT s.id, s.name
  FROM student s
  JOIN (
   SELECT student_id
     FROM student_club
    WHERE club_id IN (30, 50)
    GROUP BY 1
   HAVING COUNT(*) > 1
       ) sc USING (student_id);

2)存在

当然,还有经典EXISTS。与Derek的变体类似IN。简单快捷。(在MySQL中,这应该比的变体要快很多IN):

SELECT s.id, s.name
  FROM student s
 WHERE EXISTS (SELECT 1 FROM student_club
               WHERE  student_id = s.student_id AND club_id = 30)
   AND EXISTS (SELECT 1 FROM student_club
               WHERE  student_id = s.student_id AND club_id = 50);

3

由于没有人添加此(经典)版本:

SELECT s.*
FROM student AS s
WHERE NOT EXISTS
      ( SELECT *
        FROM club AS c 
        WHERE c.id IN (30, 50)
          AND NOT EXISTS
              ( SELECT *
                FROM student_club AS sc 
                WHERE sc.student_id = s.id
                  AND sc.club_id = c.id  
              )
      )

或类似:

SELECT s.*
FROM student AS s
WHERE NOT EXISTS
      ( SELECT *
        FROM
          ( SELECT 30 AS club_id  
          UNION ALL
            SELECT 50
          ) AS c
        WHERE NOT EXISTS
              ( SELECT *
                FROM student_club AS sc 
                WHERE sc.student_id = s.id
                  AND sc.club_id = c.club_id  
              )
      )

再尝试使用稍微不同的方法。受到Explain Extended:EAV表中的多个属性的启发:GROUP BY与NOT EXISTS

SELECT s.*
FROM student_club AS sc
  JOIN student AS s
    ON s.student_id = sc.student_id
WHERE sc.club_id = 50                      --- one option here
  AND NOT EXISTS
      ( SELECT *
        FROM
          ( SELECT 30 AS club_id           --- all the rest in here
                                           --- as in previous query
          ) AS c
        WHERE NOT EXISTS
              ( SELECT *
                FROM student_club AS scc 
                WHERE scc.student_id = sc.id
                  AND scc.club_id = c.club_id  
              )
      )

另一种方法:

SELECT s.stud_id
FROM   student s

EXCEPT

SELECT stud_id
FROM 
  ( SELECT s.stud_id, c.club_id
    FROM student s 
      CROSS JOIN (VALUES (30),(50)) c (club_id)
  EXCEPT
    SELECT stud_id, club_id
    FROM student_club
    WHERE club_id IN (30, 50)   -- optional. Not needed but may affect performance
  ) x ;   

+1 ..不错的补充,使猫毛收集不那么完整!:)我将它们添加到基准中。
Erwin Brandstetter,

这不是公平的竞争:)这样的关系划分的最大优点是除数可以是基表,因此更改除数非常便宜,例如,通过更改SQL对比更新同一查询所针对的基表中的行每次查询。
2011年

@ErwinBrandstetter:是否可以在测试中添加第三个版本?
ypercubeᵀᴹ

@ypercube:知道了。很扭曲的版本。:)
Erwin Brandstetter '02

1
@Erwin:当您设法在此上浪费一些时间时,是否还可以尝试在(stud_id, club_id)和和(club_id, stud_id)(或主和唯一)上同时具有两个UNIQUE键?我仍然认为,对于其中一些查询,2到140 ms的差异太大,无法用执行计划的差异来解释。
ypercubeᵀᴹ

2
WITH RECURSIVE two AS
    ( SELECT 1::integer AS level
    , student_id
    FROM tmp.student_club sc0
    WHERE sc0.club_id = 30
    UNION
    SELECT 1+two.level AS level
    , sc1.student_id
    FROM tmp.student_club sc1
    JOIN two ON (two.student_id = sc1.student_id)
    WHERE sc1.club_id = 50
    AND two.level=1
    )
SELECT st.* FROM tmp.student st
JOIN two ON (two.student_id=st.id)
WHERE two.level> 1

    ;

由于CTE扫描避免了需要两个单独的子查询,因此这似乎表现不错。

总是有理由滥用递归查询!

(顺便说一句:mysql似乎没有递归查询)


+1寻找另一种体面的方式!我将您的查询添加到基准中。希望你能接受。:)
Erwin Brandstetter

没关系。但这当然是在开玩笑。如果添加更多的“流浪”学生*俱乐部记录,CTE实际上表现良好。(为进行测试,我使用了1000名学生* 100个俱乐部,并随机删除了80%)
wildplasser 2011年

1

查询2)和10)中的不同查询计划

我在真实的db中进行了测试,因此名称与catskin列表不同。这是一个备份副本,因此在所有测试运行期间都没有任何更改(对目录进行的细微更改除外)。

查询2)

SELECT a.*
FROM   ef.adr a
JOIN (
    SELECT adr_id
    FROM   ef.adratt
    WHERE  att_id IN (10,14)
    GROUP  BY adr_id
    HAVING COUNT(*) > 1) t using (adr_id);

Merge Join  (cost=630.10..1248.78 rows=627 width=295) (actual time=13.025..34.726 rows=67 loops=1)
  Merge Cond: (a.adr_id = adratt.adr_id)
  ->  Index Scan using adr_pkey on adr a  (cost=0.00..523.39 rows=5767 width=295) (actual time=0.023..11.308 rows=5356 loops=1)
  ->  Sort  (cost=630.10..636.37 rows=627 width=4) (actual time=12.891..13.004 rows=67 loops=1)
        Sort Key: adratt.adr_id
        Sort Method:  quicksort  Memory: 28kB
        ->  HashAggregate  (cost=450.87..488.49 rows=627 width=4) (actual time=12.386..12.710 rows=67 loops=1)
              Filter: (count(*) > 1)
              ->  Bitmap Heap Scan on adratt  (cost=97.66..394.81 rows=2803 width=4) (actual time=0.245..5.958 rows=2811 loops=1)
                    Recheck Cond: (att_id = ANY ('{10,14}'::integer[]))
                    ->  Bitmap Index Scan on adratt_att_id_idx  (cost=0.00..94.86 rows=2803 width=0) (actual time=0.217..0.217 rows=2811 loops=1)
                          Index Cond: (att_id = ANY ('{10,14}'::integer[]))
Total runtime: 34.928 ms

查询10)

WITH two AS (
    SELECT adr_id
    FROM   ef.adratt
    WHERE  att_id IN (10,14)
    GROUP  BY adr_id
    HAVING COUNT(*) > 1
    )
SELECT a.*
FROM   ef.adr a
JOIN   two using (adr_id);

Hash Join  (cost=1161.52..1261.84 rows=627 width=295) (actual time=36.188..37.269 rows=67 loops=1)
  Hash Cond: (two.adr_id = a.adr_id)
  CTE two
    ->  HashAggregate  (cost=450.87..488.49 rows=627 width=4) (actual time=13.059..13.447 rows=67 loops=1)
          Filter: (count(*) > 1)
          ->  Bitmap Heap Scan on adratt  (cost=97.66..394.81 rows=2803 width=4) (actual time=0.252..6.252 rows=2811 loops=1)
                Recheck Cond: (att_id = ANY ('{10,14}'::integer[]))
                ->  Bitmap Index Scan on adratt_att_id_idx  (cost=0.00..94.86 rows=2803 width=0) (actual time=0.226..0.226 rows=2811 loops=1)
                      Index Cond: (att_id = ANY ('{10,14}'::integer[]))
  ->  CTE Scan on two  (cost=0.00..50.16 rows=627 width=4) (actual time=13.065..13.677 rows=67 loops=1)
  ->  Hash  (cost=384.68..384.68 rows=5767 width=295) (actual time=23.097..23.097 rows=5767 loops=1)
        Buckets: 1024  Batches: 1  Memory Usage: 1153kB
        ->  Seq Scan on adr a  (cost=0.00..384.68 rows=5767 width=295) (actual time=0.005..10.955 rows=5767 loops=1)
Total runtime: 37.482 ms

@wildplasser:请参阅不同的查询计划!出乎我的意料。第9.0页。聊天室很笨拙,因此我在这里滥用了答案。
Erwin Brandstetter

奇怪的场面。此处的CTE基本上是相同的QP(9.0.1-beta-something):seq scan + bitmap而不是index scan + merge。优化器的成本启发法可能存在缺陷吗?我
将再

1

@ erwin-brandstetter请对此进行基准测试:

SELECT s.stud_id, s.name
FROM   student s, student_club x, student_club y
WHERE  x.club_id = 30
AND    s.stud_id = x.stud_id
AND    y.club_id = 50
AND    s.stud_id = y.stud_id;

我想这就像@sean的6),更干净。


2
您必须知道@-notification仅在注释中有效,而在答案中无效。我偶然发现了这个职位。查询计划和查询的性能与Sean的查询相同。实际上是相同的,但是使用明确JOIN语法的Sean查询通常是首选形式,因为它更清晰。+1可以得到另一个有效答案!
Erwin Brandstetter,2012年

0
-- EXPLAIN ANALYZE
WITH two AS (
    SELECT c0.student_id
    FROM tmp.student_club c0
    , tmp.student_club c1
    WHERE c0.student_id = c1.student_id
    AND c0.club_id = 30
    AND c1.club_id = 50
    )
SELECT st.* FROM tmp.student st
JOIN two ON (two.student_id=st.id)
    ;

查询计划:

 Hash Join  (cost=1904.76..1919.09 rows=337 width=15) (actual time=6.937..8.771 rows=324 loops=1)
   Hash Cond: (two.student_id = st.id)
   CTE two
     ->  Hash Join  (cost=849.97..1645.76 rows=337 width=4) (actual time=4.932..6.488 rows=324 loops=1)
           Hash Cond: (c1.student_id = c0.student_id)
           ->  Bitmap Heap Scan on student_club c1  (cost=32.76..796.94 rows=1614 width=4) (actual time=0.667..1.835 rows=1646 loops=1)
                 Recheck Cond: (club_id = 50)
                 ->  Bitmap Index Scan on sc_club_id_idx  (cost=0.00..32.36 rows=1614 width=0) (actual time=0.473..0.473 rows=1646 loops=1)                     
                       Index Cond: (club_id = 50)
           ->  Hash  (cost=797.00..797.00 rows=1617 width=4) (actual time=4.203..4.203 rows=1620 loops=1)
                 Buckets: 1024  Batches: 1  Memory Usage: 57kB
                 ->  Bitmap Heap Scan on student_club c0  (cost=32.79..797.00 rows=1617 width=4) (actual time=0.663..3.596 rows=1620 loops=1)                   
                       Recheck Cond: (club_id = 30)
                       ->  Bitmap Index Scan on sc_club_id_idx  (cost=0.00..32.38 rows=1617 width=0) (actual time=0.469..0.469 rows=1620 loops=1)
                             Index Cond: (club_id = 30)
   ->  CTE Scan on two  (cost=0.00..6.74 rows=337 width=4) (actual time=4.935..6.591 rows=324 loops=1)
   ->  Hash  (cost=159.00..159.00 rows=8000 width=15) (actual time=1.979..1.979 rows=8000 loops=1)
         Buckets: 1024  Batches: 1  Memory Usage: 374kB
         ->  Seq Scan on student st  (cost=0.00..159.00 rows=8000 width=15) (actual time=0.093..0.759 rows=8000 loops=1)
 Total runtime: 8.989 ms
(20 rows)

因此,似乎仍然希望对学生进行seq扫描。


迫不及待想看看它是否已在9.1中修复。
欧文·布兰德斯特

0
SELECT s.stud_id, s.name
FROM   student s,
(
select x.stud_id from 
student_club x 
JOIN   student_club y ON x.stud_id = y.stud_id
WHERE  x.club_id = 30
AND    y.club_id = 50
) tmp_tbl
where tmp_tbl.stud_id = s.stud_id
;

使用最快的变体(Brandstetter先生图表中的Sean先生)。可能是一种变体,只允许一个加入,仅student_club矩阵有权居住。因此,最长的查询将只有两列要计算,其目的是使查询变薄。


1
尽管此代码段可以解决问题,但提供说明确实有助于提高您的帖子质量。请记住,您将来会为读者回答问题,而不仅仅是现在问的人!请编辑您的答案以添加说明,并指出适用的限制和假设。
BrokenBinary
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.