解决MySQL“无法重新打开表”错误


88

我目前正在忙于实现一种过滤器,我需要为每个要过滤的“标签”生成一个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答案在我的情况下不起作用,因为我的条件是以特定顺序排列的多个列,这会使OR超出我需要的AND范围。但是,它确实帮助我解决了一个较早的问题,因此现在不再需要临时表或临时表。我们只是认为对我们的问题过于笼统。过滤器的整个应用现在已经从大约一分钟恢复到不到四分之一秒。


2
我在使用UNION的同一查询中两次使用临时表时遇到相同的问题。
塞巴斯蒂安·格里戈诺利

Answers:



122

一个简单的解决方案是复制临时表。如果表相对较小,则效果很好,临时表通常是这种情况。


8
实际上应该是选择的答案,因为这可以解决问题,而不会四处走动。
dysdyes 2014年

4
关于如何复制表格的任何建议?(我的意思是不复制重复所述查询的方式)
埃尔南Eche

16
即使临时表很大,mysql的缓存也应该可以帮助您。至于从一个临时表复制到另一个临时表,应该执行一个简单的“ CREATE TEMPORARY TABLE tmp2 SELECT * FROM tmp1”。
AS7K

2
如果您复制临时内容,请不要忘记也创建索引,否则查询可能会很慢。
gaborsch

1
@NgSekLong是的。每时每刻。显然,它取决于查询的应用程序,但是直到> 100,000,我才看不到“巨大的”性能问题。在一个ETL过程中,我将这种方法与350万张表一起使用。该应用程序的速度并不那么重要。
Tanner Clark

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
这实际上并不能解决我的问题,但是确实使我能够简化导致它的问题,从而消除了对临时表的需求。谢谢!
克里斯(Kris)

6

我通过创建一个永久的“临时”表并将SPID(很抱歉,我来自SQL Server)添加到表名,以创建唯一的表名,从而解决了这个问题。然后创建动态SQL语句以创建查询。如果发生任何不良情况,该表将被删除并重新创建。

我希望有一个更好的选择。来吧,MySQL开发人员。自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接管Oracle,她可以给MySQL一个很好的推动力。
Pacerier,2015年

2
叹息,我对此表示怀疑:(
贝克

3
一个大。2016年7月,此临时表错误仍未修复。为了避免这个问题,我可能会想出一种与永久表名(我来自Oracle领域)连接的序列号。
TheWalkingData '16

Hattrick叹了口气……它可能永远都不会被修复,因为它已经变成了2019年。
Zimano

3

就我个人而言,我只是使其成为永久性桌子。您可能想为这些表创建一个单独的数据库(由于它们可以一次完成许多查询,因此它们将需要唯一的名称),还允许合理地设置权限(您可以设置数据库的权限;您可以'设置表通配符的权限)。

然后,您还需要清理作业以偶尔删除旧的作业(MySQL方便地记住创建表的时间,因此您可以使用它来计算需要清理的时间)


9
临时表的最大优势是可以同时运行多个查询。永久表是不可能的。
Pacerier

我认为永久表“解决方案”不是解决方案。它可以肯定地解决问题,但是不切实际。出现了这么多问题:如何同时创建多个?您将如何处理命名约定并覆盖相同的命名表?删除永久表的过程是什么?如果您在回答这些问题时能详细说明使用永久表的可行解决方案,我会非常高兴!
Tanner Clark

0

我能够将查询更改为永久表,这对我来说已经解决了。(更改了MicroStrategy中临时表类型的VLDB设置)。


-1

您可以通过以下方法解决此问题:制作一个永久表,然后将其删除;或者仅制作两个具有相同数据的临时表


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.