Tabla temporal de SQL compartida entre diferentes lectores de SQL
-
23-08-2019 - |
Pregunta
Estoy intentando hacer muchas consultas diferentes en un conjunto de resultados que tiene un muy gran tiempo de creación.Para obtener ganancias de rendimiento, deseo usar una tabla temporal y simplemente hacer muchas consultas en esta tabla temporal.
Parece bastante estándar.Sin embargo, estoy luchando por compartir esta tabla temporal en SQL dinámico.Según tengo entendido, cada objeto SqlCommand se ejecuta en su propio subproceso, por lo que la tabla temporal tiene un alcance diferente, lo que la hace inaccesible desde el subproceso de consulta.
Intenté usar una tabla temporal global y funciona muy bien, ¿pero no es ideal?
¿Cómo puedo compartir una tabla temporal local entre consultas SQL dinámicas?
Mi intención:
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
Solución
Se podría tratar de usar una tabla temporal global (es decir, el uso ##MyTemp
en lugar de #MyTemp
en sus consultas), con esta advertencia :
Las tablas temporales globales son caído de forma automática cuando la sesión que creó los extremos de mesa y todos otras tareas han dejado de referencia ellos. La asociación entre una tarea y una mesa se mantiene sólo para el vida de un solo Transact-SQL declaración. Esto significa que un mundial tabla temporal se deja caer en el finalización de la última Transact-SQL declaración que fue activamente referencia a la tabla cuando el Creando terminó la sesión.
EDIT:. Vaya, se perdió el hecho de que ya haya probado tablas temporales globales
¿Qué hay de mover toda la lógica en un solo procedimiento almacenado que crea / rellena la tabla temporal y luego ejecuta las consultas y devuelve varios conjuntos de resultados para el código de cliente?
Otros consejos
Sé que es un tiempo desde que éste se publicó, pero la respuesta, creo, es bastante simple.
Conjeturo que está utilizando la Biblioteca MS Enterprise para acceder a la base de datos, esto explica por qué la tabla temporal no existe entre los comandos. El Enterprise Library cierra explícitamente la conexión a la base de datos (lo pone de nuevo en la piscina) cuando termina el comando. Es decir, a menos que poner los comandos en una transacción. Si utiliza ADO.NET directamente (mediante la apertura de la conexión, la construcción y ejecución de los comandos, a continuación, cerrar la conexión) no obtiene este problema (que depende de usted cuando se cierra la conexión - que es más arriesgado). Aquí hay un código escrito usando el Enterprise Library MS y una transacción (lo siento, 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.
Si se va a sacar el ámbito de transacción, el código podría fallar en la Selección de la cuenta (*) como la tabla ya no existe. Especificando el alcance mantiene la conexión abierta entre llamadas de comandos.
Espero que esto ayude a alguien.
Neil.
Lo que falta en su pregunta es el ciclo de vida de la tabla creada. Si va a tener que quedarse allí por un tiempo, entonces no es absolutamente una tabla temporal, se trata de una mesa de trabajo que se puede rellenar y utilizar. Yo no usaría una tabla temporal en absoluto, sólo una mesa normal que se crea por el SELECT INTO y usada por todos los demás hasta que se cae (o nunca).
Una alternativa que he utilizado con éxito es la creación de una mesa de trabajo en tempdb, y utilizarlo como si se trata de una tabla temporal global (por ejemplo, "TempDb.dbo.MyTable"). Recuerde que el usuario tablas se eliminan cuando se reinicia SQL Server.