Dada esta tabela:
CREATE TABLE dbo.Target (
TargetId int identity(1, 1) NOT NULL,
Color varchar(20) NOT NULL,
Action varchar(10) NOT NULL, -- of course this should be normalized
Code int NOT NULL,
CONSTRAINT PK_Target PRIMARY KEY CLUSTERED (TargetId)
);
Em dois cenários ligeiramente diferentes, desejo inserir linhas e retornar os valores da coluna de identidade.
Cenário 1
INSERT dbo.Target (Color, Action, Code)
OUTPUT inserted.TargetId
SELECT t.Color, t.Action, t.Code
FROM
(VALUES
('Blue', 'New', 1234),
('Blue', 'Cancel', 4567),
('Red', 'New', 5678)
) t (Color, Action, Code)
;
Cenário 2
CREATE TABLE #Target (
Color varchar(20) NOT NULL,
Action varchar(10) NOT NULL,
Code int NOT NULL,
PRIMARY KEY CLUSTERED (Color, Action)
);
-- Bulk insert to the table the same three rows as above by any means
INSERT dbo.Target (Color, Action, Code)
OUTPUT inserted.TargetId
SELECT t.Color, t.Action, t.Code
FROM #Target
;
Pergunta
Posso confiar que os valores de identidade retornados da dbo.Target
inserção da tabela serão retornados na ordem em que existiam na VALUES
cláusula 1) e na #Target
tabela 2), para que eu possa correlacioná-los por sua posição no conjunto de linhas de saída de volta à entrada original?
Para referência
Aqui está um código C# reduzido que demonstra o que está acontecendo no aplicativo (cenário 1, que será convertido em breve para usar SqlBulkCopy
):
public IReadOnlyCollection<Target> InsertTargets(IEnumerable<Target> targets) {
var targetList = targets.ToList();
const string insertSql = @"
INSERT dbo.Target (
CoreItemId,
TargetDateTimeUtc,
TargetTypeId,
)
OUTPUT
Inserted.TargetId
SELECT
input.CoreItemId,
input.TargetDateTimeUtc,
input.TargetTypeId,
FROM
(VALUES
{0}
) input (
CoreItemId,
TargetDateTimeUtc,
TargetTypeId
);";
var results = Connection.Query<DbTargetInsertResult>(
string.Format(
insertSql,
string.Join(
", ",
targetList
.Select(target => $@"({target.CoreItemId
}, '{target.TargetDateTimeUtc:yyyy-MM-ddTHH:mm:ss.fff
}', {(byte) target.TargetType
})";
)
)
)
.ToList();
return targetList
.Zip( // The correlation that relies on the order of the two inputs being the same
results,
(inputTarget, insertResult) => new Target(
insertResult.TargetId, // with the new TargetId to replace null.
inputTarget.TargetDateTimeUtc,
inputTarget.CoreItemId,
inputTarget.TargetType
)
)
.ToList()
.AsReadOnly();
}
Não, você não pode confiar em nada para ser garantido sem uma garantia real documentada. A documentação afirma explicitamente que não existe tal garantia.
Isso dependeria de muitas suposições não documentadas
Um exemplo de falha do ponto dois (assumindo PK clusterizado de
(Color, Action)
) pode ser visto se você adicionar 600 linhas àVALUES
cláusula. Em seguida, o plano possui um operador de classificação antes da inserção, perdendo sua ordem original naVALUES
cláusula.Existe uma maneira documentada de atingir seu objetivo e isso é adicionar uma numeração à fonte e usar
MERGE
em vez deINSERT
@um cavalo sem nome
Sim você pode. Ordenar garantias no SQL Server… afirma que
Então você poderia usar
Isso garantiria que os valores de identidade sejam atribuídos em ordem,
t.SourceId
mas não que sejam exibidos em qualquer ordem específica ou que os valores de coluna de identidade atribuídos não tenham lacunas (por exemplo, se uma inserção simultânea for tentada).