¿Cuánto tiempo persistirá una tabla de MEMORIA temporal si no la dejo caer (MySQL)

13

Estoy usando un procedimiento almacenado recursivo en MySQL para generar una tabla temporal llamada id_list, pero debo usar los resultados de ese procedimiento en una consulta de selección de seguimiento, por lo que no puedo DROPla tabla temporal dentro del procedimiento ...

BEGIN;

/* generates the temporary table of ID's */
CALL fetch_inheritance_groups('abc123',0);

/* uses the results of the stored procedure in the WHERE */
SELECT a.User_ID
FROM usr_relationships r 
INNER JOIN usr_accts a ON a.User_ID = r.User_ID 
WHERE r.Group_ID = 'abc123' OR r.Group_ID IN (SELECT * FROM id_list) 
GROUP BY r.User_ID;

COMMIT;

Al llamar al procedimiento, el primer valor es la ID superior de la rama que quiero, y el segundo es el tierque utiliza el procedimiento durante las recursiones. Antes del bucle recursivo, comprueba si tier = 0se ejecuta y si es así:

DROP TEMPORARY TABLE IF EXISTS id_list;
CREATE TEMPORARY TABLE IF NOT EXISTS id_list (iid CHAR(32) NOT NULL) ENGINE=memory;

Entonces mi pregunta es: si no hago DROPla MEMORYtabla temporal al final del procedimiento, o dentro de mi transacción, ¿cuánto tiempo durará esa tabla en la memoria? ¿Se descarta automáticamente una vez que finaliza la sesión, o permanecerá en la memoria mientras la conexión esté abierta?

** Nota: la respuesta obvia podría ser abandonar la tabla temporal antes de la declaración de confirmación, pero supongamos por un momento que no puedo hacer eso. *


EDITAR : Para ser un poco más precisos, ¿qué pasa si se emplean conexiones persistentes, la tabla persistirá a través de múltiples solicitudes? Hasta ahora parece que lo hará y que tendríamos que eliminar explícitamente la tabla temporal para liberar ese recurso.


ACTUALIZACIÓN : Basado en el consejo de los comentaristas, he encontrado una forma de ajustar mi procedimiento almacenado para que pueda utilizar la tabla TEMP MEMORY, pero poder explícitamente DROPal final ...

En lugar de simplemente llamar al procedimiento almacenado y usar la tabla TEMP restante para recopilar los resultados en la consulta real, he cambiado el CALLformato para usar una tercera OUTvariable de esta manera:

CALL fetch_inheritance_groups('abc123','0',@IDS);

... luego, dentro del procedimiento almacenado, agregué un segundo IF tier = 0al final con lo siguiente:

IF tier = 0
    THEN
    SELECT GROUP_CONCAT(DISTINCT iid SEPARATOR ',') FROM id_list INTO inherited_set;
    DROP TEMPORARY TABLE IF EXISTS id_list;
END IF;

Por lo tanto, el resultado del procedimiento almacenado ahora es una lista separada por comas de ID que es compatible y FIND_IN_SET, por lo tanto, la consulta final se modificó para que:

WHERE r.Group_ID = 'abc123' OR r.Group_ID IN (SELECT * FROM id_list)

... es ahora ...

WHERE r.Group_ID = 'abc123' OR FIND_IN_SET(r.Group_ID,@IDS)

Voila! Gracias a los comentaristas por su aporte y por darme la razón por la que necesitaba esforzarme un poco más :)

oucil
fuente

Respuestas:

17

Lo divertido de las tablas temporales en un procedimiento almacenado no es tanto la existencia transitoria de la tabla (que se cae al finalizar la conexión de la base de datos), sino el alcance del procedimiento almacenado.

Alguien hizo esta pregunta en StackOverflow: Alcance de las tablas temporales creadas en el procedimiento almacenado MySQL . ¿Ha pasado más de un año y nadie respondió la pregunta? Déjame aclarar las cosas. El hecho es: la tabla temporal existe dentro y fuera del procedimiento almacenado, pero puede hacer cosas con la tabla temporal solo dentro del alcance de un procedimiento almacenado en ejecución .

De acuerdo con el libro

kdsjx

El capítulo 5 tiene un subtítulo que devuelve conjuntos de resultados a otro procedimiento almacenado .

Dice en el párrafo 2 de la página 117:

Desafortunadamente, la única forma de pasar un conjunto de resultados de un procedimiento almacenado a otro es pasar los resultados a través de una tabla temporal. Esta es una solución incómoda b, y, debido a que la tabla temporal tiene un alcance durante toda la sesión, crea muchos de los mismos problemas de mantenibilidad planteados por el uso de variables globales. pero si un programa almacenado necesita suministrar resultados a otro programa almacenado, entonces una tabla temporal puede ser la mejor solución.

Mirando hacia atrás a la pregunta de StackOverflow , puedo ver a alguien llamado Procedimiento almacenado desde el cliente mysql. Dado que el cliente mysql no es un Procedimiento almacenado, los resultados no pueden manipularse a nivel del cliente mysql a través de DML que no sea hacer una SELECCIÓN para ver los resultados. Como llama a un procedimiento almacenado recursivo, puede estar seguro de que la tabla temporal es totalmente accesible durante la conexión DB .

