107

我目前正忙于实现一个过滤器,我需要为每个“标签”生成一个 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。但是,它确实帮助我解决了早期的问题,因此现在不再需要该表,无论是否为临时表。对于我们的问题,我们只是想得太笼统了。过滤器的整个应用现在已经从大约一分钟缩短到不到四分之一秒。

4

8 回答 8

143

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

于 2013-01-28T10:27:59.517 回答
51

对,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>;
于 2008-12-07T06:03:11.813 回答
7

我通过创建一个永久的“临时”表并将 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;
于 2014-05-03T06:30:34.143 回答
4

如果切换到 MariaDB(MySQL 的一个分支)是可行的——从 10.2.1 版开始,这个烦恼就在那里得到解决:https ://jira.mariadb.org/browse/MDEV-5535 。

于 2020-05-25T05:31:53.877 回答
3

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

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

于 2008-12-07T07:55:12.773 回答
1

您可以通过创建一个永久表(之后将其删除)来解决它,或者只使用相同的数据创建 2 个单独的临时表

于 2018-12-08T07:27:27.580 回答
1

在此处输入图像描述

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

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

于 2020-01-27T19:32:25.720 回答
0

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

于 2009-06-19T17:44:15.210 回答