2012-02-29 11 views
19

Digamos que tengo la siguiente instrucción y la unión interna da como resultado 3 filas donde a.Id = b.Id, pero cada una de las 3 filas tiene valores b.Value diferentes. Como solo se está actualizando una fila de la tabla A, ¿cuál de los 3 valores se usa en la actualización?Actualizando y unir en varias filas, ¿qué valor de fila se usa?

UPDATE a 
SET a.Value = b.Value 
FROM tableA AS a 
INNER JOIN tableB as b 
ON a.Id = b.Id 
+0

@bluefeet - ¿Siempre? Pensé que este comportamiento dependería de la optimización del índice. Es decir, el orden en SQL no está garantizado en sentencias 'SELECT' a menos que se especifique una cláusula' ORDER BY'. Y desafortunadamente, este comportamiento no es trivialmente comprobable. El hecho de que no arroje un error (para algo como 'declaración devolvió más de un resultado') también me molesta. Estoy un poco receloso de 'JOIN's en' UPDATE's, por esta razón (a pesar de que puede haber simplificado algunas de las cosas que tenía que hacer en DB2). –

+0

No tiene datos para probar esto? Parece que deberías poder decirle a todos los demás. – JeffO

+0

@ X-Zero, puedo confirmar que una actualización con una fila unida en varias filas no es determinista. ¿Cómo es esto posible? Cuando pienso en las computadoras, creo que es determinista, esto es algo inusual para mí. – sooprise

Respuesta

15

No creo que haya reglas para este caso y no puede depender de un resultado en particular.

Si estás después de una fila específica, dicen que el último de ellos, puede utilizar apply, como:

UPDATE a 
SET  a.Value = b.Value 
FROM tableA AS a 
CROSS APPLY 
     (
     select top 1 * 
     from tableB as b 
     where b.id = a.id 
     order by 
       DateColumn desc 
     ) as b 
2

Esto es lo que ocurrió con el uso de SQL Server 2008

--drop table #b 
--drop table #a 
select 1 as id, 2 as value 
into #a 

select 1 as id, 5 as value 
into #b 

insert into #b 
select 1, 3 

insert into #b 
select 1, 6 

select * from #a 
select * from #b 

UPDATE #a 
SET #a.Value = #b.Value 
FROM #a 
INNER JOIN #b 
ON #a.Id = #b.Id 

Parece que usa el valor superior de una selección básica cada vez (fila 1 de select * from #b). Entonces, posiblemente dependa de la indexación. Sin embargo, no confiaría en la implementación establecida por SQL, ya que eso tiene la posibilidad de cambiar. En su lugar, sugeriría utilizar la solución presentada por Andomar para asegurarse de saber qué valor va a elegir.

En resumen, no confíe en la implementación predeterminada, cree la suya propia. Pero esta era una pregunta académica interesante :)

+0

Agradecería saber por qué obtuve el voto negativo para aquellos que rechazaron mi respuesta? :) –

5

Por lo general, lo que termina en este escenario es la primera fila que aparece en el orden del índice físico en la tabla. En la práctica real, debe tratar esto como no determinista e incluir algo que reduzca su resultado a una fila.

+1

Absolutamente, esto es algo que he aprendido ahora. Solo tengo curiosidad por saber qué estaban haciendo mis consultas antes de arreglarlo. – sooprise

2

La mejor opción en mi caso para actualizar múltiples registros es utilizar la combinación de consultas (Compatible con SQL Server 2008), en esta consulta tiene control total de lo que está actualizando. También puede usar la consulta de salida para realizar un procesamiento posterior.

Ejemplo: Sin cláusula de salida (sólo actualización)

;WITH cteB AS 
(SELECT Id, Col1, Col2, Col3 
    FROM B WHERE Id > 10 ---- Select Multiple records 
) 
MERGE A 
USING cteB 
ON(A.Id = cteB.Id) -- Update condition 
WHEN MATCHED THEN UPDATE 
SET 
A.Col1 = cteB.Col1, --Note: Update condition i.e; A.Id = cteB.Id cant appear here again. 
A.Col2 = cteB.Col2, 
A.Col3 = cteB.Col3; 

Ejemplo: Con la cláusula OputPut

CREATE TABLE #TempOutPutTable 
    { 
    PkId INT NOT NULL, 
    Col1 VARCHAR(50), 
    Col2 VARCHAR(50) 
    } 

;WITH cteB AS 
(SELECT Id, Col1, Col2, Col3 
FROM B WHERE Id > 10 
) 
MERGE A 
USING cteB 
ON(A.Id = cteB.Id) 
WHEN MATCHED THEN UPDATE 
SET 
A.Col1 = cteB.Col1, 
A.Col2 = cteB.Col2, 
A.Col3 = cteB.Col3 
OUTPUT 
INSERTED.Id, cteB.Col1, A.Col2 INTO #TempOutPutTable; 

--Do what ever you want with the data in temporary table 
SELECT * FROM #TempOutPutTable; -- you can check here which records are updated. 
+0

Por el bien de la integridad, 'UPDATE', también, apoya' OUTPUT', y le da "un control completo de lo que está puesta al día": la diferencia clave (en este contexto sólo actualización) es que * 'MERGE' fallará si varias filas coinciden, en contraposición a escoger uno (eficacia) al azar * –

+0

estoy de acuerdo sencilla instrucción de actualización puede tener cláusula de salida para conocer los registros actualizados, y la consulta de fusión fallará cuando actualizamos un único registro de tiempo múltiple. En caso habitual que deberíamos tratar de actualizar un único registro de una vez, como la actualización de tiempo múltiple, sólo conservará el último valor. – sudhAnsu63

0

Sí, me ocurrió un experimento similar a Justin Pihony:

IF OBJECT_ID('tempdb..#test') IS NOT NULL DROP TABLE #test ; 
SELECT 
1 AS Name, 0 AS value 
INTO #test 

IF OBJECT_ID('tempdb..#compare') IS NOT NULL DROP TABLE #compare ; 
SELECT 1 AS name, 1 AS value 
INTO #compare 
INSERT INTO #compare 
SELECT 1 AS name, 0 AS value; 

SELECT * FROM #test 
SELECT * FROM #compare 

UPDATE t 
SET t.value = c.value 
FROM #test t 
INNER JOIN #compare c 
    ON t.Name = c.name 

Toma la fila superior en la tabla de comparación del lado derecho. Puede invertir los valores de # compare.value a 0 y 1 y obtendrá el reverso. Estoy de acuerdo con los carteles de arriba ... es muy extraño que esta operación no arroje un mensaje de error ya que es completamente oculto que esta operación IGNORA los valores secundarios

Cuestiones relacionadas