Subquery returned more than 1 value...

mayrastevenson

Neuer Benutzer
Beiträge
3
Hallo zusammen,

Suche Hilfe zur Lösung dieses Problems: "Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression"

Hoffentlich kann mir jemand dabei helfen. Es geht um eine Stored Procedure, die nicht nur die Anzahl von Opportunities mit Status "Pending" oder "Accepted" sondern auch die Namen der Opportunities mit diesen Stati ausgeben sollte. Folgende Probleme:

1. Der SELECT, der mir die Namen der Opportunities ausgibt, kann mir aber in manchen Faellen mehr als einen Wert ausgeben. Ich kann aber den SELECT nicht einer Variable zuweisen, z.B. mi SET, wie kann ich das in dem Code anders zuweisen oder wie kann man den Code abändern in dem Fall?

2. Der Code müsste auch die Namen der Opportunities ausgeben, naemlich nach "Die Opportunities sind:" und diese dem Empfaenger einer Email zugeschickt werden. Wie kann ich aber das so darstellen, so dass diese Opportunities Zeile fuer Zeile ausgegeben werden und nicht alles in einer Zeile, damit diese Ergebnisse dann per Email zugeschickt werden können?

Es wuerde mich sehr freuen, wenn mir ein Specialist in Stored Procedures mit SQL Server dabei hilft, da ich schon längst versuche, diese Probleme zu lösen. Folgendes ist der Code:

Code:
CREATE PROCEDURE [dbo].[Pending_Opportunities]
AS


BEGIN 

declare @V_USER VARCHAR(20),   
    @V_NAME VARCHAR(100),
    @V_OPP_NAME VARCHAR(100),   
    @V_ANZAHL INT,   
    @V_EMAIL VARCHAR(60),      
    @V_BODY VARCHAR(MAX),   
    @V_ANZAHL_AKTIV int

DECLARE CUR CURSOR FOR 
SELECT USER, COMPLETEDNAME, EMAILDIRECTION  FROM USER_SIEBEL  WHERE STATE = 'G'  

OPEN CUR  FETCH NEXT FROM CUR INTO @V_USER, @V_NAME, @V_EMAIL  
WHILE @@FETCH_STATUS = 0 

BEGIN   

  
SET @V_OPP_NAME = (    SELECT  T1.NAME FROM dbo.S_OPTY T1        
            INNER JOIN dbo.S_OPTY_POSTN T2 ON T1.PR_POSTN_ID = T2.POSITION_ID AND T1.ROW_ID = T2.OPTY_ID       
            INNER JOIN dbo.S_OPTY_BU T3 ON T3.BU_ID = '1-O5H' AND T1.ROW_ID = T3.OPTY_ID       
            INNER JOIN dbo.S_PARTY T4 ON T2.POSITION_ID = T4.ROW_ID       
            INNER JOIN dbo.S_PARTY T5 ON T3.BU_ID = T5.ROW_ID       
            LEFT OUTER JOIN dbo.S_USER T6 ON T1.CREATED_BY = T6.PAR_ROW_ID   
               LEFT OUTER JOIN dbo.S_SYS_KEYMAP T7 ON T1.ROW_ID = T7.SIEBEL_SYS_KEY       
            LEFT OUTER JOIN dbo.S_OPTY T8 ON T1.PAR_OPTY_ID = T8.ROW_ID       
            LEFT OUTER JOIN dbo.S_ADDR_ORG T9 ON T1.PR_OU_ADDR_ID = T9.ROW_ID       
            LEFT OUTER JOIN dbo.S_ORG_EXT T10 ON T1.PR_DEPT_OU_ID = T10.PAR_ROW_ID       
            LEFT OUTER JOIN dbo.S_POSTN T11 ON T1.PR_POSTN_ID = T11.PAR_ROW_ID       
            LEFT OUTER JOIN dbo.S_PRI_LST T12 ON T10.CURR_PRI_LST_ID = T12.ROW_ID       
            LEFT OUTER JOIN dbo.S_USER T13 ON T11.PR_EMP_ID = T13.PAR_ROW_ID      
            WHERE  T1.STATUS_CD in ( 'Pending' , 'Accepted')    
            --AND T13.LOGIN = @V_USER    
            AND (T13.LOGIN in (@V_USER) )
            --OR (T6.LOGIN in (@V_USER)) )
            AND T1.SUM_EFFECTIVE_DT <= GETDATE()     
            AND T1.SUM_EFFECTIVE_DT >= '20100601' )


