为什么更新不适用于内连接?

时间:2010-11-08 22:10:42

标签: sql oracle sql-update

这是我桌子的简化版。

CREATE TABLE TBLAGENT(AGENTID NUMBER, NUMBERSENT NUMBER, AGENTNAME VARCHAR2(100));
INSERT INTO TBLAGENT VALUES(100,NULL,'KNIGHT');
INSERT INTO TBLAGENT VALUES(200,NULL,'SUPES');
INSERT INTO TBLAGENT VALUES(300,NULL,'SPIDEY');

CREATE TABLE TBLSERVICES(AGENTID NUMBER, SERVICES NUMBER);
INSERT INTO TBLSERVICES VALUES(100,44);
INSERT INTO TBLSERVICES VALUES(200,13);
INSERT INTO TBLSERVICES VALUES(300,24);
INSERT INTO TBLSERVICES VALUES(100,34);
INSERT INTO TBLSERVICES VALUES(200,13);
INSERT INTO TBLSERVICES VALUES(300,24);

SELECT TA.AGENTID, SUM(SERVICES), TA.AGENTNAME, TA.NUMBERSENT 
       FROM TBLAGENT TA, TBLSERVICES TS
       WHERE TA.AGENTID = TS.AGENTID
       GROUP BY TA.AGENTID, TA.AGENTNAME, TA.NUMBERSENT

要求是使用tblServices表中的SUM(Services)更新tblAgent表中的NUMBERSENT列。

我想出了这个更新声明。

/*Works*/
UPDATE tblagent t
   SET t.numbersent =
       (SELECT SUM(services)
          FROM tblservices x
         WHERE t.agentid = x.agentid
         GROUP BY x.agentid)

当我将此语句的语法更改为INNER JOIN语法时,它会失败。

/*Throws an error*/
UPDATE tblagent t
   SET t.numbersent =
       (SELECT SUM(services)
          FROM tblservices x INNER JOIN tblAgent t
         ON t.agentid = x.agentid
         GROUP BY x.agentid)

这会引发错误ORA-01427:单行子查询返回多行

为什么第二个语句会抛出错误?

7 个答案:

答案 0 :(得分:4)

让我们看看2个查询如何更详细地工作:

首先,有效的那个:

/*Works*/
UPDATE tblagent t
   SET t.numbersent =
       (SELECT SUM(services)
          FROM tblservices x
         WHERE t.agentid = x.agentid
         GROUP BY x.agentid)

显然,子查询必须返回一个值才能在SET中使用,所以让我们自己看一下:

        SELECT SUM(services)
          FROM tblservices x
         WHERE t.agentid = x.agentid
         GROUP BY x.agentid

请注意,此处的“t”别名将子查询与外部查询相关联 - 即在评估子查询时它具有一个特定值,例如

        SELECT SUM(services)
          FROM tblservices x
         WHERE 123 = x.agentid
         GROUP BY x.agentid

因此,尽管查询组的结果是x.agentid,但实际上只有一个x.agentid值,即t.agentid的当前值(例如123)。所以这很有效。

现在单独查看第二个查询的子查询:

       SELECT SUM(services)
         FROM tblservices x INNER JOIN tblAgent t
           ON t.agentid = x.agentid
       GROUP BY x.agentid

此时t.agentid 是对外部查询的引用,因此此查询与外部查询相关联。 可以返回超过1行(只需运行并查看),因此不能在外部查询的SET子句中安全使用。

答案 1 :(得分:3)

您是否尝试单独运行子查询以确保它只返回一行?

答案 2 :(得分:3)

@ Tony Andrews 是对的,如果你还想使用INNER JOIN,你应该这样写:

 UPDATE tblagent t1
   SET t1.numbersent =
       (SELECT SUM(services)
          FROM tblservices x INNER JOIN tblAgent t
         ON t.agentid = x.agentid
         GROUP BY x.agentid
         having t1.agentid = x.agentid)

(要使上部和内部DML有一个公共列,以便不返回多行)

但我当然认为这只是使你的工作复杂化而已。仅使用第一个变体......这是更好的建议。

答案 3 :(得分:2)

您使用INNER JOIN重新分配t,因此外部t不再与UPDATE相关联。

答案 4 :(得分:1)

实际上现在我考虑一下,第一个版本是相关子查询,第二个版本不是。如果没有数据可以尝试,我无法告诉你,但这可能与它有关。

答案 5 :(得分:1)

tblAgent中有两行具有相同的agentid。如果没有agentid is NULL,那么这可能会让您失望。

检查:

select * from
(
    SELECT count(*) c, agentid from tblAgent group by agentid
) x
where x.c > 1

如果有任何行回来,那就是你的问题。

答案 6 :(得分:0)

不应该是

(SELECT x.agentid, SUM(services)
      FROM tblservices x INNER JOIN tblAgent t
     ON t.agentid = x.agentid
     GROUP BY x.agentid)

如果您是通过agentid加入,或

(SELECT  SUM(services)
      FROM tblservices x INNER JOIN tblAgent t
     ON t.agentid = x.agentid
     )

如果你不是吗?