2012-08-23 13 views
5

Ho una tabella da aggiornare utilizzando i record di un'altra tabella e sto facendo questo per portare le informazioni da un sistema (database) a un altro. Lo scenario è complesso po ', ma ho un disperato bisogno di aiuto :-sQUERY INTERNO SQL restituisce più di un valore in una query UPDATE

Ci sono 3 tavoli - component, scan e stage_link

componenti

component_id stage_id 
------------ -------- 
1    NULL 
2    NULL 
3    NULL 
4    NULL 
5    NULL 

scansione

scan_id component_id scanner_id   date_scanned 
------- ------------ ---------- ----------------------- 
1   1   scanner_a 2012-01-01 07:25:15.125 
2   1   scanner_b 2012-01-02 08:14:05.456 
3   2   scanner_a 2012-01-01 12:05:45.465 
4   3   scanner_a 2012-01-01 19:45:12.536 
5   1   scanner_c 2012-01-03 23:33:54.243 
6   2   scanner_b 2012-01-02 11:59:12.545 

stage_link

stage_link_id scanner_id stage_id 
    -------  ---------- ---------- 
     1   scanner_a 1 
     2   scanner_b 1  
     3   scanner_c 2  
     4   scanner_d 2  
     5   scanner_e 2 
     6   scanner_f 3 

ho bisogno di update tavolo component e set il campo stage_id secondo l'ultimo scansione. Ogni scansione porta il componente in una fase in base allo scanner coinvolto. Ho scritto la seguente query per update la tabella component, ma genera un errore che dice;

Subquery returned more than 1 value. This is not permitted when the subquery follows '='

La query è;

UPDATE component 
SET stage_id = (select stage_id 
       from(
        select scn.scanner_id, sl.stage_id 
        from scan scn 
        INNER JOIN stage_link sl ON scn.scanner_id = sl.scanner_id 
        where scn.date_scanned = ( select temp_a.max_date 
               from ( SELECT x.component_id, MAX(x.date_scanned) as max_date 
                 FROM scan x 
                 where component_id = x.component_id 
                 GROUP BY x.component_id 
                ) as temp_a 
               where component_id = temp_a.component_id) 
        ) as temp_b 
       ) 

Sto lavorando su MS SQL Server e voglia di risolvere la questione senza usare PHP o qualsiasi altra lingua.

Ho provato per un giorno a fare questo lavoro, ma ancora non ho avuto modo di farlo funzionare. Qualsiasi aiuto sarebbe molto apprezzato!

Grazie mille in anticipo :-)

risposta

4

Verificare questo senza utilizzare sottoquery correlate:

UPDATE Com 
SET  stage_id = Temp4.stage_id 
FROM dbo.component Com 
     INNER JOIN 
     ( 
      SELECT Temp2.component_id ,SL.stage_id 
      FROM dbo.stage_link SL 
      INNER JOIN (
          SELECT component_id ,scanner_id 
          FROM scan 
          WHERE date_scanned IN (
           SELECT MaxScanDate 
           FROM  
           ( 
            SELECT component_id , MAX(date_scanned) MaxScanDate 
            FROM scan 
            GROUP BY component_id 
           ) Temp 
          ) 
         ) Temp2 ON Temp2.scanner_id = SL.scanner_id 
     ) Temp4 ON Com.component_id = Temp4.component_id 

L'output:

component_id stage_id 
------------ ----------- 
1   2 
2   1 
3   1 
4   NULL 
5   NULL 
+0

Grazie mille per questo suggerimento !! è stato di grande aiuto !! Questo è esattamente quello che volevo !!! Sorriso grazie mille ancora !!!!! – mithilatw

+0

@mithilatw: prego. Si noti che potrebbero esserci 2 problemi con questa query (per riferimento futuro): (1) Questo presuppone che data_scanning sia sempre univoca. Se 2 scanner eseguono la scansione contemporaneamente (fino alla parte del nanosecondo), quindi il comando "SELECT component_id, scanner_id' restituirà 2 valori. (2) I campi nel join 'Temp2.scanner_id = SL.scanner_id' sono colonne basate su char, quindi se si hanno molti scanner_ids, questa query potrebbe iniziare a rallentare. – Kash

2

Bene, il vostro subquery sta tornando più di un valore. Un modo semplice è quello di fare un'aggregazione:

