Quanto tempo uma tabela temporária de MEMORY persistirá se eu não a soltar (MySQL)

13

Estou usando um procedimento armazenado recursivo no MySQL para gerar uma tabela temporária chamada id_list, mas devo usar os resultados desse procedimento em uma consulta de seleção de acompanhamento, para que não possa DROPa tabela temporária dentro do procedimento ...

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;

Ao chamar o procedimento, o primeiro valor é o ID superior da ramificação que desejo e o segundo é o tierque o procedimento usa durante as recursões. Antes do loop recursivo, ele verifica se tier = 0e se é executado:

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

Portanto, minha pergunta é: se eu não fizer DROPa MEMORYtabela temporária no final do procedimento ou dentro da minha transação, por quanto tempo essa tabela persistirá na memória? Ele é descartado automaticamente quando a sessão termina ou permanecerá na memória enquanto a conexão estiver aberta?

** NB A resposta óbvia pode ser descartar a tabela temporária antes da declaração de commit, mas vamos assumir por um momento que eu não posso fazer isso. *


EDIT : Para ser um pouco mais preciso, e se conexões persistentes forem empregadas, a tabela persistirá por meio de várias solicitações? Até agora, parece que sim e que precisaríamos remover explicitamente a tabela temporária para liberar esse recurso.


ATUALIZAÇÃO : Com base nos conselhos dos comentaristas, encontrei uma maneira de ajustar meu procedimento armazenado para poder utilizar a tabela TEMP MEMORY, mas poder explicitamente explicá- DROPla no final ...

Em vez de apenas chamar o procedimento armazenado e usar a tabela TEMP restante para reunir os resultados na consulta real, alterei o CALLformato para usar uma terceira OUTvariável da seguinte forma:

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

... então, dentro do procedimento armazenado, adicionei um segundo IF tier = 0no final com o seguinte:

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;

Portanto, o resultado do procedimento armazenado agora é uma lista separada por vírgula de IDs compatíveis e FIND_IN_SET, portanto, a consulta final foi modificada para que:

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

... é agora ...

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

Voila! Agradeço aos comentaristas por sua contribuição e por me darem o motivo de eu precisar me esforçar um pouco mais :)

oucil
fonte

Respostas:

17

O engraçado das tabelas temporárias em um procedimento armazenado não é tanto a existência transitória da tabela (que é descartada após o término da conexão com o banco de dados), mas o escopo do procedimento armazenado.

Alguém fez esta pergunta no StackOverflow: Escopo das tabelas temporárias criadas no procedimento armazenado do MySQL . Já faz mais de um ano e ninguém respondeu à pergunta? Deixe-me esclarecer as coisas. O fato é: A tabela temporária existe dentro e fora do Procedimento Armazenado, mas você pode fazer coisas com a tabela temporária apenas dentro do escopo de um Procedimento Armazenado em execução .

De acordo com o livro

kdsjx

O capítulo 5 possui uma subposição Retornando conjuntos de resultados para outro procedimento armazenado .

Diz no parágrafo 2 da página 117:

Infelizmente, a única maneira de passar um conjunto de resultados de um procedimento armazenado para outro é passar os resultados por meio de uma tabela temporária. Essa é uma solução complicada b e - porque a tabela temporária tem escopo durante toda a sessão - ela cria muitos dos mesmos problemas de manutenção levantados pelo uso de variáveis ​​globais. mas se um programa armazenado precisar fornecer resultados a outro programa armazenado, uma tabela temporária poderá ser a melhor solução.

Olhando para a pergunta StackOverflow , posso ver alguém chamado Stored Procedure no cliente mysql. Como o cliente mysql não é um Procedimento Armazenado, os resultados não podem ser manipulados no nível do cliente mysql via DML, a não ser fazer um SELECT para ver os resultados. Como você está chamando um procedimento armazenado recursivo, pode ter certeza de que a tabela temporária está totalmente acessível durante a conexão com o banco de dados .

Espero que isso responda à sua pergunta.

ATUALIZAÇÃO 31-01-2014 11:26 EST

No seu último comentário, você disse

