Совместное использование временной таблицы SQL различными средствами чтения SQL
-
23-08-2019 - |
Вопрос
Я пытаюсь выполнить много различных запросов к результирующему набору, который имеет очень большое время создания.Чтобы получить прирост производительности, я хочу использовать временную таблицу и просто выполнять множество запросов к этой временной таблице.
Кажется довольно стандартным.Тем не менее, я изо всех сил пытаюсь поделиться этой временной таблицей в динамическом SQL.Насколько я понимаю, каждый объект SqlCommand выполняется в своем собственном потоке, и поэтому временная таблица находится в другой области видимости, что делает ее недоступной из потока запросов.
Я пробовал использовать глобальную временную таблицу, и это отлично работает, но не идеально?
Как я могу совместно использовать локальную временную таблицу между динамическими SQL-запросами?
Мое намерение:
using (var conn = new SqlClient.SqlConnection("..."))
{
// Creation involes many table joins in reality
String creationScript = "SELECT * FROM FooTable INTO #MyTemp";
SqlCommand createTempTbl = new SqlCommand(creationScript, conn);
createTempTbl.ExecuteNonQuery();
String query1 = "SELECT * FROM #MyTemp where id=@id";
SqlCommand query1Comm = new SqlCommand(query1, conn);
query1Comm.Parameters.Add("@id", ...);
String query2 = "SELECT * FROM #MyTemp where name=@name";
SqlCommand query2Comm = new SqlCommand(query2, conn);
query2Comm.Parameters.Add("@name", ...);
// And so on the queries go
} // Now want #MyTemp to be destroyed
Решение
Вы могли бы попробовать использовать глобальную временную таблицу (т. Е. Использовать ##MyTemp
вместо того , чтобы #MyTemp
в ваших запросах), с этим предостережением:
Глобальные временные таблицы автоматически удаляются при завершении сеанса , который создал таблицу, и все другие задачи перестали ссылаться на них.Связь между задачей и таблицей поддерживается только в течение срока службы одного оператора Transact-SQL .Это означает, что глобальная временная таблица удаляется при завершении последнего оператора Transact-SQL , который активно ссылался на таблицу, когда закончился сеанс создания. Это означает, что глобальная временная таблица удаляется при завершении последнего оператора Transact-SQL, который активно ссылался на таблицу, когда закончился сеанс создания.
Редактировать: Упс, пропустил тот факт, что вы уже пробовали глобальные временные таблицы.
Как насчет переноса всей вашей логики в единую хранимую процедуру, которая создает / заполняет временную таблицу, а затем выполняет запросы и возвращает несколько наборов результатов в клиентский код?
Другие советы
Я знаю, что прошло много времени с тех пор, как это сообщение было опубликовано, но ответ, я полагаю, довольно прост.
Я предполагаю, что вы используете библиотеку MS Enterprise для доступа к базе данных, это объясняет, почему временная таблица не существует между командами.Корпоративная библиотека ЯВНО закрывает соединение с базой данных (помещает его обратно в пул) после завершения выполнения команды.То есть, ЕСЛИ ТОЛЬКО вы не вставите команды в транзакцию.Если вы используете ADO.NET напрямую (открывая соединение, создавая и выполняя команды, затем закрывая соединение), вы не сталкиваетесь с этой проблемой (это зависит от вас, когда соединение закрывается, что более рискованно).Вот некоторый код, написанный с использованием библиотеки MS Enterprise и транзакции (извините, VB.NET):
' Get a reference to the database
Dim sqlNET As New Sql.SqlDatabase("*Your Connection String Here...*")
' Specify the transaction options
Dim oTranOpt As TransactionOptions = New TransactionOptions
' What type of isolation the transaction should have (V. Important):
oTranOpt.IsolationLevel = IsolationLevel.ReadUncommitted ' This one doesn't place locks on DB but allows dirty reads
' How long the transaction has to complete before implicitly failing (h,m,s):
oTranOpt.Timeout = New TimeSpan(0, 0, 30)
' Start the scope of the transation
Using oTranScope As TransactionScope = New TransactionScope(TransactionScopeOption.Required, oTranOpt)
' Create the connection to the DB. Not abs. necessary. EL will create one but best to do so.
Using Conn As Common.DbConnection = sqlNET.CreateConnection
' Create a Temp Table
sqlNET.ExecuteNonQuery(CommandType.Text, "SELECT * INTO #MyTemp FROM FooTable")
' Get results from table, e.g.
Dim intCount As Integer = sqlNET.ExecuteScalar(CommandType.Text, "Select Count(*) from #MyTemp")
MsgBox(intCount)
' Flag transaction as successful (causes a commit when scope is disposed)
oTranScope.Complete()
End Using ' Disposes the connection
End Using ' If this point is reached without hitting the oTranScope.Complete - the transaction is rolled back and locks, if any, are released.
Если бы вы удалили область транзакции, код потерпел бы неудачу при подсчете выбора (*), поскольку таблица больше не существует.Указание области сохраняет соединение открытым между вызовами команд.
Я надеюсь, что это кому-то поможет.
Нил.
Чего не хватает в вашем вопросе, так это жизненного цикла созданной таблицы.Если она останется у вас на некоторое время, то это не совсем временная таблица, это рабочая таблица, которую вы заполняете и используете.Я бы вообще не использовал временную таблицу, просто обычную таблицу, которая создается SELECT INTO и используется всеми остальными до тех пор, пока она не будет удалена (если вообще когда-либо).
Альтернативой, которую я успешно использовал, является создание рабочей таблицы в базе данных TempDb и использование ее как глобальной временной таблицы (например, "TempDb.dbo.MyTable").Помните, что пользовательские таблицы удаляются при перезагрузке SQL Server.