2010-10-13 10 views
6

Necesito soltar y volver a crear una tabla, que existe para "almacenar en caché" una vista costosa. La vista puede cambiar y quiero que el mantenimiento sea lo más fácil posible, por lo que quiero que la nueva tabla refleje la última versión de la vista.Servidor SQL - Suelta y recrea una tabla - Bloquea el acceso mientras lo haces

También quiero ser capaz de evitar errores de lectura si un procedimiento intenta acceder a la tabla mientras está en el medio de ser descartada y recreada. Estoy usando una transacción, pero no estoy seguro de si eso funcionará en una tabla "descartada" por esa fracción de segundo que no existe.

Hice una prueba básica, 30 x SELECT de la vista en un bucle mientras ejecutaba la vista desplegable/recrear. Sin errores hasta ahora.

He considerado un Truncar/Eliminar con inserción, pero las columnas potencialmente cambiantes en la vista en el futuro requieren que lo mantenga lo más flexible posible, y las columnas fijas no me ayudarán con esto.

¿Alguien me puede decir si la transacción protegerá la mesa del acceso de lectura mientras se retira y esto es seguro, o si hay una forma mejor?

gota/Código Recrear:

BEGIN TRAN 

    BEGIN TRY 

     DROP TABLE Persisted_View_1 

     SELECT * INTO Persisted_View_1 

     FROM View_1 

    END TRY 
    BEGIN CATCH 

     RAISERROR('The procedure proc_PersistView1 failed to commit, the transaction was rolled back', 16, 1) 

     IF @@TRANCOUNT > 0 
     BEGIN 
      ROLLBACK TRAN 
     END 

    END CATCH 

     IF @@TRANCOUNT > 0 
     BEGIN 
      COMMIT TRAN 
     END 

    GO 

ACTUALIZACIÓN: consulta revisado siguientes clavitos Respuesta:

ALTER PROCEDURE proc_Drop_Recreate_Persisted_View_MyData 