SET stage_id = (select max(stage_id) 
. . . 

Un probabile motivo è perché non v'è più di una scansione alla data più recente. Dal momento che puoi sceglierne solo uno, dato il contesto, allora MIN o MAX dovrebbero essere sufficienti.

Tuttavia, penso che la vera ragione è che non si hanno gli alias giusti per la sottoquery correlata. Credo che queste righe:

where component_id = x.component_id 
where component_id = temp_a.component_id 

dovrebbe includere gli alias, probabilmente essendo:

where component.component_id = x.component_id 
where component.component_id = temp_a.component_id 

Se questo non è sufficiente, è necessario spiegare ciò che si desidera. Vuoi che la query restituisca una scansione casuale dalla data più recente? Vuoi aggiornare il componente per tutte le scansioni nella data più recente?

È necessario investigare ulteriormente.Provare qualcosa di simile:

select scn.scanner_id, sl.stage_id, count(*) 
from scan scn INNER JOIN 
    stage_link sl 
    ON scn.scanner_id = sl.scanner_id join 
    (SELECT x.component_id, 
      MAX(x.date_scanned) as max_date 
     FROM scan x 
     GROUP BY x.component_id 
    ) cmax 
    on scn.component_id = cmax.component_id 
where scn.date_scanned = cmax.maxdate 
group by scn.scanner_id, sl.stage_id 
order by count(*) desc 
+0

ringrazio molto per la risposta !! Sto cercando di aggiornare tutti i componenti della tabella 'componente' osservando le scansioni eseguite su un componente alla volta. Pertanto, può esistere una sola scansione sulla data 'max' quando il componente' raggruppato per'. Il problema con il tuo suggerimento è la precisione delle informazioni. E il valore numerico massimo di 'stage_id' non è l'ultimo stadio del componente potrebbe essere (mi dispiace questo scenario è molto complesso!) Il mio problema è, ci può essere solo una scansione alla data' max' per componente e ancora restituisce più di 1 :-( – mithilatw

+1

@mithilatw.) Rimuovi la sottoquery dall'aggiornamento e inizia a indagare su dove si verifica il duplicato. Potrebbe essere un duplicato in una delle tabelle di scanner_id o per componente. i duplicati nella stessa data sono un probabile colpevole –

+0

Provato questo, non ha funzionato Ci dovrebbe essere un modo migliore di usare sottoquery correlate – Kash

2

Questa aveva bisogno di una funzione OLAP per lavorare:

UPDATE Component SET Component.stage_id = Stage_Link.stage_id 
FROM Component 
JOIN (SELECT component_id, scanner_id, 
      ROW_NUMBER() OVER(PARTITION BY component_id 
           ORDER BY date_scanned DESC) rownum 
     FROM Scan) Scan 
    ON Scan.component_id = Component.component_id 
    AND Scan.rownum = 1 
JOIN Stage_Link 
    ON Stage_Link.scanner_id = Scan.scanner_id 
WHERE Component.stage_id IS NULL 

che genera un set di risultati di:

Component 
component_id stage_id 
======================== 
1    2 
2    1 
3    1 
4    null 
5    null 

(ho anche un lavoro di SQL Fiddle example .)

+0

Grazie mille per il tuo tempo! Ho provato questo suggerimento nel mio database e il la query è durata più di 1 ora, ma non ha restituito alcun risultato. Vedo l'idea che tenti di implementare e sono d'accordo. Non capisco perché il mio database non lo accetto :-P – mithilatw

+0

# # # In realtà, il tuo problema è che il tuo database _did_ lo accetti - non sta funzionando il più velocemente possibile. È _probabilmente_ che esegue il recupero del valore per ogni riga. Questo non è un problema quando è piccolo (come per i dati di test), ma ovviamente non scala bene. SQL Server non supporta le CTE nelle istruzioni 'UPDATE'? Potresti provare ad ottenere almeno la parte con 'ROW_NUMBER()' in una CTE, in modo che venga eseguita una sola volta (probabilmente è il miglior risparmio). In alternativa, scaricare i risultati della sottoselezione in una tabella temporanea ed eseguire un aggiornamento correlato da quello. –

+0

@ X-Zero: Penso che intendessi una subquery correlata invece di CTE. SQL Server supporta entrambi, ma con sottoquery correlate, come già menzionato, valuta per ogni riga di Component e quindi non esattamente basata su set che spiegherebbe perché richiede molto tempo. – Kash

Problemi correlati