Wie lange bleibt eine temporäre MEMORY-Tabelle bestehen, wenn ich sie nicht lösche? (MySQL)

13

Ich benutze eine rekursive gespeicherte Prozedur in MySQL, um eine temporäre Tabelle mit dem Namen zu generieren id_list, aber ich muss die Ergebnisse dieser Prozedur in einer nachfolgenden Auswahlabfrage verwenden, damit ich DROPdie temporäre Tabelle in der Prozedur nicht verwenden kann ...

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;

Beim Aufrufen der Prozedur ist der erste Wert die oberste ID des gewünschten Zweigs und der zweite Wert die, tierdie die Prozedur bei Rekursionen verwendet. Vor der rekursiven Schleife wird geprüft, ob tier = 0und ob sie ausgeführt wird:

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

Meine Frage lautet also: Wenn ich DROPdie temporäre MEMORYTabelle am Ende des Vorgangs oder innerhalb meiner Transaktion nicht erhalte, wie lange bleibt diese Tabelle im Speicher? Wird es nach Beendigung der Sitzung automatisch gelöscht oder verbleibt es im Speicher, solange die Verbindung geöffnet ist?

** NB Die naheliegende Antwort könnte darin bestehen, die temporäre Tabelle vor der Festschreibungsanweisung zu löschen, aber nehmen wir für einen Moment an, dass ich das nicht kann. *


EDIT : Genauer gesagt, was passiert, wenn dauerhafte Verbindungen verwendet werden, bleibt die Tabelle bei mehreren Anforderungen bestehen? Bisher scheint es so zu sein und wir müssten die temporäre Tabelle explizit entfernen, um diese Ressource freizugeben.


UPDATE : Basierend auf den Ratschlägen der Kommentatoren habe ich eine Möglichkeit gefunden, meine gespeicherte Prozedur so anzupassen, dass ich die TEMP MEMORY-Tabelle verwenden kann, sie aber DROPam Ende explizit verwenden kann ...

Anstatt nur die gespeicherte Prozedur aufzurufen und die verbleibende TEMP-Tabelle zu verwenden, um die Ergebnisse in der tatsächlichen Abfrage zu erfassen, habe ich das CALLFormat geändert , um eine dritte OUTVariable wie die folgende zu verwenden :

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

... dann habe ich innerhalb der gespeicherten Prozedur IF tier = 0ganz am Ende eine Sekunde mit folgendem hinzugefügt :

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;

Das Ergebnis der gespeicherten Prozedur ist nun eine durch Kommas getrennte Liste von IDs, mit denen kompatibel FIND_IN_SETist. Die endgültige Abfrage wurde folgendermaßen geändert:

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

... ist jetzt ...

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

Voila! Vielen Dank an die Kommentatoren für deinen Beitrag und dafür, dass du mir den Grund gegeben hast, warum ich mich ein bisschen mehr anstrengen musste :)

oucil
quelle

Antworten:

17

Was an temporären Tabellen in einer gespeicherten Prozedur lustig ist, ist weniger die vorübergehende Existenz der Tabelle (die bei Beendigung der DB-Verbindung gelöscht wird), als vielmehr der Umfang der gespeicherten Prozedur.

Jemand hat diese Frage zu StackOverflow gestellt: Umfang der temporären Tabellen, die in einer gespeicherten MySQL-Prozedur erstellt wurden . Es ist über ein Jahr her und niemand hat die Frage beantwortet? Lassen Sie mich den Rekord klarstellen. Tatsache ist: Die temporäre Tabelle ist innerhalb und außerhalb der gespeicherten Prozedur vorhanden, aber Sie können die temporäre Tabelle nur im Rahmen einer ausgeführten gespeicherten Prozedur verwenden .

Nach dem Buch

kdsjx

Kapitel 5 enthält eine Unterüberschrift Zurückgeben von Ergebnismengen an eine andere gespeicherte Prozedur .

In Absatz 2 auf Seite 117 heißt es:

Leider besteht die einzige Möglichkeit, eine Ergebnismenge von einer gespeicherten Prozedur an eine andere zu übergeben, darin, die Ergebnisse über eine temporäre Tabelle zu übergeben. Dies ist eine umständliche Lösung. B. Da die temporäre Tabelle während der gesamten Sitzung Gültigkeit hat, treten viele der Probleme mit der Wartbarkeit auf, die durch die Verwendung globaler Variablen verursacht werden. Wenn jedoch ein gespeichertes Programm ein anderes gespeichertes Programm mit Ergebnissen versorgen muss, kann eine temporäre Tabelle die beste Lösung sein.

Wenn ich auf die StackOverflow-Frage zurückblicke , sehe ich, dass jemand die gespeicherte Prozedur vom MySQL-Client aus aufgerufen hat. Da es sich bei dem MySQL-Client nicht um eine gespeicherte Prozedur handelt, können die Ergebnisse nicht über DML auf MySQL-Client-Ebene bearbeitet werden, es sei denn, Sie führen ein SELECT aus, um die Ergebnisse anzuzeigen. Da Sie eine rekursive gespeicherte Prozedur aufrufen, können Sie sicher sein, dass die temporäre Tabelle für die Dauer der DB-Verbindung uneingeschränkt verfügbar ist .

Ich hoffe das beantwortet deine Frage.

UPDATE 31.01.2014 11:26 EST

