Czy instrukcje SQL mogą być wykonywane jednocześnie w ramach jednej sesji w programie SQL Server?

16

Napisałem procedurę składowaną, która korzysta z tabeli tymczasowej. Wiem, że w SQL Server tabele tymczasowe mają zasięg sesji. Nie udało mi się jednak znaleźć ostatecznych informacji na temat tego, do czego zdolna jest sesja. W szczególności, jeśli możliwe jest wykonanie tej procedury składowanej dwa razy jednocześnie w jednej sesji, wymagany jest znacznie wyższy poziom izolacji dla transakcji w ramach tej procedury ze względu na to, że dwie wykonania współużytkują teraz tabelę tymczasową.

Trevor Giddings
źródło

Odpowiedzi:

19

Podczas gdy odpowiedź Brenta jest poprawna dla wszystkich praktycznych celów i nie jest to coś, o czym ktoś jeszcze się martwi, możliwe jest , że wielokrotne wywołanie procedury przechowywanej w sesji wpływa na siebie nawzajem poprzez tabelę #temp o zasięgu sesji .

Dobra wiadomość jest taka, że ​​jest bardzo mało prawdopodobne, aby zdarzyło się to na wolności, ponieważ

1) Tabele #Temp zadeklarowane w procedurach przechowywanych lub zagnieżdżonych partiach w rzeczywistości nie mają widoczności sesji (ani czasu życia). I są to zdecydowanie najczęstsze przypadki.

2) Wymaga MultipleActiveResultset i albo jakiegoś bardzo dziwnego programowania klienta asynchronicznego, albo procedury składowanej, aby zwrócić zestaw wyników w środku, a klient wywołuje inną instancję procedury składowanej podczas przetwarzania wyników od pierwszego.

Oto wymyślony przykład:

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

które wyjścia

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

ponieważ drugie wywołanie procedury składowanej wstawiło wiersz, a następnie usunęło wszystkie wiersze z #t, podczas gdy pierwsze wywołanie czekało na pobranie przez klienta wierszy z pierwszego zestawu wyników. Zauważ, że jeśli pierwszy zestaw wyników był mały, wiersze mogą zostać buforowane, a wykonywanie może być kontynuowane bez wysyłania czegokolwiek do klienta.

Jeśli przeniesiesz

create table #t(id int)

do procedury przechowywanej wyprowadza:

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

A z tabelą temp zadeklarowaną w ramach procedury, jeśli zmienisz drugie zapytanie na

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

Nie działa z:

„Niepoprawna nazwa obiektu„ #t ”.”

Ponieważ tabela #temp utworzona w ramach procedury składowanej lub partii zagnieżdżonej jest widoczna tylko w tej procedurze składowanej lub partii oraz w procedurach zagnieżdżonych i partiach, które wywołuje, i jest niszczona po zakończeniu procedury lub partii.

David Browne - Microsoft
źródło
12

Nie jednocześnie. Twoje opcje obejmują:

  • Uruchom zapytania jeden po drugim w tej samej sesji
  • Przełącz się z tabeli tymczasowej do globalnej tabeli tymczasowej (użyj ## TableName zamiast #TableName), ale pamiętaj, że globalna tabela tymczasowa jest automatycznie usuwana po zamknięciu sesji, która utworzyła tabelę tymczasową, i nie ma innych aktywnych sesji z odniesienie do tego
  • Przełącz się na prawdziwą tabelę użytkowników w TempDB - możesz tam tworzyć tabele, ale pamiętaj, że znikną one po ponownym uruchomieniu serwera
  • Przejdź do prawdziwej tabeli użytkowników w bazie danych użytkowników
Brent Ozar
źródło