SET @V_ANZAHL = 0  
SET @V_ANZAHL_AKTIV = 0  
SET @V_ANZAHL = (    SELECT  COUNT(*) FROM dbo.S_OPTY T1        
            INNER JOIN dbo.S_OPTY_POSTN T2 ON T1.PR_POSTN_ID = T2.POSITION_ID AND T1.ROW_ID = T2.OPTY_ID       
            INNER JOIN dbo.S_OPTY_BU T3 ON T3.BU_ID = '1-O7F' AND T1.ROW_ID = T3.OPTY_ID       
            INNER JOIN dbo.S_PARTY T4 ON T2.POSITION_ID = T4.ROW_ID       
            INNER JOIN dbo.S_PARTY T5 ON T3.BU_ID = T5.ROW_ID       
            LEFT OUTER JOIN dbo.S_USER T6 ON T1.CREATED_BY = T6.PAR_ROW_ID   
               LEFT OUTER JOIN dbo.S_SYS_KEYMAP T7 ON T1.ROW_ID = T7.SIEBEL_SYS_KEY       
            LEFT OUTER JOIN dbo.S_OPTY T8 ON T1.PAR_OPTY_ID = T8.ROW_ID       
            LEFT OUTER JOIN dbo.S_ADDR_ORG T9 ON T1.PR_OU_ADDR_ID = T9.ROW_ID       
            LEFT OUTER JOIN dbo.S_ORG_EXT T10 ON T1.PR_DEPT_OU_ID = T10.PAR_ROW_ID       
            LEFT OUTER JOIN dbo.S_POSTN T11 ON T1.PR_POSTN_ID = T11.PAR_ROW_ID       
            LEFT OUTER JOIN dbo.S_PRI_LST T12 ON T10.CURR_PRI_LST_ID = T12.ROW_ID       
            LEFT OUTER JOIN dbo.S_USER T13 ON T11.PR_EMP_ID = T13.PAR_ROW_ID      
            WHERE  T1.STATUS_CD in ( 'Pending' , 'Accepted')    
            --AND T13.LOGIN = @V_USER    
            AND (T13.LOGIN in (@V_USER) )
            --OR (T6.LOGIN in (@V_USER)) )
            AND T1.SUM_EFFECTIVE_DT <= GETDATE()     
            AND T1.SUM_EFFECTIVE_DT >= '20100601' )   

           
SET @V_ANZAHL_AKTIV = (     SELECT COUNT(*)           
                FROM dbo.S_EVT_ACT T1         
                WHERE (        (T1.APPT_REPT_REPL_CD IS NULL)         
                        AND (T1.TEMPLATE_FLG != 'Y' AND T1.TEMPLATE_FLG != 'P' OR T1.TEMPLATE_FLG IS NULL)         
                        AND (T1.PRIV_FLG = 'N' OR T1.PRIV_FLG IS NULL OR T1.OWNER_PER_ID = '1-ONG')
                      )         
                AND T1.TODO_PLAN_START_DT <=  getdate() 
                AND T1.TODO_PLAN_START_DT >=  '20100601' 
                    AND T1.EVT_STAT_CD IN ('Pending','Programmed')         
                AND T1.TODO_CD != 'Erinnerung'         
                AND T1.TODO_CD != 'Task'        
                AND T1.OPTY_ID is not null       
                AND T1.NAME NOT LIKE '%hat Ihnen zu einer Opportunity eingeladen:%'        
                AND T1.OWNER_LOGIN = @V_USER )   


