Обход MySQL Ошибка "Невозможно повторно открыть таблицу"

В настоящее время я занят реализацией фильтра, для которого мне нужно создать кластер 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 не работает в моей ситуации, потому что мои условия - это несколько столбцов в определенном порядке, это сделает ORs из того, что мне нужно для AND. Тем не менее, это помогло мне решить более раннюю проблему, поэтому теперь таблица, темп или нет, больше не требуется. Мы просто думали слишком общие для нашей проблемы. Теперь все применение фильтров было возвращено примерно через минуту, чтобы пройти через четверть секунды.

Ответ 1

Правильно, MySQL docs говорят: "Вы не можете ссылаться на таблицу 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>;

Ответ 2

Простым решением является дублирование временной таблицы. Хорошо работает, если таблица относительно небольшая, что часто бывает с временными таблицами.

Ответ 3

Лично я просто сделаю это постоянным столом. Возможно, вам захочется создать отдельную базу данных для этих таблиц (по-видимому, им понадобятся уникальные имена, так как многие из этих запросов могут быть выполнены за один раз), а также для обеспечения возможности разрешения разрешений (вы можете установить разрешения для баз данных, t установить разрешения на подстановочные знаки таблицы).

Затем вам также понадобится работа по очистке, чтобы иногда удалять старые (MySQL удобно запоминает время создания таблицы, поэтому вы можете просто использовать это для разработки, когда требуется очистка)

Ответ 4

Я обошел это, создав постоянную "временную" таблицу и суффикс 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;

Ответ 5

Я смог изменить запрос на постоянную таблицу, и это исправило это для меня. (изменились настройки VLDB в MicroStrategy, временный тип таблицы).

Ответ 6

Вы можете обойти это, создав постоянную таблицу, которую вы потом удалите, или просто сделав 2 отдельные временные таблицы с одинаковыми данными.