В настоящее время я занят реализацией своего рода фильтра, для которого мне нужно сгенерировать блокировку INNER JOIN для каждого «тега» для фильтрации.
Проблема в том, что после целой кучи SQL у меня есть таблица, содержащая всю информацию, которая мне нужна для выбора, но она мне нужна снова для каждого сгенерированного INNER JOIN
В основном это выглядит так:
SELECT
*
FROM search
INNER JOIN search f1 ON f1.baseID = search.baseID AND f1.condition = condition1
INNER JOIN search f2 ON f2.baseID = search.baseID AND f2.condition = condition2
...
INNER JOIN search fN ON fN.baseID = search.baseID AND fN.condition = conditionN
Это работает, но я бы предпочел, чтобы таблица "поиска" была временной (она может быть на несколько порядков меньше, если это не обычная таблица), но это дает мне очень досадную ошибку: Can't reopen table
Некоторые исследования приводят меня к этому отчету об ошибке но люди в MySQL, похоже, не заботятся о том, что такая базовая функция (использование таблицы более одного раза) не работает с временными таблицами. У меня много проблем с масштабируемостью, связанных с этой проблемой.
Есть ли какой-нибудь жизнеспособный обходной путь, который не требует от меня управления потенциально большим количеством временных, но очень реальных таблиц или необходимости поддерживать огромную таблицу со всеми данными в ней?
С уважением, Крис
[дополнительный]
Ответ GROUP_CONCAT не работает в моей ситуации, потому что мои условия представляют собой несколько столбцов в определенном порядке, это сделало бы ИЛИ из того, что мне нужно, быть И. Однако это помогло мне решить более раннюю проблему, поэтому теперь таблица, временная или нет, больше не требуется. Мы просто думали слишком общими для нашей проблемы. Полное применение фильтров теперь уменьшено с одной минуты до менее чем четверти секунды.
источник
Ответы:
Если переход на MariaDB (форк MySQL) возможен - это раздражение исправлено там, начиная с версии 10.2.1: https://jira.mariadb.org/browse/MDEV-5535 .
источник
Простое решение - продублировать временную таблицу. Хорошо работает, если таблица относительно небольшая, что часто бывает с временными таблицами.
источник
Правильно, в документации MySQL говорится: «Вы не можете ссылаться на
TEMPORARY
таблицу более одного раза в одном запросе».Вот альтернативный запрос, который должен найти те же строки, хотя все условия сопоставления строк не будут в отдельных столбцах, они будут в списке, разделенном запятыми.
SELECT f1.baseID, GROUP_CONCAT(f1.condition) FROM search f1 WHERE f1.condition IN (<condition1>, <condition2>, ... <conditionN>) GROUP BY f1.baseID HAVING COUNT(*) = <N>;
источник
Я обошел это, создав постоянную «временную» таблицу и добавив к имени таблицы суффикс SPID (извините, я из страны SQL Server), чтобы получить уникальное имя таблицы. Затем создание динамических операторов SQL для создания запросов. Если произойдет что-то плохое, таблица будет удалена и создана заново.
Надеюсь на лучший вариант. Да ладно, MySQL Devs. «Ошибка» / «запрос функции» открыт с 2008 года! Кажется, что все встреченные «жучки» находятся в одной лодке.
select concat('ReviewLatency', CONNECTION_ID()) into @tablename; #Drop "temporary" table if it exists set @dsql=concat('drop table if exists ', @tablename, ';'); PREPARE QUERY1 FROM @dsql; EXECUTE QUERY1; DEALLOCATE PREPARE QUERY1; #Due to MySQL bug not allowing multiple queries in DSQL, we have to break it up... #Also due to MySQL bug, you cannot join a temporary table to itself, #so we create a real table, but append the SPID to it for uniqueness. set @dsql=concat(' create table ', @tablename, ' ( `EventUID` int(11) not null, `EventTimestamp` datetime not null, `HasAudit` bit not null, `GroupName` varchar(255) not null, `UserID` int(11) not null, `EventAuditUID` int(11) null, `ReviewerName` varchar(255) null, index `tmp_', @tablename, '_EventUID` (`EventUID` asc), index `tmp_', @tablename, '_EventAuditUID` (`EventAuditUID` asc), index `tmp_', @tablename, '_EventUID_EventTimestamp` (`EventUID`, `EventTimestamp`) ) ENGINE=MEMORY;'); PREPARE QUERY2 FROM @dsql; EXECUTE QUERY2; DEALLOCATE PREPARE QUERY2; #Insert into the "temporary" table set @dsql=concat(' insert into ', @tablename, ' select e.EventUID, e.EventTimestamp, e.HasAudit, gn.GroupName, epi.UserID, eai.EventUID as `EventAuditUID` , concat(concat(concat(max(concat('' '', ui.UserPropertyValue)), '' (''), ut.UserName), '')'') as `ReviewerName` from EventCore e inner join EventParticipantInformation epi on e.EventUID = epi.EventUID and epi.TypeClass=''FROM'' inner join UserGroupRelation ugr on epi.UserID = ugr.UserID and e.EventTimestamp between ugr.EffectiveStartDate and ugr.EffectiveEndDate inner join GroupNames gn on ugr.GroupID = gn.GroupID left outer join EventAuditInformation eai on e.EventUID = eai.EventUID left outer join UserTable ut on eai.UserID = ut.UserID left outer join UserInformation ui on eai.UserID = ui.UserID and ui.UserProperty=-10 where e.EventTimestamp between @StartDate and @EndDate and e.SenderSID = @FirmID group by e.EventUID;'); PREPARE QUERY3 FROM @dsql; EXECUTE QUERY3; DEALLOCATE PREPARE QUERY3; #Generate the actual query to return results. set @dsql=concat(' select rl1.GroupName as `Group`, coalesce(max(rl1.ReviewerName), '''') as `Reviewer(s)`, count(distinct rl1.EventUID) as `Total Events` , (count(distinct rl1.EventUID) - count(distinct rl1.EventAuditUID)) as `Unreviewed Events` , round(((count(distinct rl1.EventUID) - count(distinct rl1.EventAuditUID)) / count(distinct rl1.EventUID)) * 100, 1) as `% Unreviewed` , date_format(min(rl2.EventTimestamp), ''%W, %b %c %Y %r'') as `Oldest Unreviewed` , count(distinct rl3.EventUID) as `<=7 Days Unreviewed` , count(distinct rl4.EventUID) as `8-14 Days Unreviewed` , count(distinct rl5.EventUID) as `>14 Days Unreviewed` from ', @tablename, ' rl1 left outer join ', @tablename, ' rl2 on rl1.EventUID = rl2.EventUID and rl2.EventAuditUID is null left outer join ', @tablename, ' rl3 on rl1.EventUID = rl3.EventUID and rl3.EventAuditUID is null and rl1.EventTimestamp > DATE_SUB(NOW(), INTERVAL 7 DAY) left outer join ', @tablename, ' rl4 on rl1.EventUID = rl4.EventUID and rl4.EventAuditUID is null and rl1.EventTimestamp between DATE_SUB(NOW(), INTERVAL 7 DAY) and DATE_SUB(NOW(), INTERVAL 14 DAY) left outer join ', @tablename, ' rl5 on rl1.EventUID = rl5.EventUID and rl5.EventAuditUID is null and rl1.EventTimestamp < DATE_SUB(NOW(), INTERVAL 14 DAY) group by rl1.GroupName order by ((count(distinct rl1.EventUID) - count(distinct rl1.EventAuditUID)) / count(distinct rl1.EventUID)) * 100 desc ;'); PREPARE QUERY4 FROM @dsql; EXECUTE QUERY4; DEALLOCATE PREPARE QUERY4; #Drop "temporary" table set @dsql = concat('drop table if exists ', @tablename, ';'); PREPARE QUERY5 FROM @dsql; EXECUTE QUERY5; DEALLOCATE PREPARE QUERY5;
источник
Лично я бы просто сделал это постоянным столом. Возможно, вы захотите создать отдельную базу данных для этих таблиц (предположительно, им потребуются уникальные имена, так как многие из этих запросов могут быть выполнены одновременно), а также для разумной установки разрешений (вы можете установить разрешения для баз данных; вы можете '' t установить разрешения на подстановочные знаки таблиц).
Тогда вам также понадобится задание по очистке, чтобы иногда удалять старые (MySQL удобно запоминает время создания таблицы, поэтому вы можете просто использовать это для работы, когда требуется очистка)
источник
Я смог изменить запрос на постоянную таблицу, и это исправило это для меня. (изменил настройки VLDB в MicroStrategy, тип временной таблицы).
источник
Вы можете обойти это, создав постоянную таблицу, которую потом удалите, или просто создайте две отдельные временные таблицы с одинаковыми данными.
источник
Вот документы MYSQL по этой проблеме. Я использую повторяющиеся временные таблицы, такие как некоторые из ответов выше, однако у вас может быть ситуация, когда CTE подходит!
https://dev.mysql.com/doc/refman/8.0/en/ Contemporary-table-problems.html
источник