Per quanto tempo persiste una tabella MEMORY temporanea se non la trascino (MySQL)


13

Sto usando una procedura memorizzata ricorsiva in MySQL per generare una tabella temporanea chiamata id_list, ma devo usare i risultati di quella procedura in una query di selezione successiva, quindi non posso DROPla tabella temporanea all'interno della procedura ...

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;

Quando si chiama la procedura, il primo valore è l'ID superiore del ramo che desidero, e il secondo è l' tierusato dalla procedura durante le ricorsioni. Prima del ciclo ricorsivo verifica se tier = 0e se è in esecuzione:

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

Quindi la mia domanda è: se non DROPeseguo la MEMORYtabella temporanea al termine della procedura o all'interno della mia transazione, per quanto tempo la tabella persisterà in memoria? Viene automaticamente rilasciato al termine della sessione o rimarrà in memoria finché la connessione è aperta?

** NB La risposta ovvia potrebbe essere quella di eliminare la tabella temporanea prima dell'istruzione commit, ma supponiamo per un momento che non posso farlo. *


EDIT : per essere un po 'più precisi, cosa succede se vengono utilizzate connessioni persistenti, la tabella persisterà attraverso più richieste? Finora sembra che lo farà e che dovremmo rimuovere esplicitamente la tabella temporanea per liberare quella risorsa.


AGGIORNAMENTO : Sulla base dei consigli dei commentatori, ho trovato un modo per adattare la mia procedura memorizzata in modo da poter utilizzare la tabella MEMP TEMP, ma poterla esplicitamente DROPalla fine ...

Invece di chiamare semplicemente la procedura memorizzata e utilizzare la tabella TEMP rimanente per raccogliere i risultati nella query effettiva, ho modificato il CALLformato per utilizzare una terza OUTvariabile in questo modo:

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

... poi all'interno della procedura memorizzata, ho aggiunto un secondo IF tier = 0alla fine con il seguente:

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;

Quindi il risultato della procedura memorizzata è ora un elenco separato da virgole di ID compatibili FIND_IN_SET, e quindi la query finale è stata modificata in modo che:

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

... è ora ...

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

Ecco! Grazie ai commentatori per il tuo contributo e per avermi dato il motivo per cui ho dovuto provare un po 'di più :)

Risposte:


17

La cosa divertente delle tabelle temporanee in una procedura memorizzata non è tanto l'esistenza transitoria della tabella (che viene rilasciata al termine della connessione DB), ma l'ambito della procedura memorizzata.

Qualcuno ha posto questa domanda su StackOverflow: ambito delle tabelle temporanee create nella stored procedure MySQL . È passato più di un anno e nessuno ha risposto alla domanda? Permettetemi di mettere le cose in chiaro. Il fatto è: la tabella temporanea esiste all'interno e all'esterno della Stored procedure, ma è possibile eseguire operazioni con la tabella temporanea solo nell'ambito di una Stored procedure in esecuzione .

Secondo il libro

kdsjx

Il capitolo 5 presenta una sottovoce Restituzione dei set di risultati in un'altra procedura memorizzata .

Si dice al paragrafo 2 a pagina 117:

Sfortunatamente, l'unico modo per passare un set di risultati da una procedura memorizzata a un'altra è passare i risultati tramite una tabella temporanea. Questa è una soluzione imbarazzante b e, poiché la tabella temporanea ha portata nell'intera sessione, crea molti degli stessi problemi di manutenibilità sollevati dall'uso delle variabili globali. ma se un programma memorizzato deve fornire risultati a un altro programma memorizzato, una tabella temporanea può essere la soluzione migliore.

Guardando indietro alla domanda StackOverflow , posso vedere qualcuno chiamato Stored Procedure dal client mysql. Poiché il client mysql non è una Stored procedure, i risultati non possono essere manipolati a livello di client mysql tramite DML se non facendo un SELECT per vedere i risultati. Poiché si chiama una procedura memorizzata ricorsiva, si può essere certi che la tabella temporanea è completamente accessibile per la durata della connessione DB .

Spero che questo risponda alla tua domanda.

AGGIORNAMENTO 2014-01-31 11:26 EST

Nel tuo ultimo commento, hai detto