if @V_ANZAHL + @V_ANZAHL_AKTIV > 0  
begin   

    SET @V_BODY = '<BODY style="color:navy; font-size:10pt; font-family:verdana">'   
    SET @V_BODY = @V_BODY + 'Sehr geehrte(r) ' + @V_USER + ',<br>'
    SET @V_BODY = @V_BODY + 'Sie haben '     IF @V_ANZAHL > 0 

    BEGIN     SET @V_BODY = @V_BODY + cast(@V_ANZAHL as varchar) + ' Opportunities mit Status Pending'        
    SET @V_BODY = @V_BODY + '<br>Die Opportunities sind:<br>'
    SET @V_BODY = @V_BODY + '<BR>'   
    SET @V_BODY = @V_BODY + @V_OPP_NAME

    IF @V_ANZAHL_AKTIV > 0     
        SET @V_BODY = @V_BODY + ' und '   
    END   

IF @V_ANZAHL_AKTIV > 0   
BEGIN    
    SET @V_BODY = @V_BODY + cast(@V_ANZAHL_AKTIV as varchar) + ' Activities mit Status Pending/Programmed' END 
    SET @V_BODY = @V_BODY + '<BR>'   

</BODY>'    

exec msdb..sp_send_dbmail @profile_name = 'Siebel Administrator',
     @recipients = @V_EMAIL, 
     @subject = 'SIEBEL Email zur Verfolgung der Opportunities / Activities', 
     @body = @V_BODY, 
     @body_format = 'HTML', 
     @exclude_query_output = 1
end


FETCH NEXT FROM CUR INTO @V_USER, @V_NAME, @V_EMAIL 
END  
CLOSE CUR 
DEALLOCATE CUR  END
 
Werbung:
1. Der SELECT, der mir die Namen der Opportunities ausgibt, kann mir aber in manchen Faellen mehr als einen Wert ausgeben. Ich kann aber den SELECT nicht einer Variable zuweisen, z.B. mi SET, wie kann ich das in dem Code anders zuweisen oder wie kann man den Code abändern in dem Fall?

Ich denke, Du müßtest da eine Aggregation machen. Offenbar ist aber eine String-Aggregation in M$SQL nicht so einfach wie in anderen Systemen.
Vielleicht hilft http://stackoverflow.com/questions/273238/how-to-use-group-by-to-concatenate-strings-in-sql-server
 
Danke für die Antwort, aber in den SELECTs ist nichts zu ändern. Der Kunde hatte uns diese SELECTs so zugeschickt. Es geht nur um die Punkte, die ich erwähnt hatte und für die ich leider keine Lösung finden kann (schon seit Tagen).
 
Danke für die Antwort, aber in den SELECTs ist nichts zu ändern.

Dann wird Dir auch kam zu helfen sein.

Um das mal in PostgreSQL nachzustellen, die Fehlermeldung klingt ähnlich:

Code:
test=*# select 1 = (select * from generate_Series(1,3) s);
ERROR:  more than one row returned by a subquery used as an expression

Die Query im inneren liefert mehr als 1 Tupel:

Code:
test=*# select * from generate_Series(1,3) s;
 s
---
 1
 2
 3
(3 rows)

Würde sie nur einen liefern ginge es:

Code:
test=*# select 1 = (select * from generate_Series(1,1) s);
 ?column?
----------
 t
(1 row)

Du oder eurer Kunde hat also einen Fehler im SQL. Wenn er den nicht abstellen will muß er damit leben.
 
Was kannst/darfst du denn ändern?

Wenn man nichts an der Logik ändern kann/darf, dann funktioniert deine/seine Prozedur nur dann, wenn ein USER nur eine Opportunity hat.

In welchen Punkt genau erhoffst du dir Hilfe von uns?
 
Wenn du das ganze sowieso innerhalb der SP umsetzt kannst du auch eine Schleife oder einen Cursor über das Ergebnis des Selects laufen lassen um die Werte zu verketten. Das ist zwar nicht ideal oder schnell aber es sollte bei kleineren Mengen kein Problem sein.

In diesem Code müsstest du zweimal das Select Statement packen und sonst nur den Spaltennamen der zu aggregierenden VARCHAR() Spalte kennen.
Code:
DECLARE    @counter SMALLINT,
        @anzahl SMALLINT,
        @text VARCHAR(8000)

SET        @counter = 0
SET        @text = ''