AS 
BEGIN 

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 

    BEGIN TRAN 

     BEGIN TRY 

      -- Re create 

      SELECT * INTO Persisted_View_MyData_Temp FROM View_MyData 

      -- Create index on product ID 

      CREATE CLUSTERED INDEX [IX_ProductID_ProductTypeID] ON [dbo].[Persisted_View_MyData_Temp] 
      (
       [productID] ASC, 
       [productTypeID] ASC 
      ) 
      WITH 
      (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 

      -- Check and drop table 

      IF EXISTS (SELECT Id FROM sysObjects WHERE Name like 'Persisted_View_MyData') 
      BEGIN   
       DROP TABLE Persisted_View_MyData  
      END   

      EXEC sp_rename 'Persisted_View_MyData_Temp', 'Persisted_View_MyData' 

     END TRY 
     BEGIN CATCH 

      RAISERROR('The procedure proc_PersistViewMyData failed to commit, the transaction was rolled back', 16, 1) 

      IF @@TRANCOUNT > 0 
      BEGIN 
       ROLLBACK TRAN 
      END 

     END CATCH 

      IF @@TRANCOUNT > 0 
      BEGIN 
       COMMIT TRAN 
      END 


END 
+2

En lugar de utilizar esta tabla de almacenamiento en caché manual, ¿puede materializar la vista haciéndola una vista indexada? Entonces, SQL debería encargarse de todos estos detalles por usted. (Tenga en cuenta que ciertas construcciones en la vista podrían descartar esto, como UNIONs o GROUP BYs) – BradC

+0

@BradC, ¿puede comentar más sobre cómo hacer que una vista sea una "vista indexada"? – Brad

+0

@Brad Amplió mi comentario en una respuesta. Vea abajo. – BradC

Respuesta

8

Utilizo el proceso que he venido a copiar drop-flip- flop. He utilizado este proceso con gran éxito al crear la tabla toma algo de tiempo y no quiero que todas las aplicaciones/usuarios que necesitan la tabla se mantengan durante este proceso de reconstrucción.

  1. crear la nueva versión de la tabla con un poco de pre/post-fix (por ejemplo <TableName>_New)
  2. gota el elemento existente (es decir DROP TABLE <TableName>)
  3. Cambiar el nombre de la nueva tabla al nombre de la tabla original (EXEC sql_rename...) [REF]

Normalmente creo un procedimiento almacenado con esta lógica y lo programo en un trabajo.


* NOTA: para sacar el máximo provecho de este proceso, que necesita para crear cualquier índice que necesita en la nueva tabla entre el paso 1 y 2. Esto significa que usted tiene que pre/post-fix también y cambiarles el nombre junto con la tabla para evitar problemas cuando la secuencia de comandos se ejecuta de nuevo.

Para mayor seguridad, puede crear una transacción en torno a los pasos 2 y 3. Configurar el nivel de aislamiento en Serialized lo hará más seguro, pero no tengo experiencia si realmente previene errores. Nunca me he encontrado con ningún problema sin usar una transacción.

+0

Gracias, he revisado el procedimiento anterior. Con suerte eso es bastante a prueba de balas. – gb2d

+0

¿He configurado correctamente el nivel de aislamiento de transacción? – gb2d

+0

Creo que puede mover el 'TRAN BEGIN' (y' TRY BEGIN') hacia abajo después de crear la tabla '_Temp' y agregar lógica al principio para soltar la tabla' _Temp' si se encuentra. – Brad

0

Ajuste el nivel de aislamiento en serie. Esto significa que cuando inicia una transacción, las filas que están bloqueadas se hacen de forma tal que los demás no pueden leerlas hasta que termina el transexual

+0

¿Podría explicar el beneficio? – gb2d

3

Esto funcionará, pero como indica @Brad, podría tener un período prolongado de bloqueo mientras se reconstruye la tabla.Su crear nuevo/soltar antiguo/cambiar el nombre funcionaría bastante bien.

Otro truco que he utilizado se basa en puntos de vista:

  • tiene dos tablas permanentes, MyTable0 y MyTable1
  • almacena un indicador de algún lugar que indica qué tabla es "actual", y que está desconectado
  • tiene una vista definida sobre la mesa "actual" (tan simple como CREATE VIEW MyTable as SELECT * from MyTable0)
  • Cuando llegue el momento de actualizar, se cargan los datos en la tabla fuera de línea,
  • ru n una vista ALTER para hacer referencia a la tabla ahora-actual (como una sola declaración, está envuelto en una transacción implícita, por lo que es posible que no tenga que quejarse con operaciones declaradas),
  • y la actualización del indicador en consecuencia (Indicator = (Indicator + 1) % 2)

(una vez tuve un sistema de mesas de cuatro vías sobre la base de esta idea, para los datos anteriores, los datos actuales, los futuros datos disponibles para los clientes de suscripción, y "a la espera de la próxima carga".)

+0

@Phillip, +1 para la idea interesante ... – Brad

+0

Gracias por otra forma interesante de abordar esto. – gb2d

3

voy a ampliar mi Comente como una respuesta completa: Antes de implementar Drop-Flip-Flop, primero verificaría si puede indexar la vista:

Indexed views in SQL 2000
Indexed views in SQL 2005
Indexed views in SQL 2008

Básicamente, si la vista se encuentra con un determinado conjunto de condiciones, simplemente puede añadir un índice agrupado a la vista, y que en realidad guarda una copia física de la Vista de Resultados, que son actualizado cada vez que se modifican las tablas subyacentes.

En otras palabras, SQL está haciendo todo el trabajo por usted que ahora está haciendo manualmente.

Lamentablemente, no todas las vistas son materializables. Una vista indizada no puede contener:

  • DISTINCT
  • Tablas o subconsultas derivados
  • existe/NO EXISTE
  • UNIÓN
  • TOP
  • combinaciones externas
  • ORDER BY

y un montón de ot sus cosas que puedes leer en los enlaces de arriba.

+0

+1 para una buena solución (si es viable en circunstancias particulares) – Brad

+0

Investigué las vistas indizadas, lo que hubiera sido una solución más ordenada, pero rápidamente descubrí que las restricciones que mencionó se aplican en mi caso, por lo que las vistas indizadas no pueden ser usado. +1 – gb2d

Cuestiones relacionadas