Помилка MySQL "Не вдається повторно відкрити таблицю"


91

В даний час я зайнятий реалізацією сортового фільтра, для якого мені потрібно створити клавішу INNER JOIN для кожного "тегу" для фільтрування.

Проблема полягає в тому, що після цілого зв’язку SQL у мене є таблиця, яка містить всю інформацію, необхідну мені для вибору, але вона потрібна мені знову для кожного згенерованого ВНУТРІШНЬОГО ПРИЄДНАННЯ

Це в основному виглядає так:

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 не працює в моїй ситуації, оскільки мої умови - це кілька стовпців у певному порядку, це зробить АБО з того, що мені потрібно бути І. Однак це допомогло мені вирішити попередню проблему, тому тепер таблиця, тимчасова чи ні, більше не потрібна. Ми просто думали надто загально для нашої проблеми. Зараз усе застосування фільтрів повернуто приблизно з хвилини до менш ніж чверті секунди.


2
У мене була однакова проблема із використанням тимчасової таблиці двічі в одному запиті за допомогою UNION.
Себастьян Гриньолі,

Відповіді:



125

Просте рішення - продублювати тимчасову таблицю. Добре працює, якщо стіл порівняно невеликий, що часто трапляється з тимчасовими таблицями.


8
Повинно бути обраною відповіддю, оскільки це відповідає на проблему, не обходячись.
dyesdyes

4
будь-яка порада щодо того, як ви дублюєте таблицю? (Я маю на увазі спосіб копіювання, не повторюючи запит)
Hernán Eche 02.03.16

16
Навіть якщо тимчасова таблиця велика, кеш-пам'ять MySQL повинен вам допомогти. Що стосується копіювання з однієї тимчасової таблиці в іншу, це має зробити проста "СТВОРИТИ ТАБЛИЦЮ ТАБЛИЦІ tmp2 SELECT * FROM tmp1".
AS7K

2
Якщо ви копіюєте спокусливий вміст, не забудьте також створити індекси, інакше ваш запит може бути досить повільним.
gaborsch

1
@NgSekLong Так. Весь час. Очевидно, це залежить від вашої заявки на запит, але я не бачу "величезних" проблем із продуктивністю до> 100 000. В одному процесі ETL я використовую цей метод із таблицею 3,5 мільйона. Швидкість цього додатка не так важлива.
Таннер Кларк,

49

Правильно, документи 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>;

2
Це насправді не вирішило мою проблему, але дозволило спростити проблему, яка її спричинила, тим самим заперечуючи потребу в спокусі. Дякую!
Кріс

6

Я обійшов це шляхом створення постійної "тимчасової" таблиці та додавання 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;

Сподіваємось, тепер, коли Oracle бере на себе владу, вона може дати MySQL хороший поштовх.
Pacerier

2
зітхаю Я сумніваюся :(
б'єкс

3
Велике зітхання . Липня 2016 р., І ця помилка тимчасової таблиці все ще не виправлена. Я, напевно, придумаю якийсь номер послідовності, об’єднаний постійною назвою таблиці (я з країни Oracle), щоб обійти цю проблему.
TheWalkingData

Hattrick зітхає ... Можливо, це ніколи не буде виправлено, оскільки вже вийшов 2019 рік.
Zimano

3

Особисто я б просто зробив це постійним столом. Можливо, ви захочете створити окрему базу даних для цих таблиць (мабуть, їм знадобляться унікальні імена, оскільки багато цих запитів можна робити одночасно), а також, щоб дозволити розумно встановлювати дозволи (Ви можете встановити дозволи для баз даних; Ви можете ' t встановити дозволи для символів узагальнення таблиці).

Тоді вам також знадобиться робота по очищенню, щоб зрідка видаляти старі (MySQL зручно запам'ятовує час створення таблиці, тому ви можете просто використовувати це для обробки, коли потрібно очищення)


9
Тимчасові таблиці мають надзвичайну перевагу в тому, що ви можете одночасно виконувати кілька запитів. Це неможливо за допомогою постійних таблиць.
Pacerier

Я думаю, що "рішення" постійної таблиці не є рішенням. Це точно вирішує проблему, але не є практичним. Виникає так багато запитань: Як створити більше, ніж одночасно? Як би ви впоралися з правилами іменування та перезаписом однакових іменованих таблиць? Який процес видалення постійної таблиці? Якщо ви могли б розробити можливе рішення, використовуючи постійні таблиці, відповідаючи на ці запитання, я - усі вуха!
Таннер Кларк,

0

Я зміг змінити запит на постійну таблицю, і це мені це виправило. (змінено налаштування VLDB в MicroStrategy, тимчасовий тип таблиці).


-1

Ви можете обійти це, створивши постійну таблицю, яку потім видалите, або просто склавши 2 окремі тимчасові таблиці з однаковими даними


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.