Espero que esto responda tu pregunta.

ACTUALIZACIÓN 2014-01-31 11:26 EST

En tu último comentario, dijiste

Si empleamos conexiones persistentes, la tabla de MEMORIA persistirá a través de MÚLTIPLES SOLICITUDES, y parece que lo hará, así que, por el bien del rendimiento, supongo que el uso de este método * REQUERIRÁ que DEJEMOS explícitamente la tabla de MEMORIA temporal. ¿Asumo correctamente?

Sí y no. Digo sí porque es una forma de hacerlo. Yo digo que no porque otra forma de hacerlo es:

CREATE TEMPORARY TABLE IF NOT EXISTS id_list (iid CHAR(32) NOT NULL) ENGINE=memory;
TRUNCATE TABLE id_list;

De cualquier forma que elija, la operación sigue siendo la misma ya que TRUNCATE TABLE cae y vuelve a crear la tabla. Esto no dañará otras conexiones de DB ya que cada conexión tiene su propia tabla id_list.

RolandoMySQLDBA
fuente
Muy apreciado Rolando! Publiqué la misma pregunta en SO ( stackoverflow.com/questions/21483448/… ) solo en caso de que tuviera más ojos en él y obtuviera respuestas similares, aunque menos informativas. Planteé un seguimiento: si empleamos conexiones persistentes, la tabla de MEMORIA persistirá a través de MÚLTIPLES SOLICITUDES, y parece que lo hará, así que, por el bien del rendimiento, supongo que usar este método * REQUERIRÁ explícitamente DROPla MEMORIA temporal mesa. ¿Asumo correctamente?
oucil
Con respecto a su ACTUALIZACIÓN, supongo que me preocupa más dejar un recurso en su lugar que ya no sea necesario hasta que esa consulta se ejecute nuevamente, y creo que es cada vez más obvio que debería eliminarlo explícitamente independientemente de si no lo hago ' No es necesario.
oucil
" Desafortunadamente, la única forma de pasar un conjunto de resultados de un procedimiento almacenado a otro es pasar los resultados a través de una tabla temporal " . ¿Significa esto que podemos acceder al conjunto de resultados (de la persona que llama) solo cuando conocemos el nombre de la tabla temporal que se creó en el procedimiento llamado? ¿No es una forma de leer el conjunto de resultados como la forma en que podemos usar para leer el conjunto de resultados de una SELECTdeclaración en procedimientos almacenados ( DECLARE aCursor CURSOR FOR SELECT ...)? P.ej. DECLARE theCursor CURSOR FOR CALL aProcedure()?
Mir-Ismaili
2

En la mayoría de los DBMS, las tablas temporales sobreviven hasta el final de la conexión actual, a menos que se especifique lo contrario o que exista una reversión de transacción explícita (en algunos sistemas, una reversión solo puede afectar el contenido de la tabla, dejando el objeto en sí mismo para ser repoblado si es necesario) . La tabla no (por defecto) será visible para otras conexiones, sin importar cuánto tiempo dure la conexión que la crea.

