Возможно ли, чтобы операторы SQL выполнялись одновременно в течение одного сеанса в SQL Server?

16

Я написал хранимую процедуру, которая использует временную таблицу. Я знаю, что в SQL Server временные таблицы имеют сессионную область. Однако я не смог найти точную информацию о том, на что способен сеанс. В частности, если возможно, чтобы эта хранимая процедура выполнялась дважды одновременно в одном сеансе, для транзакции в рамках этой процедуры требуется значительно более высокий уровень изоляции, поскольку два выполнения теперь совместно используют временную таблицу.

Тревор Гиддингс
источник

Ответы:

19

В то время как ответ Brent является правильным для всех практических целей, и это не то , что я когда - либо видел кто - то беспокоиться о том , что это возможно для нескольких вызовов хранимой процедуры в сессии влияют друг на друга через контекст сеанса #temp таблицы ,

Хорошей новостью является то, что в дикой природе это крайне маловероятно, потому что

1) Таблицы #Temp, объявленные внутри хранимых процедур или вложенных пакетов, на самом деле не имеют видимости сеанса (или времени жизни). И это, безусловно, самый распространенный случай.

2) Требуется MultipleActiveResultsets и либо очень странное асинхронное программирование клиента, либо для хранимой процедуры, которая возвращает набор результатов в середине, и клиент для вызова другого экземпляра хранимой процедуры при обработке результатов из первой.

Вот надуманный пример:

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

какие выводы

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

потому что второй вызов хранимой процедуры вставил строку, а затем удалил все строки из #t, пока первый вызов ждал, пока клиент извлечет строки из своего первого набора результатов. Обратите внимание, что если первый набор результатов будет маленьким, строки могут быть помещены в буфер, и выполнение может продолжаться без отправки чего-либо клиенту.

Если вы переместите

create table #t(id int)

в хранимую процедуру выводит:

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

И с временной таблицей, объявленной внутри процедуры, если вы измените второй запрос на

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

Это терпит неудачу с:

'Неверное имя объекта' #t '.'

Потому что таблица #temp, созданная внутри хранимой процедуры или вложенного пакета, видна только в той хранимой процедуре или пакете и во вложенных процедурах и пакетах, которые она вызывает, и уничтожается, когда процедура или пакет заканчиваются.

Дэвид Браун - Microsoft
источник
12

Не одновременно. Ваши варианты включают в себя:

  • Запускайте запросы один за другим в одном сеансе
  • Переключитесь с временной таблицы на глобальную временную таблицу (используйте ## TableName вместо #TableName), но имейте в виду, что глобальная временная таблица автоматически удаляется, когда закрывается сеанс, создавший временную таблицу, и нет никаких других активных сеансов с ссылка на это
  • Переключитесь на реальную пользовательскую таблицу в TempDB - вы можете создавать таблицы там, но имейте в виду, что они исчезнут при перезапуске сервера
  • Переключиться на реальную таблицу пользователей в базе данных пользователей
Брент Озар
источник