Können SQL-Anweisungen gleichzeitig in einer einzelnen Sitzung in SQL Server ausgeführt werden?

16

Ich habe eine gespeicherte Prozedur geschrieben, die eine temporäre Tabelle verwendet. Ich weiß, dass temporäre Tabellen in SQL Server einen Sitzungsbereich haben. Es ist mir jedoch nicht gelungen, endgültige Informationen darüber zu finden, wozu eine Sitzung in der Lage ist. Insbesondere wenn es möglich ist, dass diese gespeicherte Prozedur zweimal gleichzeitig in einer einzelnen Sitzung ausgeführt wird, ist für eine Transaktion innerhalb dieser Prozedur eine erheblich höhere Isolationsstufe erforderlich, da sich jetzt zwei Ausführungen eine temporäre Tabelle teilen.

Trevor Giddings
quelle

Antworten:

19

Obwohl Brents Antwort für alle praktischen Zwecke richtig ist und ich noch nie jemanden gesehen habe, der sich Sorgen macht, können sich mehrere Aufrufe einer gespeicherten Prozedur in einer Sitzung über eine sitzungsbezogene #temp-Tabelle gegenseitig beeinflussen .

Die gute Nachricht ist, dass es extrem unwahrscheinlich ist, dass es in der Wildnis passiert, weil

1) #Temp-Tabellen, die in einer gespeicherten Prozedur oder in verschachtelten Stapeln deklariert sind, haben keine tatsächliche Sichtbarkeit der Sitzung (oder Lebensdauer). Und dies ist bei weitem der häufigste Fall.

2) Es erfordert MultipleActiveResultsets und entweder eine sehr seltsame asynchrone Clientprogrammierung oder dass die gespeicherte Prozedur eine Ergebnismenge in der Mitte zurückgibt und der Client eine andere Instanz der gespeicherten Prozedur aufruft, während die Ergebnisse der ersten verarbeitet werden.

Hier ist ein ausgedachtes Beispiel:

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();
        }
    }
}

welche Ausgänge

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

da der zweite Aufruf der gespeicherten Prozedur eine Zeile einfügte und dann alle Zeilen aus #t löschte, während der erste Aufruf darauf wartete, dass der Client die Zeilen aus seiner ersten Ergebnismenge abrief. Beachten Sie, dass bei einer kleinen ersten Ergebnismenge die Zeilen möglicherweise gepuffert werden und die Ausführung fortgesetzt werden kann, ohne dass etwas an den Client gesendet wird.

Wenn Sie die bewegen

create table #t(id int)

in die gespeicherte Prozedur gibt es aus:

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

Und mit der in der Prozedur deklarierten temporären Tabelle , wenn Sie die zweite Abfrage in ändern

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

Es schlägt fehl mit:

'Ungültiger Objektname' #t '.'

Denn eine # temporäre Tabelle, die in einer gespeicherten Prozedur oder einem verschachtelten Stapel erstellt wurde, ist nur in dieser gespeicherten Prozedur oder diesem Stapel sowie in den von ihr aufgerufenen verschachtelten Prozeduren und Stapeln sichtbar und wird zerstört, wenn die Prozedur oder der Stapel endet.

David Browne - Microsoft
quelle
12

Nicht gleichzeitig. Ihre Optionen umfassen:

  • Führen Sie die Abfragen nacheinander in derselben Sitzung aus
  • Wechseln Sie von einer temporären Tabelle zu einer globalen temporären Tabelle (verwenden Sie ## TableName anstelle von #TableName). Beachten Sie jedoch, dass die globale temporäre Tabelle automatisch gelöscht wird, wenn die Sitzung, die die temporäre Tabelle erstellt hat, geschlossen wird und keine weiteren aktiven Sitzungen mit vorhanden sind ein Verweis darauf
  • Wechseln Sie in TempDB zu einer echten Benutzertabelle. Sie können dort Tabellen erstellen. Beachten Sie jedoch, dass diese beim Neustart des Servers verschwinden
  • Wechseln Sie zu einer realen Benutzertabelle in einer Benutzerdatenbank
Brent Ozar
quelle