Se utilizziamo connessioni persistenti, la tabella MEMORY persisterà attraverso più RICHIESTE, e sembra che lo sarà, quindi per motivi di prestazioni, presumo che l'uso di questo metodo * RICHIEDEREMO di DROP esplicitamente la tabella MEMORY temporanea. Presumo correttamente?

Sì e No. Dico Sì perché è un modo per farlo. Dico di no perché un altro modo per farlo è:

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

Qualunque modo tu scelga, l'operazione è sempre la stessa poiché TRUNCATE TABLE si abbassa e ricrea la tabella. Ciò non danneggerà altre connessioni DB poiché ciascuna connessione ha la propria tabella id_list.


Molto apprezzato Rolando! Ho pubblicato la stessa domanda su SO ( stackoverflow.com/questions/21483448/… ) nel caso in cui avesse più occhi su di esso, e ho avuto risposte simili, anche se meno istruttive. Ho proposto un follow-up: se utilizziamo connessioni persistenti, la tabella MEMORY persisterà attraverso più RICHIESTE, e sembra che lo sarà, quindi per motivi di prestazioni, presumo che l'uso di questo metodo ci * RICHIEDERÀ esplicitamente DROPla MEMORIA temporanea tavolo. Presumo correttamente?
oucil,

In relazione al tuo AGGIORNAMENTO, immagino di essere più interessato a lasciare una risorsa che non è più necessaria fino a quando la query non viene eseguita di nuovo, e penso che diventi più ovvio che dovrei rimuoverla esplicitamente indipendentemente dal fatto che non lo faccia ' non è necessario.
oucil,

" Sfortunatamente, l'unico modo per passare un set di risultati da una procedura memorizzata a un'altra è passare i risultati tramite una tabella temporanea " . Questo significa che possiamo accedere al set di risultati (dal chiamante) solo quando conosciamo il nome della tabella temporanea creata nella procedura chiamata? Non è un modo per leggere il set di risultati come il modo in cui possiamo usare per leggere il set di risultati di SELECTun'istruzione nelle stored procedure ( DECLARE aCursor CURSOR FOR SELECT ...)? Per esempio. DECLARE theCursor CURSOR FOR CALL aProcedure()?
Mir-Ismaili,

2

Nella maggior parte dei DBMS le tabelle temporanee sopravvivono fino alla fine della connessione corrente, a meno che non sia diversamente specificato o a meno che non vi sia un rollback esplicito della transazione (in alcuni sistemi un rollback può influire solo sul contenuto della tabella, lasciando l'oggetto stesso da ripopolare se necessario) . La tabella non sarà (per impostazione predefinita) visibile ad altre connessioni, indipendentemente dalla durata della connessione che la crea.

Una rapida scansione su Google sembra indicare come funziona mySQL.
( http://www.tutorialspoint.com/mysql/mysql-temporary-tables.htm afferma "per impostazione predefinita, tutte le tabelle temporanee vengono eliminate da MySQL quando viene interrotta la connessione al database. Per impostazione predefinita, tutte le tabelle temporanee vengono eliminate da MySQL quando la tua connessione al database viene terminata ")

Tuttavia, ci sono spesso modi per modificare questi comportamenti. Ad esempio in MS SQL Server è possibile creare una tabella temporanea che è visibile a tutte le connessioni anziché solo a quella corrente assegnandole un nome che inizia con ##.

Lascio sempre cadere le tabelle temporanee non appena non sono più necessarie per evitare possibili confusioni. Sono stato morso prima in cui il pool di connessioni ha provocato la creazione di tabelle temporanee causando errori perché una tabella temporanea con lo stesso nome era stata creata ma non distrutta in un'azione precedente che utilizzava la connessione corrente.


Sono d'accordo che dovrei trovare un modo per eliminare esplicitamente la tabella, ma aggiro il problema che hai risolto utilizzando un DROPprecedente alla ricostruzione all'interno dell'IF del livello iniziale. Grazie per il tuo contributo!
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 query fornita restituisce il risultato correttamente ... quando si inserisce questa query in USP, viene visualizzato l'errore per favore aiutami..processo viene fornito di seguito * /

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')


2
Solo pubblicare il codice non è abbastanza buono. Questo ha bisogno di una spiegazione
James Anderson, il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.