我有一张MODULES表(总共60个模块)
身份证 | 姓名 |
---|---|
0 | A |
1 | B |
2 | C |
3 | D |
和表 EVENTS (有更多列,但对于问题来说是不必要的)
身份证 | 模块_已使用 |
---|---|
0 | 1 |
1 | 15 |
2 | 8 |
3 | 3 |
表 EVENTS 有几百万行。
MODULES.ID 的值代表模块在 MODULES_USED 整数中的位位置(从 0 开始),因此,二进制 15 为 1111,表示模块 0、1、2、3 已被使用。同样,MODULES_USED 的值为 3 表示 0011,这意味着模块 0 和 1 已被使用。同样,1 表示 0001(模块 0),8 表示 0100(模块 2)。
我需要一个输出视图 MODULES_USED (这样我就可以创建柱形图),如下所示:
身份证 | 姓名 | TIMES_USED |
---|---|---|
0 | A | 3 |
1 | B | 2 |
2 | C | 1 |
3 | D | 2 |
我想要实现的是关于 MODULES_USED 表的报告,最终用户可以通过 EVENTS 表的其他列(ID_COUNTRY、ID_Product...)进行筛选。
我尝试过的一个可能的解决方案是使用以下 SQL 查询的视图
SELECT
`MODULES`.`ID` AS `ID`,
`MODULES`.`NAME` AS `NAME`,
(
SELECT
COUNT(`EVENTS`.`MODULES_USED`)
FROM
`EVENTS`
WHERE
(
(
`EVENTS`.`MODULES_USED` > 0
) AND(
(
FLOOR(
(
`EVENTS`.`MODULES_USED` / POW(2, `MODULES`.`ID`)
)
) % 2
) > 0
)
)
) AS `TIMES_USED`,
FROM
`MODULES`
但这有两个主要问题:
速度很慢
除非修改视图,否则不允许过滤掉事件,例如,如果我想按 ID_Product(事件中的另一个字段)进行过滤,我必须编辑视图以在 WHERE 中添加 ID_PRODUCT = 1,但事实并非如此对报告的用户有用。另一种方法是在视图中使用不同的过滤条件创建额外的列(总计一列,每种产品一列,每个国家/地区一列,产品和国家/地区的每种组合一列...最终会产生数百列和执行时间(我在结束之前杀死了它)。
我想到的另一种方法是创建 EVENTS 表的视图,在其中为每个模块添加一列,但除了现在的 60 个之外,将来可能会增加到数百个,所以我不知道如何自动/以编程方式执行此操作,以及如何随后将其组合到转置的视图中。
我不认为通过视图可以实现你所需要的。而且,我不确定你为什么想要这样做。
首先将聚合移至派生表中:
SELECT m.ID, m.NAME, IFNULL(SUM(e.num), 0) AS TIMES_USED
FROM MODULES m
LEFT JOIN (
SELECT MODULES_USED, COUNT(*) AS num
FROM `EVENTS`
GROUP BY MODULES_USED
) e ON e.MODULES_USED & POW(2, m.ID)
GROUP BY m.ID;
即使没有
EVENTS.MODULES_USED
上的索引,这也应该表现得更好。当向内部查询添加过滤器(ID_COUNTRY
、ID_PRODUCT
等)时,理想情况下您需要一个覆盖过滤条件并以 MODULES_USED
结尾的索引来满足 GROUP BY
。添加索引来满足每种可能的组合显然是不可行的,因此您需要弄清楚最重要的用例是什么。