Combien de temps une table MEMORY temporaire persistera si je ne la laisse pas tomber (MySQL)

13

J'utilise une procédure stockée récursive dans MySQL pour générer une table temporaire appelée id_list, mais je dois utiliser les résultats de cette procédure dans une requête de sélection de suivi, donc je ne peux pas DROPla table temporaire dans la procédure ...

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;

Lors de l'appel de la procédure, la première valeur est l'ID supérieur de la branche que je veux, et la seconde est celle tierque la procédure utilise lors des récursions. Avant la boucle récursive, il vérifie si tier = 0et si elle est exécutée:

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

Ma question est donc la suivante: si je n'utilise pas DROPla MEMORYtable temporaire à la fin de la procédure ou dans ma transaction, combien de temps cette table persistera-t-elle en mémoire? Est-il automatiquement supprimé une fois la session terminée ou restera-t-il en mémoire tant que la connexion sera ouverte?

** NB La réponse évidente pourrait être de supprimer la table temporaire avant l'instruction commit, mais supposons un instant que je ne peux pas faire ça. *


EDIT : Pour être un peu plus précis, que se passe-t-il si des connexions persistantes sont utilisées, la table persistera-t-elle à travers plusieurs requêtes? Jusqu'à présent, il semble que ce sera le cas et que nous devions supprimer explicitement la table temporaire pour libérer cette ressource.


MISE À JOUR : Sur la base des conseils des commentateurs, j'ai trouvé un moyen d'ajuster ma procédure stockée afin que je puisse utiliser la table TEMP MEMORY, mais pouvoir l'expliciter DROPà la fin ...

Plutôt que d'appeler simplement la procédure stockée et d'utiliser la table TEMP restante pour rassembler les résultats dans la requête réelle, j'ai changé le CALLformat pour utiliser une troisième OUTvariable comme ceci:

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

... puis dans la procédure stockée, j'ai ajouté une seconde IF tier = 0à la fin avec ce qui suit:

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;

Ainsi, le résultat de la procédure stockée est maintenant une liste d'ID séparés par des virgules qui est compatible avec FIND_IN_SET, et donc la requête finale a été modifiée de sorte que:

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

... est maintenant ...

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

Voila! Merci aux commentateurs pour votre contribution et pour m'avoir donné la raison pour laquelle je devais essayer un peu plus fort :)

oucil
la source

Réponses:

17

Ce qui est drôle à propos des tables temporaires dans une procédure stockée, ce n'est pas tant l'existence transitoire de la table (qui est supprimée à la fin de la connexion DB), mais la portée de la procédure stockée.

Quelqu'un a posé cette question sur StackOverflow: Portée des tables temporaires créées dans la procédure stockée MySQL . Cela fait plus d'un an et personne n'a répondu à la question? Permettez-moi de remettre les pendules à l'heure. Le fait est que la table temporaire existe à l'intérieur et à l'extérieur de la procédure stockée, mais vous ne pouvez faire des choses avec la table temporaire qu'à l'intérieur de la portée d'une procédure stockée en cours d'exécution .

Selon le livre

kdsjx

Le chapitre 5 comporte une sous-rubrique Renvoyer les jeux de résultats à une autre procédure stockée .

Il est dit au paragraphe 2 de la page 117:

Malheureusement, la seule façon de passer un jeu de résultats d'une procédure stockée à une autre est de passer les résultats via une table temporaire. Il s'agit d'une solution maladroite b, et - parce que la table temporaire a une portée tout au long de la session - elle crée bon nombre des mêmes problèmes de maintenabilité soulevés par l'utilisation de variables globales. mais si un programme stocké doit fournir un autre programme stocké avec des résultats, alors une table temporaire peut être la meilleure solution.

En repensant à la question StackOverflow , je peux voir quelqu'un appelé la procédure stockée à partir du client mysql. Étant donné que le client mysql n'est pas une procédure stockée, les résultats ne peuvent pas être manipulés au niveau du client mysql via DML autrement que de faire un SELECT pour voir les résultats. Puisque vous appelez une procédure stockée récursive, vous pouvez être assuré que la table temporaire est entièrement accessible pendant la durée de la connexion DB .

J'espère que cela répond à votre question.

MISE À JOUR 2014-01-31 11:26 EST

Dans votre dernier commentaire, vous avez dit

