Как долго будет сохраняться временная таблица MEMORY, если я ее не удаляю (MySQL)

13

Я использую рекурсивную хранимую процедуру в MySQL для создания временной таблицы с именем id_list, но я должен использовать результаты этой процедуры в последующем запросе выбора, поэтому я не могу DROPиспользовать временную таблицу в процедуре ...

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;

При вызове процедуры первое значение - это верхний идентификатор нужной мне ветви, а второе - это то, tierкоторое процедура использует во время рекурсии. Перед рекурсивным циклом он проверяет, tier = 0выполняется ли он:

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

Итак, мой вопрос: если у меня нет DROPвременной MEMORYтаблицы в конце процедуры или в моей транзакции, как долго эта таблица будет храниться в памяти? Будет ли он автоматически удален после завершения сеанса или останется в памяти до тех пор, пока соединение открыто?

** NB. Очевидный ответ может состоять в том, чтобы убрать временную таблицу перед оператором commit, но давайте на минутку предположим, что я не могу этого сделать. *


РЕДАКТИРОВАТЬ : Чтобы быть более точным, что, если постоянные соединения используются, таблица будет сохраняться через несколько запросов? До сих пор кажется, что так и будет, и что нам потребуется явно удалить временную таблицу, чтобы освободить этот ресурс.


ОБНОВЛЕНИЕ : Основываясь на рекомендациях комментаторов, я нашел способ настроить свою хранимую процедуру так, чтобы я мог использовать таблицу TEMP MEMORY, но иметь возможность явно DROPее в конце ...

Вместо того, чтобы просто вызывать хранимую процедуру и использовать оставшуюся таблицу TEMP для сбора результатов в реальном запросе, я изменил CALLформат, чтобы использовать третью OUTпеременную, например, так:

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

... затем в хранимой процедуре я добавил секунду IF tier = 0в самом конце со следующим:

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;

Таким образом, результатом хранимой процедуры теперь является список идентификаторов, разделенных запятыми, которые совместимы FIND_IN_SET, и поэтому окончательный запрос был изменен так, чтобы:

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

... сейчас ...

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

Вуаля! Спасибо комментаторам за ваш вклад и за то, что я объяснил причину, по которой мне нужно было постараться немного усерднее :)

oucil
источник

Ответы:

17

Что смешно во временных таблицах в хранимой процедуре, так это не столько временное существование таблицы (которое теряется при прекращении соединения с БД), но и объем хранимой процедуры.

Кто-то задал этот вопрос в StackOverflow: Область временных таблиц, созданных в хранимой процедуре MySQL . Прошло больше года и никто не ответил на вопрос? Позвольте мне установить рекорд прямо. Дело в том, что временная таблица существует внутри и вне хранимой процедуры, но вы можете делать что-то с временной таблицей только внутри области действия хранимой процедуры .

Согласно Книге

kdsjx

В главе 5 есть подзаголовок, возвращающий наборы результатов в другую хранимую процедуру .

В пункте 2 на странице 117 говорится:

К сожалению, единственный способ передать набор результатов из одной хранимой процедуры в другую - это передать результаты через временную таблицу. Это неудобное решение b, и - поскольку временная таблица имеет область действия на протяжении всего сеанса, - она ​​создает многие из тех же проблем сопровождения, возникающих при использовании глобальных переменных. но если одна хранимая программа должна предоставить результаты другой хранимой программе, то временная таблица может быть лучшим решением.

Оглядываясь назад на вопрос StackOverflow , я вижу, что кто-то вызвал хранимую процедуру из клиента mysql. Так как клиент mysql не является хранимой процедурой, результатами нельзя манипулировать уровнем клиента mysql через DML, кроме выполнения SELECT для просмотра результатов. Поскольку вы вызываете рекурсивную хранимую процедуру, вы можете быть уверены, что временная таблица полностью доступна на время соединения с БД .

Надеюсь, это ответит на ваш вопрос.

ОБНОВЛЕНИЕ 2014-01-31 11:26 EST

В своем последнем комментарии вы сказали

