自然加入大桌子

时间:2011-10-12 06:15:08

标签: sql join natural-join

我在两张大桌子上进行简单的自然连接。

  • 多边形包含68,000行(45 MB)
  • roadshydro包含大约200万行(210 MB)。

这是否意味着数据库引擎在内部执行自然连接时生成68,000 * 200万行的数据集?如果是这样,那么所需的内存量必须是45 * 210 MB,这比我的系统大得多,只有1.5 GB。

当我执行此查询时,5分钟后我的系统崩溃(abrupty关闭)。它不能处理数据库上的250 MB数据?那么数据库有什么好处呢?

"I am modifying the above Question to clear the doubts of readers. 29-02-2012 today."

似乎很多朋友都感到困惑,因为我在上面的问题中提到了“自然加入”这个词。我正在使用的真正空间查询是:

select p.OID , r.OID
    from poygons as p , roadshydro as r
                Where st_intersects(p.the_geom , r.the_geom) ;

多边形和多边形roadshydro表每个都有两个字段:OID,the_geom。显然,它是两个表的交叉产品,而不是自然加入一些常用键。

我监视主内存消耗当我执行上面的查询时。它什么都没发生。没有最轻微的内存消耗,我也没有得到任何输出,但CPU使用率几乎是100%。似乎数据库根本不做任何计算。但是,如果我从查询中删除where子句,主内存消耗逐渐变得过高(5-6分钟后),导致系统崩溃并且机器突然关闭。这就是我所经历的。删除where子句有什么特别之处?为什么postgres无法执行查询!!对此行为感到惊讶。

4 个答案:

答案 0 :(得分:1)

使用NATURAL JOIN构造几乎没有意义。话虽如此,如果连接匹配两个表中的每个记录,您描述的查询只会产生两个表的乘积。

只有在两个表中都有一个具有相同名称且每个记录具有相同值的字段时才会发生这种情况 - 这极不可能,但逻辑上不可能或者如果2个表中没有与名称匹配的字段

如果我是你,我会弃用NATURAL JOIN赞成普通JOIN,指定你想要匹配的字段。

如果这样可以解决崩溃问题,那么一切都很好,但如果确实如此,那对我来说会是一个惊喜。

答案 1 :(得分:0)

这实际上取决于许多不同的因素,但最重要的是在您使用的DBMS及其配置上。

但是要清除最大的误解:DBMS不必将所有行保存在内存中:它可以写入临时表(在硬盘上)并为您提供结果......慢慢...所以如果它崩溃了,这不正常。

然后,为什么要问68k * 2M行?这是136,000,000,000行!你确定你不想在某个键上直接连接吗?

答案 2 :(得分:0)

由于我对这篇文章的评论受到批评,我已经准备了一个例子来说明我对这个问题的看法。

以下Oracle脚本说明了我认为使用NATURAL JOIN构造时固有的危险。我接受这是一个人为的例子,但为了防御性的发展,我相信我的立场是正确的。

DROP TABLE TABLE1;
DROP TABLE TABLE2;

CREATE TABLE TABLE1 (
FIELD1   VARCHAR2(10),
FIELD2   VARCHAR2(10),
DESCR_T1 VARCHAR2(20)
);

CREATE TABLE TABLE2 (
FIELD1   VARCHAR2(10),
FIELD2   VARCHAR2(10),
DESCR_T2 VARCHAR2(20)
);

INSERT INTO TABLE1 VALUES('AAA','AAA',    'AAA_AAA_T1'   );
INSERT INTO TABLE1 VALUES('BBB','BBB',    'BBB_BBB_T1'   );
INSERT INTO TABLE1 VALUES('CCC','T1_CCC', 'CCC_T1_CCC_T1');
INSERT INTO TABLE1 VALUES('DDD','T1_DDD', 'DDD_T1_DDD_T1');
INSERT INTO TABLE1 VALUES('EEE',NULL    , 'EEE_NULL_T1'  );

INSERT INTO TABLE2 VALUES('AAA','AAA',    'AAA_AAA_T2'   );
INSERT INTO TABLE2 VALUES('BBB','BBB',    'BBB_BBB_T2'   );
INSERT INTO TABLE2 VALUES('CCC','T2_CCC', 'CCC_T1_CCC_T2');
INSERT INTO TABLE2 VALUES('DDD','T2_DDD', 'DDD_T1_DDD_T2');
INSERT INTO TABLE2 VALUES('EEE',NULL    , 'EEE_NULL_T2'  );

COMMIT;

-- try the following queries and review the results

SELECT 
  FIELD1, DESCR_T1, DESCR_T2 
FROM 
  TABLE1 NATURAL JOIN TABLE2;

SELECT 
  * 
FROM 
  TABLE1 NATURAL JOIN TABLE2;

SELECT 
  TABLE1.FIELD1, TABLE1.DESCR_T1, TABLE2.DESCR_T2 
FROM 
  TABLE1 JOIN 
    TABLE2 ON 
      TABLE2.FIELD1 = TABLE1.FIELD1 AND 
      TABLE2.FIELD2 = TABLE1.FIELD2;

SELECT * FROM 
  TABLE1 NATURAL JOIN TABLE2;

-- Issue the following statement then retry the previous 3 statements.
-- The 'NJs' silently change behaviour and produce radically different results
-- whereas the third requires hands-on attention.  I believe this third behaviour
-- is desirable.  (You could equally drop the column TABLE2.FIELD2 as dportas 
-- has suggested

-- ALTER TABLE TABLE2 RENAME COLUMN FIELD2 TO T2_FIELD2;

答案 3 :(得分:0)

扩展Hugh的示例数据,这是两个NATURAL JOIN查询的示例。希望可以看出,这些对于Hugh所描述的问题是“安全的”,并且NJ版本实际上比INNER JOIN版本更简洁(在我看来更具可读性)。

SELECT *
FROM 
(SELECT FIELD1, DESCR_T1 FROM TABLE1) T1
NATURAL JOIN
(SELECT FIELD1, DESCR_T2 FROM TABLE2) T2;

SELECT * 
FROM 
(SELECT FIELD1, FIELD2, DESCR_T1 FROM TABLE1) T1
NATURAL JOIN
(SELECT FIELD1, FIELD2, DESCR_T2 FROM TABLE2) T2;

除非你写下邋code的代码,否则Hugh所说的问题不存在。如果你写了草率代码,那么INNER JOIN也是“不安全的”。这种交换可能表明的是,自然连接并不总是很好理解。这可能是某些人对他们不合理怀疑的原因。