Si nous utilisons des connexions persistantes, la table MEMORY persistera-t-elle à travers plusieurs DEMANDES, et il semble que ce sera le cas, donc pour des raisons de performances, je suppose que l'utilisation de cette méthode * nous obligera à DROP explicitement DROP la table MEMORY temporaire. Dois-je supposer correctement?

Oui et non. Je dis oui parce que c'est une façon de le faire. Je dis non car une autre façon de procéder est:

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

Quelle que soit la méthode choisie, l'opération est toujours la même puisque TRUNCATE TABLE supprime et recrée la table. Cela ne nuira pas aux autres connexions DB, car chaque connexion a sa propre table id_list.

RolandoMySQLDBA
la source
TRÈS apprécié Rolando! J'ai posté la même question sur SO ( stackoverflow.com/questions/21483448/… ) juste au cas où il y aurait plus d'yeux, et j'ai obtenu des réponses similaires, bien que moins informatives. J'ai posé un suivi: si nous employons des connexions persistantes, la table MEMORY persistera-t-elle à travers plusieurs DEMANDES, et il semble que ce sera le cas, donc pour des raisons de performances, je suppose que l'utilisation de cette méthode * nous obligera à explicitement DROPla MEMORY temporaire table. Dois-je supposer correctement?
oucil
En ce qui concerne votre mise à jour, je suppose que je suis plus soucieux de laisser une ressource en place qui n'est plus nécessaire jusqu'à ce que cette requête soit exécutée à nouveau, et je pense qu'il devient de plus en plus évident que je devrais la supprimer explicitement, que je le fasse ou non. pas besoin.
oucil
" Malheureusement, la seule façon de passer un jeu de résultats d'une procédure stockée à une autre est de passer les résultats via une table temporaire " . Est-ce à dire que nous ne pouvons accéder au jeu de résultats (depuis l'appelant) que lorsque nous connaissons le nom de la table temporaire créée dans la procédure appelée? N'est-ce pas une façon de lire l'ensemble de résultats comme la façon dont nous pouvons utiliser pour lire l'ensemble de résultats d'une SELECTinstruction dans les procédures stockées ( DECLARE aCursor CURSOR FOR SELECT ...)? Par exemple. DECLARE theCursor CURSOR FOR CALL aProcedure()?
Mir-Ismaili
2

Dans la plupart des SGBD, les tables temporaires survivent jusqu'à la fin de la connexion actuelle, sauf indication contraire ou à moins qu'il n'y ait une annulation explicite des transactions (dans certains systèmes, une annulation peut uniquement affecter le contenu de la table, laissant l'objet lui-même autour pour être repeuplé si nécessaire) . Le tableau ne sera pas (par défaut) visible pour les autres connexions, quelle que soit la durée de la connexion qui le crée.

Une analyse rapide sur Google semble indiquer que c'est ainsi que fonctionne mySQL.
( http://www.tutorialspoint.com/mysql/mysql-temporary-tables.htm déclare "par défaut, toutes les tables temporaires sont supprimées par MySQL lorsque votre connexion à la base de données prend fin. Par défaut, toutes les tables temporaires sont supprimées par MySQL lorsque votre connexion à la base de données est interrompue ")

Cependant, il existe souvent des moyens de modifier ces comportements. Par exemple, dans MS SQL Server, vous pouvez créer une table temporaire qui est visible pour toutes les connexions au lieu de la seule en cours en lui donnant un nom commençant par ##.

Je laisse toujours tomber les tables temporaires dès qu'elles ne sont plus nécessaires pour éviter toute confusion possible. J'ai déjà été mordu lorsque le regroupement de connexions a entraîné la création d'une table temporaire provoquant des erreurs, car une table temporaire du même nom a été créée mais n'a pas été détruite lors d'une action précédente qui a utilisé la connexion actuelle.

David Spillett
la source
Je suis d'accord que je devrais trouver un moyen de supprimer explicitement la table, mais je contourne le problème avec lequel vous avez terminé en utilisant un DROPavant de recréer dans l'IF du niveau initial. Merci pour votre contribution!
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 requête donnée donne un résultat avec succès ... lorsque vous placez cette requête dans USP, puis affichez l'erreur plz help me..proc est donné ci-dessous * /

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 ;

CALL usp_GetEngMonthlyChart_Test ('2014-01-01', '2015-07-30')

Ashutosh
la source
2
Publier du code n'est pas suffisant. Cela nécessite une explication
James Anderson