Se empregarmos conexões persistentes, a tabela MEMORY persistirá por meio de vários PEDIDOS, e parece que sim, portanto, por uma questão de desempenho, estou assumindo que o uso desse método * EXIGE-NOS a DROP explicitamente a tabela temporária MEMORY. Eu assumo corretamente?

Sim e não. Eu digo sim, porque é uma maneira de fazê-lo. Eu digo não, porque outra maneira de fazer isso é:

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

Qualquer que seja a maneira que você escolher, a operação ainda é a mesma, pois TRUNCATE TABLE solta e recria a tabela. Isso não prejudicará outras conexões do banco de dados, pois cada conexão possui sua própria tabela id_list.

RolandoMySQLDBA
fonte
MUITO apreciado Rolando! Publiquei a mesma pergunta no SO ( stackoverflow.com/questions/21483448/… ) para o caso de ter mais olhos nele e obter respostas semelhantes, embora menos informativas. Fiz um acompanhamento: Se empregarmos conexões persistentes, a tabela MEMORY persistirá por meio de vários PEDIDOS, e parece que sim, portanto, por uma questão de desempenho, estou assumindo que o uso desse método * EXIGE - NOS a explicitamente DROPa MEMÓRIA temporária mesa. Eu assumo corretamente?
oucil 31/01
Em relação à sua atualização, acho que estou mais preocupado em deixar um recurso no local que não é mais necessário até que a consulta seja executada novamente e acho que está se tornando mais óbvio que devo removê-la explicitamente, independentemente de não não preciso.
oucil 31/01
" Infelizmente, a única maneira de passar um conjunto de resultados de um procedimento armazenado para outro é passar os resultados por meio de uma tabela temporária " . Isso significa que podemos acessar o conjunto de resultados (do chamador) somente quando sabemos o nome da tabela temporária criada no procedimento chamado? Não é uma maneira de ler o conjunto de resultados da mesma maneira que podemos usar para ler o conjunto de resultados de uma SELECTinstrução nos procedimentos armazenados ( DECLARE aCursor CURSOR FOR SELECT ...)? Por exemplo. DECLARE theCursor CURSOR FOR CALL aProcedure()?
21818 Mir-Ismaili
2

Na maioria das tabelas temporárias de DBMSs, persiste até o final da conexão atual, a menos que seja especificado de outra forma ou a menos que haja uma reversão explícita de transação (em alguns sistemas, uma reversão pode afetar apenas o conteúdo da tabela, deixando o próprio objeto para ser repovoado, se necessário) . A tabela não será (por padrão) visível para outras conexões, independentemente da duração da conexão que a cria.

Uma rápida verificação no Google parece indicar que é assim que o mySQL opera.
( http://www.tutorialspoint.com/mysql/mysql-temporary-tables.htm declara "por padrão, todas as tabelas temporárias são excluídas pelo MySQL quando a conexão com o banco de dados é encerrada. Por padrão, todas as tabelas temporárias são excluídas pelo MySQL quando sua conexão com o banco de dados é encerrada ")

Muitas vezes existem maneiras de alterar esses comportamentos. Por exemplo, no MS SQL Server, você pode criar uma tabela temporária que é visível para todas as conexões, e não apenas a atual, atribuindo um nome a partir de ##.

Eu sempre largo tabelas temporárias assim que elas não são mais necessárias para evitar possíveis confusões. Fui mordido antes em que o pool de conexões resultou na criação temporária de tabelas, causando erros porque uma tabela temporária com o mesmo nome havia sido criada, mas não destruída em uma ação anterior que usava a conexão atual.

David Spillett
fonte
Concordo que devo encontrar uma maneira de eliminar explicitamente a tabela, mas contornar o problema com o qual você terminou usando um DROPantes da recriação no FI da camada inicial. Obrigado pela sua contribuição!
oucil 31/01
-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;

/ * determinada consulta fornece resultado com sucesso ... quando coloca esta consulta na USP e mostra o erro plz help me..proc é dado abaixo * /

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
fonte
2
Apenas postar código não é bom o suficiente. Isso precisa de uma explicação
James Anderson