É possível que as instruções SQL sejam executadas simultaneamente em uma única sessão no SQL Server?

16

Eu escrevi um procedimento armazenado que faz uso de uma tabela temporária. Eu sei que no SQL Server, tabelas temporárias têm escopo de sessão. No entanto, não consegui encontrar informações definitivas sobre exatamente do que uma sessão é capaz. Em particular, se for possível que esse procedimento armazenado seja executado duas vezes simultaneamente em uma única sessão, será necessário um nível de isolamento significativamente mais alto para uma transação dentro desse procedimento, pois as duas execuções agora compartilham uma tabela temporária.

Trevor Giddings
fonte

Respostas:

19

Embora a resposta de Brent esteja correta para todos os propósitos práticos, e isso não seja algo com que eu já vi alguém se preocupar, é possível que várias invocações de um procedimento armazenado em uma sessão afetem um ao outro por meio de uma tabela #temp no escopo da sessão .

A boa notícia é que é extremamente improvável que aconteça na natureza porque

1) As tabelas #Temp declaradas dentro de procedimentos armazenados ou lotes aninhados não têm realmente visibilidade da sessão (ou vida útil). E estes são de longe o caso mais comum.

2) Ela exige MultipleActiveResultSets e quer algumas muito estranhas programação do cliente assíncrona, ou para o procedimento armazenado para retornar um conjunto de resultados no meio, eo cliente para chamar outra instância do procedimento armazenado ao processar os resultados da primeira.

Aqui está um exemplo artificial:

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

quais saídas

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

porque a segunda invocação do procedimento armazenado inseriu uma linha e excluiu todas as linhas de #t enquanto a primeira invocação aguardava o cliente buscar as linhas do primeiro conjunto de resultados. Observe que, se o primeiro conjunto de resultados for pequeno, as linhas poderão ficar em buffer e a execução poderá continuar sem enviar nada ao cliente.

Se você mover o

create table #t(id int)

no procedimento armazenado, ele gera:

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

E com a tabela temporária declarada dentro do procedimento, se você alterar a segunda consulta para

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

Falha com:

'Nome de objeto inválido' #t '.'

Como uma tabela #temp criada dentro de um procedimento armazenado ou lote aninhado só é visível nesse procedimento ou lote armazenado e nos procedimentos e lotes aninhados que ele chama e é destruída quando o procedimento ou lote termina.

David Browne - Microsoft
fonte
12

Não simultaneamente. Suas opções incluem:

  • Execute as consultas uma após a outra na mesma sessão
  • Alterne de uma tabela temporária para uma tabela temporária global (use ## TableName em vez de #TableName), mas lembre-se de que a tabela temporária global é descartada automaticamente quando a sessão que criou a tabela temporária é fechada e não há outras sessões ativas com uma referência a ele
  • Alterne para uma tabela de usuário real no TempDB - você pode criar tabelas lá, mas saiba que elas desaparecerão na reinicialização do servidor
  • Alterne para uma tabela de usuário real em um banco de dados do usuário
Brent Ozar
fonte