web-dev-qa-db-fra.com

Est-il possible que les instructions SQL s'exécutent simultanément dans une seule session dans SQL Server?

J'ai écrit une procédure stockée qui utilise une table temporaire. Je sais que dans SQL Server, les tables temporaires ont une portée de session. Cependant, je n'ai pas pu trouver d'informations définitives sur ce dont une session est capable. En particulier, s'il est possible que cette procédure stockée s'exécute deux fois simultanément dans une même session, un niveau d'isolement nettement plus élevé est requis pour une transaction au sein de cette procédure car les deux exécutions partagent désormais une table temporaire.

16
Trevor Giddings

Bien que la réponse de Brent soit correcte à toutes fins pratiques, et que je n'ai jamais vu quelqu'un s'inquiéter, cela est possible pour plusieurs invocations de une procédure stockée dans une session pour s'influencer mutuellement via une table #temp à portée de session.

La bonne nouvelle est qu'il est extrêmement improbable de se produire dans la nature car

1) Les tables #Temp déclarées à l'intérieur d'une procédure stockée ou de lots imbriqués n'ont pas réellement de visibilité de session (ou de durée de vie). Et ce sont de loin le cas le plus courant.

2) Il nécessite MultipleActiveResultsets et soit une programmation client asynchrone très étrange, soit que la procédure stockée renvoie un ensemble de résultats au milieu, et que le client appelle une autre instance de la procédure stockée lors du traitement des résultats à partir de la première.

Voici un exemple artificiel:

using System;
using System.Data.SqlClient;

namespace ado.nettest
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var con = new SqlConnection("Server=localhost;database=tempdb;integrated security=true;MultipleActiveResultSets = True"))
            {
                con.Open();

                var procDdl = @"
create table #t(id int)
exec ('
create procedure #foo
as
begin
  insert into #t(id) values (1);
  select top 10000 * from sys.messages m, sys.messages m2;
  select count(*) rc from #t;
  delete from #t;
end
');
";
                var cmdDDL = con.CreateCommand();
                cmdDDL.CommandText = procDdl;
                cmdDDL.ExecuteNonQuery();

                var cmd = con.CreateCommand();
                cmd.CommandText = "exec #foo";
                using (var rdr = cmd.ExecuteReader())
                {
                    rdr.Read();

                    var cmd2 = con.CreateCommand();
                    cmd2.CommandText = "exec #foo";
                    using (var rdr2 = cmd2.ExecuteReader())
                    {

                    }

                    while (rdr.Read())
                    {

                    }
                    rdr.NextResult();
                    rdr.Read();
                    var rc = rdr.GetInt32(0);
                    Console.WriteLine($"Numer of rows in temp table {rc}");

                }


            }

            Console.WriteLine("Hit any key to exit");
            Console.ReadKey();
        }
    }
}

qui sort

Numer of rows in temp table 0
Hit any key to exit

car le deuxième appel de la procédure stockée a inséré une ligne, puis supprimé toutes les lignes de #t pendant que le premier appel attendait que le client récupère les lignes de son premier jeu de résultats. Notez que si le premier ensemble de résultats était petit, les lignes pourraient être mises en mémoire tampon et l'exécution pourrait continuer sans rien envoyer au client.

Si vous déplacez le

create table #t(id int)

dans la procédure stockée, il génère:

Numer of rows in temp table 1
Hit any key to exit

Et avec la table temporaire déclarée à l'intérieur de la procédure, si vous changez la deuxième requête en

cmd2.CommandText = "select * from #t";

Il échoue avec:

'Nom d'objet non valide' #t '.'

Parce qu'une table #temp créée à l'intérieur d'une procédure stockée ou d'un lot imbriqué n'est visible que dans cette procédure stockée ou ce lot et dans les procédures et les lots imbriqués qu'elle appelle et est détruite à la fin de la procédure ou du lot.

19

Pas simultanément. Vos options comprennent:

  • Exécutez les requêtes l'une après l'autre dans la même session
  • Passer d'une table temporaire à une table temporaire globale (utilisez ## TableName au lieu de #TableName), mais sachez que la table temporaire globale est automatiquement supprimée lorsque la session qui a créé la table temporaire se ferme, et il n'y a aucune autre session active avec une référence à elle
  • Basculez vers une vraie table utilisateur dans TempDB - vous pouvez y créer des tables, mais sachez qu'elles disparaîtront au redémarrage du serveur
  • Passer à une vraie table utilisateur dans une base de données utilisateur
12
Brent Ozar