Muoversi Errore MySQL "Impossibile riaprire la tabella"


88

Sono attualmente impegnato nell'implementazione di una sorta di filtro per il quale ho bisogno di generare una clausola INNER JOIN per ogni "tag" su cui filtrare.

Il problema è che dopo un intero gruppo di SQL, ho una tabella che contiene tutte le informazioni di cui ho bisogno per effettuare la mia selezione, ma ne ho bisogno di nuovo per ogni INNER JOIN generato

Questo fondamentalmente sembra:

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

Funziona ma preferirei di gran lunga che la tabella di "ricerca" fosse temporanea (può essere più piccola di diversi ordini di grandezza se non è una tabella normale) ma questo mi dà un errore molto fastidioso: Can't reopen table

Alcune ricerche mi portano a questa segnalazione di bug, ma la gente di MySQL non sembra preoccuparsi del fatto che una caratteristica di base (usando una tabella più di una volta) non funzioni con le tabelle temporanee. Sto riscontrando molti problemi di scalabilità con questo problema.

Esiste una soluzione alternativa praticabile che non mi richieda di gestire potenzialmente molte tabelle temporanee ma molto reali o di farmi mantenere una tabella enorme con tutti i dati in essa contenuti?

Cordiali saluti, Kris

[aggiuntivo]

La risposta GROUP_CONCAT non funziona nella mia situazione perché le mie condizioni sono più colonne in un ordine specifico, renderebbe OR da quello che devo essere AND. Tuttavia, mi ha aiutato a risolvere un problema precedente, quindi ora la tabella, temporanea o meno, non è più necessaria. Stavamo solo pensando troppo generici per il nostro problema. L'intera applicazione dei filtri è stata ora riportata da circa un minuto a ben meno di un quarto di secondo.


2
Ho avuto lo stesso problema utilizzando una tabella temporanea due volte nella stessa query utilizzando UNION.
Sebastián Grignoli

Risposte:



122

Una soluzione semplice è duplicare la tabella temporanea. Funziona bene se la tabella è relativamente piccola, come spesso accade con le tabelle temporanee.


8
In realtà dovrebbe essere la risposta scelta poiché questa risponde al problema, senza andare in giro.
coloranti

4
qualche consiglio su come duplicheresti la tabella? (Intendo un modo di copiare senza ripetere la domanda)
Hernán Eche

16
Anche se la tabella temporanea è grande, la cache di mysql dovrebbe aiutarti. Per quanto riguarda la copia da una tabella temporanea a un'altra, un semplice "CREATE TEMPORARY TABLE tmp2 SELECT * FROM tmp1" dovrebbe farlo.
AS7K

2
Se copi il contenuto temporaneo, non dimenticare di creare anche gli indici, altrimenti la tua query potrebbe essere piuttosto lenta.
gaborsch

1
@NgSekLong Sì. Tutto il tempo. Ovviamente dipende dalla tua applicazione per la query, ma non vedo problemi di prestazioni "enormi" fino a> 100.000. In un processo ETL, utilizzo questo metodo con una tabella da 3,5 mil. La velocità di quell'applicazione non è così importante.
Tanner Clark,

49

Bene, i documenti MySQL dicono: "Non puoi fare riferimento a un fileTEMPORARY tabella più di una volta nella stessa query".

Ecco una query alternativa che dovrebbe trovare le stesse righe, sebbene tutte le condizioni di corrispondenza delle righe non saranno in colonne separate, saranno in un elenco separato da virgole.

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
Questo in realtà non ha risolto il mio problema a portata di mano, ma mi ha permesso di semplificare il problema che lo ha causato, annullando così la necessità del temptable. Grazie!
Kris

6

Ho aggirato questo problema creando una tabella "temporanea" permanente e aggiungendo il suffisso SPID (scusate, vengo da SQL Server) al nome della tabella, per creare un nome di tabella univoco. Quindi creare istruzioni SQL dinamiche per creare le query. Se succede qualcosa di brutto, la tabella verrà eliminata e ricreata.

Spero in un'opzione migliore. Dai, MySQL Devs. Il "bug" / "richiesta di funzionalità" è aperto dal 2008! Sembra che tutti i "bug" che ho incontrato siano sulla stessa barca.

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;

Si spera che ora che Oracle prende il sopravvento, possa dare a MySQL una buona spinta.
Pacerier

2
sospiro ne dubito :(
beeks

3
Un grande sospiro . Luglio 2016 e questo bug della tabella temporanea non è stato ancora risolto. Probabilmente inventerò una sorta di numero di sequenza concatenato con un nome di tabella permanente (vengo da Oracle Land) per aggirare questo problema.
TheWalkingData

Hattrick sigh ... Potrebbe non essere mai risolto, visto che è già il 2019.
Zimano

3

Personalmente ne farei un tavolo permanente. Potresti voler creare un database separato per queste tabelle (presumibilmente avranno bisogno di nomi univoci poiché molte di queste query potrebbero essere eseguite contemporaneamente), anche per consentire l'impostazione ragionevole delle autorizzazioni (Puoi impostare le autorizzazioni sui database; puoi " t impostare i permessi sui caratteri jolly della tabella).

Quindi avresti anche bisogno di un lavoro di pulizia per rimuovere di tanto in tanto quelli vecchi (MySQL ricorda comodamente l'ora in cui è stata creata una tabella, quindi puoi semplicemente usarlo per capire quando è necessaria una pulizia)


9
Le tabelle temporanee hanno l'estremo vantaggio di poter eseguire più query contemporaneamente. Questo non è possibile con le tabelle permanenti.
Pacerier

Penso che la "soluzione" della tabella permanente non sia una soluzione. Risolve sicuramente il problema, ma non è pratico. Sorgono così tante domande: come posso crearne più di una contemporaneamente? Come gestireste la convenzione di denominazione e la sovrascrittura di tabelle con lo stesso nome? Qual è il processo per eliminare la tabella permanente? Se potessi elaborare una soluzione fattibile utilizzando tabelle permanenti mentre rispondi a queste domande, sono tutt'orecchi!
Tanner Clark,

0

Sono stato in grado di modificare la query in una tabella permanente e questo lo ha risolto per me. (modificato le impostazioni VLDB in MicroStrategy, tipo di tabella temporanea).


-1

Puoi aggirarlo creando una tabella permanente, che rimuoverai in seguito, o semplicemente creando 2 tabelle temporanee separate con gli stessi dati


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.