绕过MySQL“无法重新打开表”错误

时间:2008-12-05 10:18:34

标签: mysql temp-tables

我目前正在忙于实现一个过滤器,我需要为每个“标签”生成一个INNER JOIN clausse来过滤。

问题是,在一大堆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

有些研究引导我this bug report,但是MySQL的人似乎并不关心这样的基本功能(使用多次表)不适用于临时表。我遇到了很多可伸缩性问题。

有没有可行的解决方法,不需要我管理潜在的大量临时但非常真实的表,或者让我维护一个包含所有数据的大表?

亲切的问候,克里斯

[附加]

GROUP_CONCAT答案在我的情况下不起作用,因为我的条件是特定顺序的多个列,它会使OR成为我需要的AND。但是,它确实帮助我解决了早期的问题,所以现在不再需要表格,不管是不是临时表格。我们只是想对我们的问题过于笼统。过滤器的整个应用现在已经从大约一分钟恢复到大约四分之一秒。

8 个答案:

答案 0 :(得分:101)

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

答案 1 :(得分:42)

对,MySQL docs说:“你不能在同一个查询中多次引用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 :(得分:5)

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

我希望有更好的选择。来吧,MySQL Devs。 'bug'/'功能请求'自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;

答案 3 :(得分:3)

就个人而言,我只是把它变成一张永久的桌子。您可能希望为这些表创建一个单独的数据库(可能它们需要唯一的名称,因为可以一次完成大量这些查询),也允许合理设置权限(您可以设置数据库权限;您可以' t设置表通配符的权限。)

然后你还需要一个清理工作来偶尔删除旧的(MySQL会方便地记住创建表的时间,所以你可以在需要清理时使用它来解决)

答案 4 :(得分:1)

我能够将查询更改为永久表,并为我修复了这个问题。 (更改了MicroStrategy中的VLDB设置,临时表类型)。

答案 5 :(得分:0)

您可以通过制作一个永久表来解决该问题,然后将其删除,或者只制作两个具有相同数据的临时表

答案 6 :(得分:0)

如果切换到MariaDB(MySQL的分支)是可行的-从10.2.1版开始,此烦恼已解决:https://jira.mariadb.org/browse/MDEV-5535

答案 7 :(得分:-1)

enter image description here

以下是有关此问题的MYSQL文档。我使用重复的临时表,例如上面的一些答案,但是,您可能会遇到CTE合适的情况!

https://dev.mysql.com/doc/refman/8.0/en/temporary-table-problems.html