Un escaneo rápido en Google parece indicar que así es como opera mySQL.
( http://www.tutorialspoint.com/mysql/mysql-temporary-tables.htm establece "de forma predeterminada, MySQL elimina todas las tablas temporales cuando finaliza la conexión de la base de datos. De forma predeterminada, MySQL elimina todas las tablas temporales cuando su conexión de base de datos se termina ")

Sin embargo, a menudo hay formas de alterar estos comportamientos. Por ejemplo, en MS SQL Server puede crear una tabla temporal que sea visible para todas las conexiones en lugar de solo la actual dándole un nombre que comience ##.

Siempre elimino las tablas temporales tan pronto como ya no son necesarias para evitar posibles confusiones. Me han mordido antes donde la agrupación de conexiones dio como resultado la creación de una tabla temporal que causa errores porque se creó una tabla temporal del mismo nombre pero no se destruyó en una acción anterior que utilizó la conexión actual.

David Spillett
fuente
Estoy de acuerdo en que debería encontrar una manera de descartar explícitamente la tabla, pero eludí el problema con el que terminaste usando DROPantes de recrear dentro del IF del nivel inicial. ¡Gracias por tu contribución!
oucil
-2
CREATE TEMPORARY TABLE  IF NOT EXISTS temp (Months VARCHAR(50),Sequence INT)
AS (
SELECT 
CONCAT(MONTHNAME(m1),' ',YEAR(m1)) AS Months,CONVERT(m1,DATE) AS Sequence
FROM
(
SELECT 
('2014-01-01' - INTERVAL DAYOFMONTH('2014-01-01')-1 DAY) 
+INTERVAL m MONTH AS m1
FROM
(
SELECT @rownum:=@rownum+1 AS m FROM
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t1,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t2,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t3,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t4,
(SELECT @rownum:=-1) t0
) d1
) d2 
WHERE m1<= '2015-07-30'
ORDER BY m1
) ;

SELECT t.Months,A.OpenCount,A.CloseCount FROM Temp T
 LEFT JOIN ( SELECT  CONCAT(MONTHNAME(e.dtcdate),' ',YEAR(e.dtcdate)) AS Months,
 ( SELECT  COUNT(csr.ddlcsstatus) FROM csrcrn_frmempengagreqs csr 
 JOIN master_detail md ON md.masterDetailId=csr.ddlcsstatus WHERE md.abbr='open' AND csr.dtcdate >='2014-01-01' AND csr.dtcdate <='2015-07-30' AND csr.ddlArea=e.ddlArea ) AS OpenCount
 ,
 ( SELECT  COUNT(csr.ddlcsstatus) FROM csrcrn_frmempengagreqs csr 
 JOIN master_detail md ON md.masterDetailId=csr.ddlcsstatus WHERE md.abbr='Close' AND csr.dtcdate >='2014-01-01' AND csr.dtcdate <='2015-07-30' AND csr.ddlArea=e.ddlArea ) AS CloseCount

 FROM csrcrn_frmempengagreqs e 
 INNER JOIN master_detail m ON e.ddlcsstatus=m.masterDetailId 
  WHERE  e.dtcdate >='2014-01-01' AND e.dtcdate <='2015-07-30' 
 GROUP BY MONTH(e.dtcdate) ORDER BY e.dtcdate 
 ) A ON CONVERT(A.Months,CHAR(20))=CONVERT(T.Months,CHAR(20)) 
       ORDER BY T.Sequence; 
       DROP TEMPORARY TABLE  IF EXISTS temp;

/ * la consulta dada da un resultado exitoso ... cuando coloca esta consulta en USP y luego muestra un error, por favor ayúdame ... el proceso se proporciona a continuación * /

DELIMITER $$

DROP PROCEDURE IF EXISTS `usp_GetEngMonthlyChart_Test`$$

CREATE DEFINER=`root`@`%` PROCEDURE `usp_GetEngMonthlyChart_Test`(IN DateFrom DATE,IN DateTo DATE)
BEGIN
      -- SET @strWhere= CONCAT(' AND CSR.dtcInductionDate BETWEEN ''',CONVERT(DateFrom,DATE),''' AND ','''',CONVERT(DateTo,DATE),''''); 


    SET @strSql=CONCAT(' 

    CREATE TEMPORARY TABLE  IF NOT EXISTS temp (Months VARCHAR(50),Sequence INT)
    AS (
    SELECT 
    CONCAT(MONTHNAME(m1),'' '',YEAR(m1)) AS Months,CONVERT(m1,DATE) AS Sequence
    FROM
    (
    SELECT 
    (''',DateFrom,''' - INTERVAL DAYOFMONTH(''',DateFrom,''')-1 DAY) 
    +INTERVAL m MONTH AS m1
    FROM
    (
    SELECT @rownum:=@rownum+1 AS m FROM
    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t1,
    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t2,
    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t3,
    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t4,
    (SELECT @rownum:=-1) t0
    ) d1
    ) d2 
    WHERE m1<= ''',DateTo,'''
    ORDER BY m1
    )' );   

         SET @strSql=CONCAT(@strSql,'; GO SELECT t.Months,A.OpenCount,A.CloseCount FROM Temp T
     LEFT JOIN ( SELECT  CONCAT(MONTHNAME(e.dtcdate),'' '',YEAR(e.dtcdate)) AS Months,
     ( SELECT  COUNT(csr.ddlcsstatus) FROM csrcrn_frmempengagreqs csr 
     JOIN master_detail md ON md.masterDetailId=csr.ddlcsstatus WHERE md.abbr=''open'' AND csr.dtcdate >=''',DateFrom,
     ''' AND csr.dtcdate <=''',DateTo,''' AND csr.ddlArea=e.ddlArea ) AS OpenCount
     ,
     ( SELECT  COUNT(csr.ddlcsstatus) FROM csrcrn_frmempengagreqs csr 
     JOIN master_detail md ON md.masterDetailId=csr.ddlcsstatus WHERE md.abbr=''Close'' AND csr.dtcdate >=''',DateFrom,
     ''' AND csr.dtcdate <=''',DateTo,''' AND csr.ddlArea=e.ddlArea ) AS CloseCount

     FROM csrcrn_frmempengagreqs e 
     INNER JOIN master_detail m ON e.ddlcsstatus=m.masterDetailId 
      WHERE  e.dtcdate >=''',DateFrom,''' AND e.dtcdate <=''',DateTo,''' 
     GROUP BY MONTH(e.dtcdate) ORDER BY e.dtcdate 
     ) A ON CONVERT(A.Months,CHAR(20))=CONVERT(T.Months,CHAR(20)) 
           ORDER BY T.Sequence; 
           DROP TEMPORARY TABLE  IF EXISTS temp;'); 

    SELECT @strSql;
    PREPARE stmt FROM @strSql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    END$$

DELIMITER ;

LLAME a usp_GetEngMonthlyChart_Test ('2014-01-01', '2015-07-30')

Ashutosh
fuente
2
Solo publicar código no es lo suficientemente bueno. Esto necesita una explicación
James Anderson