2008-12-05 9 views
61

Ich bin gerade damit beschäftigt, einen Filter von Arten zu implementieren, für die ich eine INNER JOIN-Klausel für jedes "Tag" generieren muss, um zu filtern.MySQL umgehen "Kann Tabelle nicht öffnen" Fehler

Das Problem ist, dass nach einer ganzen Reihe von SQL, ich eine Tabelle, die alle Informationen enthält ich brauche, um meine Auswahl zu treffen, aber ich brauche es wieder für jeden erzeugten INNER JOIN

Dieser im Grunde wie folgt aussieht:

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 

Dies funktioniert, aber ich würde viel lieber die „Suche“ Tabelle vorübergehend sein (es kann kleiner um mehrere Größenordnungen, wenn es nicht ein normaler Tisch ist), aber das gibt mir ein sehr ärgerlich Fehler: Can't reopen table

Einige Forschung führt mich zu this bug report, aber die Leute bei MySQL scheint nicht zu kümmern, dass eine solche grundlegende Funktion (mit einer Tabelle mehr als einmal) nicht mit temporären Tabellen funktioniert. Ich habe mit diesem Problem viele Skalierbarkeitsprobleme bekommen.

Gibt es eine praktikable Problemumgehung, die es nicht erfordert, dass ich viele temporäre, aber sehr reale Tabellen verwalten muss oder eine riesige Tabelle mit allen darin enthaltenen Daten verwalten muss?

Mit freundlichen Grüßen, Kris

[zusätzliche]

Die GROUP_CONCAT Antwort in meiner Situation nicht funktioniert, weil meine Bedingungen mehrere Spalten in einer bestimmten Reihenfolge sind, wäre es OPs machen aus dem, was ich ANDs sein müssen . Jedoch half es mir, ein früheres Problem zu lösen, so dass jetzt die Tabelle, temp oder nicht, nicht mehr benötigt wird. Wir dachten nur zu allgemein für unser Problem. Die gesamte Anwendung von Filtern wurde nun von einer Minute auf gut eine Viertelsekunde zurückgebracht.

+1

Ich hatte das gleiche Problem mit einer temporären Tabelle zweimal in der gleichen Abfrage mit UNION. –

Antwort

32

Richtig, die MySQL docs sagen: "Sie können nicht mehr auf eine TEMPORARY Tabelle in derselben Abfrage verweisen."

Hier ist eine alternative Abfrage, die die gleichen Zeilen finden sollte, obwohl alle Bedingungen der übereinstimmenden Zeilen nicht in separaten Spalten sind, werden sie in einer durch Kommas getrennten Liste sein.

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

Dies löste mein eigentliches Problem nicht wirklich, aber es ermöglichte mir, das Problem, das es verursacht hat, zu vereinfachen und damit die Notwendigkeit für das Versuchbare zu negieren. Vielen Dank! – Kris

3

Persönlich würde ich es nur eine dauerhafte Tabelle machen. Möglicherweise möchten Sie eine separate Datenbank für diese Tabellen erstellen (vermutlich benötigen sie eindeutige Namen, da viele dieser Abfragen gleichzeitig ausgeführt werden können). Außerdem können Berechtigungen sinnvoll festgelegt werden (Sie können Berechtigungen für Datenbanken festlegen. t Legen Sie Berechtigungen für Tabellen-Platzhalter fest.

Dann würden Sie brauchen auch eine Cleanupauftrag alte zu entfernen gelegentlich (MySQL bequem erinnert sich an die Zeit, eine Tabelle erstellt wurde, so dass Sie nur, dass, wenn wurde eine Sanierung erforderlich ist, um herauszufinden verwenden könnte)

+4

Temporäre Tabellen haben den extremen Vorteil, dass mehrere Abfragen gleichzeitig ausgeführt werden können. Dies ist bei permanenten Tabellen nicht möglich. – Pacerier

1

I war in der Lage, die Abfrage in eine permanente Tabelle zu ändern, und dies hat es für mich behoben. (änderte die VLDB-Einstellungen in MicroStrategy, temporärer Tabellentyp).

71

Eine einfache Lösung besteht darin, die temporäre Tabelle zu duplizieren. Funktioniert gut, wenn die Tabelle relativ klein ist, was bei temporären Tabellen häufig der Fall ist.

+5

Sollte eigentlich die gewählte Antwort sein, da dies das Problem beantwortet, ohne herumzugehen. – dyesdyes

+1

ein Hinweis auf * wie * würden Sie die Tabelle duplizieren? (Ich meine eine Art zu kopieren, die Abfrage nicht zu wiederholen) –

+6

Auch wenn die temporäre Tabelle groß ist, sollte der Cache von mysql Ihnen helfen. Was das Kopieren von einer temporären Tabelle in eine andere betrifft, sollte ein einfaches "CREATE TEMPORARY TABLE tmp2 SELECT * FROM tmp1" ausreichen. – AS7K

3

Ich habe es geschafft, indem ich eine permanente "temporäre" Tabelle erstellt habe und die SPID (sorry, ich komme von SQL Server Land) an den Tabellennamen angehängt habe, um einen eindeutigen Tabellennamen zu erstellen. Erstellen Sie dann dynamische SQL-Anweisungen zum Erstellen der Abfragen.Wenn etwas Schlimmes passiert, wird die Tabelle gelöscht und neu erstellt.

Ich hoffe auf eine bessere Option. Komm schon, MySQL Devs. Der 'Bug'/'Feature Request' ist seit 2008 geöffnet! Sieht so aus, als wären alle 'Käfer' im selben Boot.

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; 
+0

Wir hoffen, dass Oracle, nachdem wir die Herrschaft übernommen haben, MySQL einen guten Schub geben kann. – Pacerier

+2

* seufz * Ich bezweifle es :( – beeks

+3

Ein großer _sigh_. Juli 2016, und diese temporäre Tabelle Bug ist immer noch nicht behoben. Ich werde wahrscheinlich mit einer Art Sequenznummer kommen mit einem festen Tabellennamen verkettet (ich bin von Oracle Land), um dieses Problem zu umgehen. – TheWalkingData

Verwandte Themen