SET        @anzahl    = (    SELECT    count(*)
                    FROM    (    SELECT    bezeichnung
                                FROM    unt_mer ) t )
IF        @anzahl > 10
BEGIN
    SET        @anzahl = 10 --maximale Anzahl
END

WHILE    @counter <= @anzahl
BEGIN
    SET        @counter = @counter + 1
    SET        @text = @text + isnull('; ' + (    SELECT    t2.bezeichnung
                                            FROM    (    SELECT    ROW_NUMBER() OVER (ORDER BY t1.bezeichnung) AS zeile,
                                                                t1.bezeichnung
                                                        FROM    (    SELECT    bezeichnung
                                                                    FROM    unt_mer ) t1 ) t2
                                            WHERE    t2.zeile = @counter )
                                    ,'')
END


SET        @text = right(@text,datalength(@text)-2)

SELECT    @text



DECLARE    @counter SMALLINT,
        @anzahl SMALLINT,
        @text VARCHAR(8000)

SET        @counter = 0
SET        @text = ''

SET        @anzahl    = (    SELECT    count(*)
                    FROM    (    /*dein select*/ ) t )
IF        @anzahl > 10
BEGIN
    SET        @anzahl = 10 --maximale Anzahl
END

WHILE    @counter <= @anzahl
BEGIN
    SET        @counter = @counter + 1
    SET        @text = @text + isnull('; ' + (    SELECT    t2.deine_spalte
                                            FROM    (    SELECT    ROW_NUMBER() OVER (ORDER BY t1.deine_spalte) AS zeile,
                                                                t1.deine_spalte
                                                        FROM    (    /*dein select*/ ) t1 ) t2
                                            WHERE    t2.zeile = @counter )
                                    ,'')
END


SET        @text = right(@text,datalength(@text)-2)

RETURN    @text
 
Danke euch allen fuer eure Antworten. Wie geschrieben die SELECTs sind nicht das Problem sondern der Code, der gaendert werden soll/muss und wir duerfen das machen, so dass diese die vom Kunden gewuenschten Ergebnisse liefern kann.

@ukulele: da hast du Recht, ein Cursor koennte eine Loesung sein.

Ich habe einen Cursor benutzt, um die Ergebnisse dann durch PRINT bei der Ausfuehrung ueber den SQL Server auszugeben (ohne Header der Spalte NAME). Das einzige Problem, das ich noch loesen muss, ist die Konkatenierung mit dem Rest des @BODY.

Folgendes Cursor habe ich noch in dem Code eingefuegt:

Code:
DECLARE CUR_TEST CURSOR FOR
  SELECT T1.NAME FROM dbo.S_OPTY T1
  INNER JOIN dbo.S_OPTY_POSTN T2 ON T1.PR_POSTN_ID = T2.POSITION_ID AND T1.ROW_ID = T2.OPTY_ID
  INNER JOIN dbo.S_OPTY_BU T3 ON T3.BU_ID = '1-O5H' AND T1.ROW_ID = T3.OPTY_ID
  INNER JOIN dbo.S_PARTY T4 ON T2.POSITION_ID = T4.ROW_ID
  INNER JOIN dbo.S_PARTY T5 ON T3.BU_ID = T5.ROW_ID
  LEFT OUTER JOIN dbo.S_USER T6 ON T1.CREATED_BY = T6.PAR_ROW_ID
  LEFT OUTER JOIN dbo.S_SYS_KEYMAP T7 ON T1.ROW_ID = T7.SIEBEL_SYS_KEY
  LEFT OUTER JOIN dbo.S_OPTY T8 ON T1.PAR_OPTY_ID = T8.ROW_ID
  LEFT OUTER JOIN dbo.S_ADDR_ORG T9 ON T1.PR_OU_ADDR_ID = T9.ROW_ID
  LEFT OUTER JOIN dbo.S_ORG_EXT T10 ON T1.PR_DEPT_OU_ID = T10.PAR_ROW_ID
  LEFT OUTER JOIN dbo.S_POSTN T11 ON T1.PR_POSTN_ID = T11.PAR_ROW_ID
  LEFT OUTER JOIN dbo.S_PRI_LST T12 ON T10.CURR_PRI_LST_ID = T12.ROW_ID
  LEFT OUTER JOIN dbo.S_USER T13 ON T11.PR_EMP_ID = T13.PAR_ROW_ID
  WHERE T1.STATUS_CD in ( 'Pending' , 'Accepted')
  --AND T13.LOGIN = @V_USER
  AND (T13.LOGIN in (@V_USER) )
