2012-08-23 12 views
5

Tengo una tabla que actualizar usando registros de otra tabla y estoy haciendo esto para traer información de un sistema (base de datos) a otro. El escenario es poco compleja, pero que desesperadamente necesitan ayuda :-sSQL INNER QUERY devuelve más de un valor en una consulta UPDATE

Hay 3 mesas - component, scan y stage_link

componentes

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

exploración

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 

necesito update la tabla component y set el campo stage_id según la última exploración. Cada exploración lleva el componente a un nivel de acuerdo con el escáner involucrado. He escrito la siguiente consulta para update la tabla component, pero arroja un error que dice;

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

La consulta es;

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 
       ) 

estoy trabajando en MS SQL Server y quiero resolver esto sin usar PHP o cualquier otro idioma.

He intentado por un día hacer que esto funcione, pero todavía no conseguí una forma de hacer que esto funcione. ¡Cualquier ayuda sería muy apreciada!

Muchas gracias de antemano :-)

Respuesta

4

Compruébalo sin utilizar subconsultas correlacionadas:

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 

La salida:

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

¡Muchas gracias por esta sugerencia! ¡fue de gran ayuda! ¡Esto es exactamente lo que quería! :-) ¡¡¡¡¡De nuevo, muchas gracias!!!!! – mithilatw

+0

@mithilatw: De nada. Tenga en cuenta que puede haber 2 problemas con esta consulta (para referencia futura): (1) Esto supone que el date_scanned siempre es único. Si 2 escáneres escanean al mismo tiempo (hasta la parte de nanosegundos), entonces 'SELECT component_id, scanner_id' devolverá 2 valores. (2) Los campos en la combinación 'Temp2.scanner_id = SL.scanner_id' son columnas basadas en char, por lo tanto, si tiene muchos scanner_ids, entonces esta consulta podría comenzar a ralentizarse. – Kash

2

Bueno, tu subconsulta devuelve más de un valor. Una forma fácil es hacer una agregación:

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

Una razón probable es porque hay más de una exploración en la fecha más reciente. Como solo puede elegir uno, dado el contexto, entonces el MIN o MAX debería ser suficiente.

Sin embargo, creo que la verdadera razón es que no tiene los alias correctos para la subconsulta correlacionada. Creo que estas líneas:

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

En caso de incluir los alias, siendo probablemente:

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

Si esto no es suficiente, es necesario explicar lo que quiere. ¿Desea que la consulta devuelva un escaneo aleatorio de la fecha más reciente? ¿Desea actualizar el componente para todos los escaneos en la fecha más reciente?

Debe investigar esto más a fondo.Intentar algo como esto:

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

Muchas gracias por la respuesta !! Estoy tratando de actualizar todos los componentes de la tabla 'component' mirando los escaneos que se realizan en un componente a la vez. Por lo tanto, solo puede haber un escaneo en la fecha 'max' cuando el componente 'agrupado por'. El problema con su sugerencia es la exactitud de la información. Y el valor numérico máximo de 'stage_id' no es la última etapa en que podría estar el componente (¡lo siento, este escenario es tan complejo!) Mi problema es que solo puede haber un escaneo en la fecha' máxima' por componente y aún así devuelve más de 1 :-( – mithilatw

+1

@mithilatw ... Elimine la subconsulta de la actualización y comience a investigar dónde se produce el duplicado. Podría ser un duplicado en una de las tablas por scanner_id o por componente. Sin embargo, la experiencia sugiere que duplicados en la misma fecha es un culpable muy probable. –

+0

Intenté esto, no funcionó. Debería haber una mejor manera que usar subconsultas correlacionadas. – Kash

2

Esto necesita una función OLAP para trabajar:

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 

que genera un conjunto de resultados de:

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

(también tengo un trabajo SQL violín example .)

+0

¡Muchas gracias por su tiempo! Probé esta sugerencia en mi base de datos y la la consulta se ejecutó durante más de 1 hora, pero aún así no devolvió el resultado. Veo la idea que intenta implementar y estoy de acuerdo con ella ... No entiendo por qué mi base de datos woul no lo acepta :-P – mithilatw

+0

# winces # En realidad, su problema es que su base de datos _did_ lo acepta, simplemente no se ejecuta tan rápido como podría. Probablemente está ejecutando la recuperación de valor para cada línea. Esto no es un problema cuando es pequeño (como para los datos de prueba), pero obviamente no escala bien. ¿SQL Server no admite CTE en las instrucciones 'UPDATE'? Puede tratar de obtener al menos la parte con 'ROW_NUMBER()' en un CTE, de modo que solo se ejecute una vez (es probable que este sea el mejor ahorro). Alternativamente, voltee los resultados de la selección secundaria en una tabla temporal y ejecute una actualización correlacionada a partir de eso. –

+0

@ X-Zero: creo que se refería a subconsulta correlacionada en lugar de CTE. SQL Server admite ambos, pero con subconsultas correlacionadas, como usted mencionó, evalúa cada fila de Componente y, por lo tanto, no está exactamente basada en conjunto, lo que explicaría por qué lleva mucho tiempo. – Kash

Cuestiones relacionadas