web-dev-qa-db-fra.com

ExecuteScalar vs ExecuteNonQuery lors du retour d'une valeur d'identité

Essayer de déterminer s'il est préférable d'utiliser ExecuteScalar ou ExecuteNonQuery si je veux renvoyer la colonne d'identité d'une ligne nouvellement insérée. J'ai lu cette question et je comprends les différences, mais en examinant du code que j'ai écrit il y a quelques semaines (tout en empruntant beaucoup à ce site), j'ai trouvé que dans mes encarts j'utilisais ExecuteScalar, comme ceci:

public static int SaveTest(Test newTest)
{
    var conn = DbConnect.Connection();
    const string sqlString = "INSERT INTO dbo.Tests ( Tester , Premise ) " +
                             "               VALUES ( @tester , @premise ) " +
                             "SET @newId = SCOPE_IDENTITY(); ";
    using (conn)
    {
        using (var cmd = new SqlCommand(sqlString, conn))
        {
            cmd.Parameters.AddWithValue("@tester", newTest.tester);
            cmd.Parameters.AddWithValue("@premise", newTest.premise);
            cmd.Parameters.Add("@newId", SqlDbType.Int).Direction = ParameterDirection.Output;

            cmd.CommandType = CommandType.Text;
            conn.Open();
            cmd.ExecuteScalar();

            return (int) cmd.Parameters["@newId"].Value;
        }
    }
}

Cela fonctionne bien pour ce dont j'ai besoin, donc je me demande

  1. Si je devrais utiliser ExecuteNonQuery ici parce que c'est "plus approprié" pour faire des insertions?
  2. La récupération de la valeur d'identité serait-elle la même dans les deux cas puisque j'utilise un paramètre de sortie?
  3. Y a-t-il des résultats de performance associés à l'une ou l'autre façon?
  4. Existe-t-il généralement une meilleure façon de procéder globalement?

J'utilise Visual Studio 2010, .NET 4.0 et SQL Server 2008r2, au cas où cela ferait une différence.

22
techturtle

Comme suggéré par Aaron, une procédure stockée la rendrait plus rapide car elle permet à Sql Server de compiler votre lot SQL. Cependant, vous pouvez toujours opter pour l'une ou l'autre approche: ExecuteScalar ou ExecuteNonQuery. À mon humble avis, la différence de performances entre eux est si faible, que l'une ou l'autre méthode est tout aussi "appropriée".

Cela dit, je ne vois pas l'intérêt d'utiliser ExecuteScalar si vous saisissez la valeur d'identité à partir d'un paramètre de sortie. Dans ce cas, la valeur renvoyée par ExecuteScalar devient inutile.

Une approche que j'aime car elle nécessite moins de code, utilise ExecuteScalar sans paramètres de sortie:

public static int SaveTest(Test newTest)
{
    var conn = DbConnect.Connection();
    const string sqlString = "INSERT INTO dbo.Tests ( Tester , Premise ) " +
                             "               VALUES ( @tester , @premise ) " +
                             "SELECT SCOPE_IDENTITY()";
    using (conn)
    {
        using (var cmd = new SqlCommand(sqlString, conn))
        {
            cmd.Parameters.AddWithValue("@tester", newTest.tester);
            cmd.Parameters.AddWithValue("@premise", newTest.premise);

            cmd.CommandType = CommandType.Text;
            conn.Open();
            return (int) (decimal) cmd.ExecuteScalar();

        }
    }
}

Bonne programmation!

EDIT : Notez que nous devons lancer deux fois: de l'objet en decimal, puis en int (merci à techturtle pour l'avoir noté).

28
Diego