2010-11-08 18 views
0

Voici une version simplifiée de ma table.Pourquoi la mise à jour ne fonctionne-t-elle pas avec une jointure interne?

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 

L'exigence est de mettre à jour la colonne NUMBERSENT dans la table tblAgent avec le SUM (Services) de table tblServices.

Je suis venu avec cette déclaration de mise à jour.

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

Lorsque je change la syntaxe de cette instruction en syntaxe INNER JOIN, elle échoue.

/*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) 

Cela pose une erreur ORA-01427: sous-requête une seule rangée retourne plus d'une ligne

Pourquoi la deuxième déclaration jetterait une erreur?

Répondre

3

@Tony Andrews est juste et si vous voulez continuer à utiliser INNER JOIN vous devriez écrire ceci:

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) 

(Pour avoir DML une colonne commune supérieure et intérieure, pour ne pas retourner plus d'un rangée)

Mais bien sûr, je pense que c'est juste compliquant votre travail et rien de plus..Utiliser la première variante ... C'est un meilleur conseil.

3

Avez-vous essayé d'exécuter la sous-requête par elle-même pour vous assurer qu'elle ne renvoie qu'une seule ligne?

+0

+1. C'est un message d'erreur assez explicite ... la sous-requête doit retourner une ligne et elle en retourne plus d'une. – ceejayoz

0

pas Si tel était

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

si vous vous joignez par agentid ou

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

si vous n'êtes pas?

+0

Non. La sous-requête doit renvoyer uniquement le SUM (Services). – abhi

+0

... qui est ce que fait la deuxième sous-requête. – SteveCav

1

En fait maintenant que je pense à ce sujet, la première version est une sous-requête corrélée, le second ne l'est pas. Sans données pour l'essayer avec je ne pourrais pas vous dire mais cela a probablement quelque chose à faire avec lui.

2

Vous réaffectez t avec l'INNER JOIN, donc le t extérieur n'est plus lié à la mise à jour.

+0

Oui, il existe deux utilisations non liées de la table tblAgent. –

1

Vous avez deux lignes tblAgent avec le même agentid. Cela pourrait avoir échappé à votre avis s'il n'y a pas où agentid is NULL.

Pour vérifier:

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

Si toutes les lignes reviennent, c'est votre problème.

4

Regardons comment les 2 requêtes fonctionnent plus en détail:

D'abord, celui qui fonctionne:

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

Il est clair que la sous-requête doit renvoyer une valeur unique à utiliser dans le SET, donc nous allons regarder que lui-même:

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

Notez que l'alias « t » corrèle ici la sous-requête à la requête externe - à savoir qu'il a une valeur spécifique lorsque la sous-requête est évaluée par exemple

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

Par conséquent, bien que les résultats des groupes de requêtes par x.agentid, il n'y a en fait une valeur de x.agentid à-dire la valeur actuelle du t.agentid (par exemple 123). Donc ça fonctionne.

Regardons maintenant le deuxième de sous-requête de requête sur son propre:

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

Ce t.agentid temps est pas une référence à la requête externe, de sorte que cette requête est pas corrélée à la requête externe . Il peut retourner plus de 1 ligne (il suffit de l'exécuter et voir), et donc ne peut pas être utilisé en toute sécurité dans la clause SET de la requête externe.