--OR (T6.LOGIN in (@V_USER)) )
  AND T1.SUM_EFFECTIVE_DT <= GETDATE()
  AND T1.SUM_EFFECTIVE_DT >= '20100601' )
OPEN CUR_TEST
  FETCH NEXT FROM CUR_TEST INTO @V_OP_NOMBRE
WHILE (@@FETCH_STATUS = 0)
BEGIN
  PRINT @V_OP_NOMBRE
  FETCH CUR_TEST INTO @V_OP_NOMBRE
END
CLOSE CUR_TEST
DEALLOCATE CUR_TEST


Aber, ich haenge noch in der Konkatenierung:

Code:
SET @V_BODY = '<BODY style="color:navy; font-size:10pt; font-family:verdana">'
  SET @V_BODY = @V_BODY + 'Sehr geehrte(r) ' + @V_USER + ',<br>'
  SET @V_BODY = @V_BODY + 'Sie haben ' IF @V_ANZAHL > 0
  BEGIN SET @V_BODY = @V_BODY + cast(@V_ANZAHL as varchar) + ' Opportunities mit Status Pending'
  SET @V_BODY = @V_BODY + '<br>Die Opportunities sind:<br>'
SET @V_BODY = @V_BODY + '<BR>'
  SET @V_BODY = @V_BODY + @V_OP_NAME

Wenn ich dies ausfuehre:

Code:
exec msdb..sp_send_dbmail @profile_name = 'Siebel Administrator',
@recipients = @V_EMAIL,
@subject = 'SIEBEL Email zur Verfolgung der Opportunities / Activities',
@body = @V_BODY,
@body_format = 'HTML',
@exclude_query_output = 1
end

dieser gibt mir den Inhalt des @V_BODY, aber an der Stelle der Ausgabe der Namen der Opportunities, gibt der mir nur der letzte Row (Zeile) der Ergebnisse der SELECT in dem Cursor CUR_TEST...wie kann ich anstatt SET was Anderes benutzen, so dass mir alle Ergebnisse der SELECT ausgegeben werden und nicht nur die letzte Zeile?

Beispiel der Ausgabe, die ich per Email erhalten habe:

Code:
Sehr geehrte(r) Mayra,
Sie haben 47 Opportunities mit Status Pending.
Die Opportunities sind:

"Name der Opportunity in der letzten Row des SELECT mit Cursor CUR_TEST " und 12
Activities mit Status Pending/Programmed.
 
Also ich bin mir nicht ganz sicher ob ich verstehe was du hier versuchst aber: Du kannst mit SET einer Variablen immer nur einen Wert zuweisen. Für mehrere Werte brauchst du entweder mehrere Variablen oder eine Tabelle. Um mehrere Zeilen zu verketten und als einen Wert zu behandeln hast du ja bereits mit einem Cursor gearbeitet, das wäre hier nicht anders. Nur das du Zeilenumbrüche und Leerschritte benötigen wirst um eine Tabellen-Optik zu erzeugen.
 
Werbung:
Code:
OPEN dein_cursor
FETCH NEXT FROM dein_cursor INTO @temp
WHILE (@@FETCH_STATUS = 0)
BEGIN
SET @temp_zwei = @v_body
SET @v_body = @temp_zwei + @temp + "hier CRLF einfügen"
FETCH  dein_cursor INTO @temp
END

Auch wenn ich keine Ahnung von Syntax etc. habe...
Das wäre mein erster Versuch das Problem zu lösen.
Und ich sehe auch nicht warum das nicht funktionieren sollte ^^
Korrigiere mich, wenn ich falsch liege :)

Edit:
Habe alles was ich so eingefügt habe mal klein geschrieben.
 
Zurück
Oben