Na DELETE
instrução a seguir, tento excluir todas as linhas, exceto a primeira ordenada por alguns critérios. (A consulta real faz mais sentido, isso é apenas uma reprodução. Todo o sys.objects
material é apenas para gerar dados de teste.)
Observe o filtro r <> 1
. No entanto, a OUTPUT
cláusula gera linhas excluídas com r = 1
. Como isso pode ser?
USE tempdb
SET XACT_ABORT ON
BEGIN TRAN
SELECT *
INTO #o
FROM sys.objects
SELECT TOP 2 name FROM #o ORDER BY object_id --debug output
DELETE k
OUTPUT Deleted.name, Deleted.r
FROM (
SELECT k.*, ROW_NUMBER() OVER (ORDER BY object_id) r
FROM #o k
) k
WHERE r <> 1 --OUTPUT returns rows with (r = 1)
SELECT TOP 2 name FROM #o ORDER BY object_id --debug output
ROLLBACK
Resultados da consulta:
(O terceiro conjunto de resultados está completo - apenas uma linha.)
Observe que todas as linhas, exceto a primeira, foram excluídas. A ordem de numeração da coluna r
parece não corresponder ao que foi solicitado. E há uma linha com r = 1
.
Este é o SQL Server 2014 CU3 com o sinalizador de rastreamento 4199 ativado.
Este é o comportamento esperado no momento
Na verdade, ele se comporta assim (pseudocódigo)
Embora este seja o comportamento esperado atualmente definido, não é realmente razoável e eles dizem
Não testei no SQL Server 2014, mas no 2012 o plano é o seguinte.
Após o operador delete (à esquerda), os valores das colunas das linhas excluídas são classificados de volta em
object_id
ordem e o row_number reaplicado.Parece que o mesmo está acontecendo no seu caso a partir dos resultados. (as tabelas temporárias têm um id negativo e são classificadas primeiro antes das
sysrscols
quais têm um baixo positivoobject_id
de3
).Assim como a semântica duvidosa do resultado, a segunda classificação por
object_id
não parece estritamente necessária neste plano, pois parece provável que eles já estejam classificados nessa ordem em qualquer caso.Em relação às soluções alternativas para este caso específico, alterar a cláusula de saída para
OUTPUT Deleted.name, 1 + Deleted.r AS r
funcionaria.Para cláusulas mais complicadas
WHERE
, acho que você precisaria de um passe para calcular o row_number e, em seguida, uma junção. por exemploResultados
Você realmente precisa de ROW_NUMBER e OUTPUT? Que tal um bom DELETE baseado em conjunto, por exemplo, algo assim: