2008-12-05 19 views
61

Actualmente estoy ocupado implementando un tipo de filtro para el que necesito generar un clausse INNER JOIN para cada "etiqueta" para filtrar.Moverse por MySQL Error "No se puede volver a abrir la tabla"

El problema es que después de un montón de SQL, tengo una tabla que contiene toda la información que necesito para hacer mi selección, pero lo necesito de nuevo para cada INTERIOR generada JOIN

Básicamente, esto se parece a:

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 

Esto funciona pero me gusta mucho más la tabla de "búsqueda" para ser temporal (que puede ser varios órdenes de magnitud más pequeñas si no es una tabla normal) pero eso me da un error muy molesto: Can't reopen table

Algunos La investigación me lleva al this bug report pero a la gente de MySQL no parece importarle que una característica tan básica (usar una tabla más de una vez) no funcione con tablas temporales. Me estoy enfrentando a muchos problemas de escalabilidad con este problema.

¿Existe alguna solución viable que no requiera que administre potencialmente muchas tablas temporales pero muy reales o me haga mantener una tabla enorme con todos los datos en ella?

respecto

cordiales, Kris

[adicional]

La respuesta GROUP_CONCAT no funciona en mi situación porque mis condiciones son varias columnas en un orden específico, tendría OR fuera de lo que necesito para ser AND . Sin embargo, sí me ayudó a resolver un problema anterior, por lo que ahora la tabla, temporal o no, ya no es necesaria. Estábamos pensando demasiado genérico para nuestro problema. Toda la aplicación de filtros ahora se ha recuperado de alrededor de un minuto a menos de un cuarto de segundo.

+1

Tuve el mismo problema al usar una tabla temporal dos veces en la misma consulta usando UNION. –

Respuesta

32

Derecho, el MySQL docs dice: "No se puede hacer referencia a una tabla TEMPORARY más de una vez en la misma consulta".

Aquí hay una consulta alternativa que debe encontrar las mismas filas, aunque todas las condiciones de las filas coincidentes no estarán en columnas separadas, estarán en una lista separada por comas.

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

Esto en realidad no resolvió mi problema, pero me permitió simplificar el problema que lo causaba, anulando la necesidad de lo tentable. ¡Gracias! – Kris

3

Personalmente, simplemente lo convertiría en una mesa permanente. Es posible que desee crear una base de datos separada para estas tablas (presumiblemente necesitarán nombres únicos ya que muchas de estas consultas se pueden hacer a la vez), también para permitir que los permisos se establezcan de manera sensata (Puede establecer permisos en bases de datos; t establecer permisos sobre comodines de tabla).

A continuación, también se necesitaría una trabajo de limpieza para eliminar los viejos de vez en cuando (MySQL convenientemente recuerda el momento en que se crea una tabla, por lo que sólo podría utilizar eso para hacer ejercicio cuando se requería una limpieza)

+4

Las tablas temporales tienen la ventaja extrema de que puede tener múltiples consultas ejecutándose simultáneamente. Esto no es posible con tablas permanentes. – Pacerier

1

I Pude cambiar la consulta a una tabla permanente y esto me solucionó. (cambió la configuración de VLDB en MicroStrategy, tipo de tabla temporal).

71

Una solución simple es duplicar la tabla temporal. Funciona bien si la tabla es relativamente pequeña, lo que a menudo es el caso con tablas temporales.

+5

En realidad, debería ser la respuesta elegida ya que esto responde al problema, sin dar vueltas. – dyesdyes

+1

algún consejo sobre * cómo * ¿duplicarías la tabla? (Me refiero a una forma de copiar no repetir la consulta) –

+6

Incluso si la tabla temporal es grande, la memoria caché de mysql debería ayudarlo. En cuanto a copiar de una tabla temporal a otra, un simple "CREATE TEMPORARY TABLE tmp2 SELECT * FROM tmp1" debería hacerlo. – AS7K

3

Lo solucioné creando una tabla "temporal" permanente y añadiendo el sufijo SPID (lo siento, procedo de SQL Server) al nombre de la tabla, para crear un nombre de tabla único. A continuación, crear declaraciones SQL dinámicas para crear las consultas.Si algo malo sucede, la mesa se descartará y se volverá a crear.

Estoy esperando una mejor opción. C'mon, MySQL Devs. ¡El 'error'/'solicitud de función' ha estado abierto desde 2008! Parece que todos los 'bichos' encontrados se encuentran en el mismo barco.

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

Esperemos que ahora que Oracle está tomando las riendas, ella pueda dar un buen empujón a MySQL. – Pacerier

+2

* suspiro * Lo dudo :( – beeks

+3

Un gran _sigh_. Julio de 2016, y este error en la tabla temporal aún no se ha solucionado. Probablemente se me ocurra algún tipo de número de secuencia concatenado con un nombre de tabla permanente (soy de Oracle) tierra) para eludir este problema. – TheWalkingData

Cuestiones relacionadas