In Ihrem letzten Kommentar haben Sie gesagt

Wenn wir dauerhafte Verbindungen verwenden, bleibt die MEMORY-Tabelle über mehrere REQUESTS erhalten, und es scheint, als ob dies der Leistung halber der Fall ist. Ich gehe davon aus, dass wir mit dieser Methode * die temporäre MEMORY-Tabelle explizit löschen müssen. Gehe ich richtig aus

Ja und Nein. Ich sage Ja, weil es eine Möglichkeit ist, dies zu tun. Ich sage nein, weil ein anderer Weg dies zu tun ist:

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

Wie auch immer Sie sich entscheiden, die Operation ist immer noch dieselbe, da TRUNCATE TABLE die Tabelle löscht und neu erstellt. Dies schadet anderen DB-Verbindungen nicht, da jede Verbindung eine eigene id_list-Tabelle hat.

RolandoMySQLDBA
quelle
Rolando sehr geschätzt! Ich habe die gleiche Frage auf SO ( stackoverflow.com/questions/21483448/… ) gepostet, nur für den Fall, dass es mehr Augen darauf gibt, und ich habe ähnliche, wenn auch weniger informative Antworten erhalten. Ich habe eine Folgemaßnahme vorgeschlagen: Wenn wir dauerhafte Verbindungen verwenden, bleibt die MEMORY-Tabelle über mehrere REQUESTS bestehen, und es scheint, als ob dies der Leistung halber der Fall ist , gehe ich davon aus, dass wir mit dieser Methode *DROP den temporären MEMORY explizit benötigen Tabelle. Gehe ich richtig aus
oucil
In Bezug auf Ihr UPDATE bin ich wahrscheinlich eher darum bemüht, eine Ressource an Ort und Stelle zu belassen, die nicht mehr erforderlich ist, bis diese Abfrage erneut ausgeführt wird, und ich denke, es wird immer offensichtlicher, dass ich sie explizit entfernen sollte , unabhängig davon, ob ich sie nicht aktiviere. muss nicht.
oucil
Leider ist die einzige Möglichkeit , eine Ergebnismenge aus einer gespeicherten Prozedur zu einem anderen zu übergeben ist , die Ergebnisse über eine temporäre Tabelle zu übergeben . Bedeutet dies, dass wir auf die Ergebnismenge (vom Aufrufer) nur zugreifen können, wenn wir den Namen der temporären Tabelle kennen, die in der aufgerufenen Prozedur erstellt wurde? Ist eine Methode zum Lesen der Ergebnismenge nicht so, wie wir sie zum Lesen der Ergebnismenge einer SELECTAnweisung in gespeicherten Prozeduren ( DECLARE aCursor CURSOR FOR SELECT ...) verwenden können? Z.B. DECLARE theCursor CURSOR FOR CALL aProcedure()?
Mir-Ismaili
2

In den meisten DBMSs bleiben temporäre Tabellen bis zum Ende der aktuellen Verbindung erhalten, sofern nichts anderes angegeben ist oder es kein explizites Transaktionsrollback gibt (in einigen Systemen wirkt sich ein Rollback möglicherweise nur auf den Inhalt der Tabelle aus, sodass das Objekt selbst bei Bedarf neu aufgefüllt werden muss). . Die Tabelle ist (standardmäßig) für andere Verbindungen nicht sichtbar, unabhängig davon, wie lange die Verbindung, die sie erstellt, dauert.

Ein schneller Scan bei Google scheint zu zeigen, wie mySQL funktioniert.
( http://www.tutorialspoint.com/mysql/mysql-temporary-tables.htm besagt, dass standardmäßig alle temporären Tabellen von MySQL gelöscht werden, wenn Ihre Datenbankverbindung beendet wird. Standardmäßig werden alle temporären Tabellen von MySQL gelöscht, wenn Ihre Datenbankverbindung wird beendet ")

Es gibt jedoch oft Möglichkeiten, diese Verhaltensweisen zu ändern. Beispielsweise können Sie in MS SQL Server eine temporäre Tabelle erstellen, die für alle Verbindungen sichtbar ist, anstatt nur für die aktuelle, indem Sie ihr einen Namen geben, der mit ## beginnt.

Ich lösche temporäre Tabellen immer, sobald sie nicht mehr benötigt werden, um mögliche Verwirrung zu vermeiden. Ich bin schon gebissen worden, als das Zusammenlegen von Verbindungen zu Fehlern bei der temporären Tabellenerstellung führte, da in einer früheren Aktion, in der die aktuelle Verbindung verwendet wurde, eine temporäre Tabelle mit demselben Namen erstellt, aber nicht zerstört wurde.

David Spillett
quelle
Ich bin damit einverstanden, dass ich einen Weg finden sollte, um die Tabelle explizit zu löschen, aber ich komme um das Problem herum, mit dem Sie fertig sind, indem Sie ein DROPvor der Neuerstellung in der IF der ersten Ebene verwenden. Danke für deinen Beitrag!
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;

/ * gegebene Abfrage liefert erfolgreiches Ergebnis ... wenn diese Abfrage in USP gestellt wird, wird der Fehler angezeigt. Bitte helfen Sie mir.

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
quelle
2
Nur die Postleitzahl ist nicht gut genug. Dies bedarf einer Erklärung
James Anderson