Почему обновление не работает с внутренним объединением? - PullRequest
0 голосов
/ 09 ноября 2010

Вот упрощенная версия моей таблицы.

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

Требуется обновить столбец NUMBERSENT в таблице tblAgent с помощью SUM (Services) из таблицы tblServices.

Я пришелс этим оператором обновления.

/*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: singleподзапрос возвращает более одной строки

Почему второе выражение выдает ошибку?

Ответы [ 7 ]

4 голосов
/ 09 ноября 2010

Давайте посмотрим, как эти 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 внешнего запроса.

3 голосов
/ 09 ноября 2010

@ Тони Эндрюс прав, и если вы все еще хотите использовать 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 голосов
/ 09 ноября 2010

Вы пытались запустить подзапрос самостоятельно, чтобы убедиться, что он возвращает только одну строку?

2 голосов
/ 09 ноября 2010

Вы переназначаете t с INNER JOIN, поэтому внешний t больше не связан с UPDATE.

1 голос
/ 09 ноября 2010

У вас есть две строки в tblAgent с одинаковыми agentid. Это, возможно, ускользнуло от вашего уведомления, если нет места, где agentid is NULL.

Для проверки:

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

Если какие-то строки возвращаются, это ваша проблема.

1 голос
/ 09 ноября 2010

На самом деле, теперь, когда я думаю об этом, первая версия является коррелированным подзапросом, а вторая - нет.Без данных, чтобы попробовать это, я не мог бы сказать вам, но это, вероятно, как-то связано с этим.

0 голосов
/ 09 ноября 2010

Разве это не должно быть

(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
     )

, если вы не?

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