Если мы используем постоянные соединения, будет ли таблица MEMORY сохраняться в течение нескольких ЗАПРОСОВ, и, похоже, так и будет, поэтому для повышения производительности я предполагаю, что использование этого метода * ОБЯЗАТЕЛЬНО приведет к явному удалению временной таблицы MEMORY. Я правильно предполагаю?

Да и нет. Я говорю Да, потому что это один из способов сделать это. Я говорю нет, потому что другой способ сделать это:

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

Какой бы путь вы ни выбрали, операция остается прежней, поскольку TRUNCATE TABLE удаляет и заново создает таблицу. Это не повредит другим соединениям с БД, поскольку каждое соединение имеет свою собственную таблицу id_list.

RolandoMySQLDBA
источник
ОЧЕНЬ высоко ценится Роландо! Я разместил тот же вопрос на SO ( stackoverflow.com/questions/21483448/… ) на тот случай, если он получит больше внимания, и я получил похожие, хотя и менее информативные ответы. Я поставил продолжение: если мы будем использовать постоянные соединения, будет ли таблица MEMORY сохраняться в течение нескольких ЗАПРОСОВ, и, похоже, так и будет, поэтому для повышения производительности я предполагаю, что при использовании этого метода * ОБЯЗАТЕЛЬНО нам потребуется явное DROPвременное ПАМЯТЬ стол. Я правильно предполагаю?
oucil
Что касается вашего ОБНОВЛЕНИЯ, я думаю, что меня больше беспокоит вопрос о том, чтобы оставить ресурс на месте, который больше не нужен, до тех пор, пока этот запрос не будет запущен снова, и я думаю, что становится все более очевидным, что я должен явно удалить его независимо от того, не буду ли я Т надо.
oucil
« К сожалению, единственный способ передать набор результатов из одной хранимой процедуры в другую - это передать результаты через временную таблицу » . Означает ли это, что мы можем получить доступ к результирующему набору (от вызывающего) только тогда, когда мы знаем имя временной таблицы, созданной в вызываемой процедуре? Разве способ чтения набора результатов не похож на способ чтения набора результатов SELECTоператора в хранимых процедурах ( DECLARE aCursor CURSOR FOR SELECT ...)? Например DECLARE theCursor CURSOR FOR CALL aProcedure()?
Мир Исмаили
2

В большинстве СУБД временные таблицы сохраняются до конца текущего соединения, если не указано иное, или если нет явного отката транзакции (в некоторых системах откат может повлиять только на содержимое таблицы, оставляя сам объект при необходимости повторным заполнением) , Таблица не будет (по умолчанию) видимой для других соединений, независимо от того, как долго длится соединение, которое ее создает.

Быстрое сканирование в Google, похоже, показывает, как работает MySQL.
( http://www.tutorialspoint.com/mysql/mysql-teilitary-tables.htm заявляет, что «по умолчанию все временные таблицы удаляются MySQL, когда соединение с базой данных прекращается по умолчанию, все временные таблицы удаляются MySQL, когда соединение с вашей базой данных прекращается ")

Однако часто есть способы изменить это поведение. Например, в MS SQL Server вы можете создать временную таблицу, которая будет видна всем соединениям, а не только текущую, присвоив ей имя, начинающееся с ##.

Я всегда удаляю временные таблицы, как только они больше не нужны, чтобы избежать путаницы. Я был укушен раньше, когда пул соединений приводил к созданию временной таблицы, что приводило к ошибкам, поскольку временная таблица с тем же именем была создана, но не уничтожена в предыдущем действии, которое использовало текущее соединение.

Дэвид Спиллетт
источник
Я согласен с тем, что должен найти способ явно отбросить таблицу, но я обошёл проблему, с которой вы покончили, используя DROPпредварительное воссоздание в IF начального уровня. Спасибо за ваш вклад!
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;

/ * данный запрос дает результат успешно ... если поместить этот запрос в USP, а затем отобразится сообщение об ошибке, помогите мне .. процедура приведена ниже * /

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
источник
2
Просто отправка кода недостаточно хороша. Это требует объяснения
Джеймс Андерсон