如何在 MySQL 中更好地使用 Pivot Table 上的逻辑门
How to better use Logic Gates on Pivot Table in MySQL
目标是使用基于数据透视表的逻辑查询数据 table。我想支持 6 个逻辑门,[AND, OR, XOR, NAND, NOR, XNOR]
,这样当用户创建自定义过滤器时,他们可以提供逻辑门和要在逻辑中使用的 tags.id
值。
示例数据:
+----------+
| data |
|----+-----+
| id | ... |
+----+-----+
| s | ... |
| t | ... |
| u | ... |
| v | ... |
| w | ... |
| x | ... |
| y | ... |
| z | ... |
+----+-----+
+---------+
| pivot |
|----+----|
| c1 | c2 |
+----+----+
| t | a |
| t | b |
| t | c |
| u | a |
| u | b |
| v | b |
| v | c |
| w | a |
| w | c |
| x | a |
| y | b |
| z | c |
+----+----+
+----------+
| tags |
+----+-----+
| id | ... |
+----+-----+
| a | ... |
| b | ... |
| c | ... |
| d | ... |
+----+-----+
预期产出:
AND(a, b, c)
= [t]
OR(a, b, c)
= [t, u, v, w, x, y, z]
XOR(a, b, c)
= [u, v, w, x, y, z]
NAND(a, b, c)
= [s, u, v, w, x, y, z]
NOR(a, b, c)
= [s]
XNOR(a, b, c)
= [s, t]
查询表示:
AND(a, b, c)
= SELECT * FROM data WHERE id IN (SELECT c1 FROM pivot WHERE c2='a') AND id IN (SELECT c1 FROM pivot WHERE c2='b') AND id IN (SELECT c1 FROM pivot WHERE c2='c')
OR(a, b, c)
= SELECT * FROM data WHERE id IN (SELECT c1 FROM pivot WHERE c2 IN ('a', 'b', 'c'))
XOR(a, b, c)
= SELECT * FROM data WHERE id IN (SELECT c1 FROM pivot WHERE c2 IN ('a', 'b', 'c')) AND !(id IN (SELECT c1 FROM pivot WHERE c2='a') AND id IN (SELECT c1 FROM pivot WHERE c2='b') AND id IN (SELECT c1 FROM pivot WHERE c2='c'))
NAND(a, b, c)
= SELECT * FROM data WHERE !(id IN (SELECT c1 FROM pivot WHERE c2='a') AND id IN (SELECT c1 FROM pivot WHERE c2='b') AND id IN (SELECT c1 FROM pivot WHERE c2='c'))
NOR(a, b, c)
= SELECT * FROM data WHERE id NOT IN (SELECT c1 FROM pivot WHERE c2 IN ('a', 'b', 'c'))
XNOR(a, b, c)
= SELECT * FROM data WHERE id NOT IN (SELECT c1 FROM pivot WHERE c2 IN ('a', 'b', 'c')) OR (id IN (SELECT c1 FROM pivot WHERE c2='a') AND id IN (SELECT c1 FROM pivot WHERE c2='b') AND id IN (SELECT c1 FROM pivot WHERE c2='c'))
数据目前约为 30K 行,标签约为 1.5K 行。我目前正在使用 100K 行填充此数据透视表 table 以对上述查询进行一些测试。我觉得多个 IN()
语句会使速度变慢。
是否可以使用视图、联接或其他 MySQL 操作对这些查询进行微调?
此外,如果您有更好的结构建议,我会洗耳恭听。我以前曾尝试在数据中使用 JSON 字段来避免使用枢轴 table,但结果非常慢。
EDIT:虽然 OR(...)
和 NOR(...)
查询工作得很好(~80ms),但 AND(...)
查询表现不佳(~ 1300 毫秒)。查看 EXPLAIN
并尝试遵循 MySQL subquery opimization suggestions 使用 DISTINCT ... INNER JOIN
生成更好的单个子查询实际上使事情变得更糟。
我通过测试偶然发现的是,与在纯 MySQL.[=49= 中相比,我可以在 Node 中使用 lodash 的 _.intersection(...)
更快地对多个 id 列表进行交集]
因此,我可以从 MySQL 中提取单独的子查询,然后在 API 本身中使用 lodash 来执行,而不是使用子查询来形成 AND(...)
逻辑交集,然后生成一个列表以在最后的 IN(...)
语句中使用以进行最终过滤。
OR(a, b, c) = [t, u, v, w, x, y, z]
SELECT GROUP_CONCAT(DISTINCT c1)
FROM pivot
WHERE p2 IN ('a', 'b', 'c');
其余的比较乱,所以让我问一下可以存在多少个不同的c1和c2。如果不超过 64,可以对 BIGINT UNSIGNED
.
中的位进行布尔运算
另请参阅数据类型 ENUM
和 SET
。
如果您有 MySQL 8.0,布尔运算符适用于 BLOB
,从而远远超过 64。
VIEWs
是语法糖,不是性能工具。
IN(SELECT ...)
通常效率很低;尝试使用 EXISTS( SELECT 1 ... )
或 JOIN
(或 LEFT JOIN
)来避免它。
AND(a, b, c) = [t]
可以这样实现:
SELECT GROUP_CONCAT(DISTINCT c1)
FROM (
SELECT c1
FROM pivot
WHERE c2 IN ('a', 'b', 'c')
GROUP BY c1
HAVING COUNT(*) = 3 -- the number of items in a,b,c
) AS x ;
注:
c2 IN ('a', 'b', 'c')
可以这样写:
FIND_IN_SET(c2, 'a,b,c')
这可能更容易将您的运算符转换为存储例程。
目标是使用基于数据透视表的逻辑查询数据 table。我想支持 6 个逻辑门,[AND, OR, XOR, NAND, NOR, XNOR]
,这样当用户创建自定义过滤器时,他们可以提供逻辑门和要在逻辑中使用的 tags.id
值。
示例数据:
+----------+
| data |
|----+-----+
| id | ... |
+----+-----+
| s | ... |
| t | ... |
| u | ... |
| v | ... |
| w | ... |
| x | ... |
| y | ... |
| z | ... |
+----+-----+
+---------+
| pivot |
|----+----|
| c1 | c2 |
+----+----+
| t | a |
| t | b |
| t | c |
| u | a |
| u | b |
| v | b |
| v | c |
| w | a |
| w | c |
| x | a |
| y | b |
| z | c |
+----+----+
+----------+
| tags |
+----+-----+
| id | ... |
+----+-----+
| a | ... |
| b | ... |
| c | ... |
| d | ... |
+----+-----+
预期产出:
AND(a, b, c)
=[t]
OR(a, b, c)
=[t, u, v, w, x, y, z]
XOR(a, b, c)
=[u, v, w, x, y, z]
NAND(a, b, c)
=[s, u, v, w, x, y, z]
NOR(a, b, c)
=[s]
XNOR(a, b, c)
=[s, t]
查询表示:
AND(a, b, c)
=SELECT * FROM data WHERE id IN (SELECT c1 FROM pivot WHERE c2='a') AND id IN (SELECT c1 FROM pivot WHERE c2='b') AND id IN (SELECT c1 FROM pivot WHERE c2='c')
OR(a, b, c)
=SELECT * FROM data WHERE id IN (SELECT c1 FROM pivot WHERE c2 IN ('a', 'b', 'c'))
XOR(a, b, c)
=SELECT * FROM data WHERE id IN (SELECT c1 FROM pivot WHERE c2 IN ('a', 'b', 'c')) AND !(id IN (SELECT c1 FROM pivot WHERE c2='a') AND id IN (SELECT c1 FROM pivot WHERE c2='b') AND id IN (SELECT c1 FROM pivot WHERE c2='c'))
NAND(a, b, c)
=SELECT * FROM data WHERE !(id IN (SELECT c1 FROM pivot WHERE c2='a') AND id IN (SELECT c1 FROM pivot WHERE c2='b') AND id IN (SELECT c1 FROM pivot WHERE c2='c'))
NOR(a, b, c)
=SELECT * FROM data WHERE id NOT IN (SELECT c1 FROM pivot WHERE c2 IN ('a', 'b', 'c'))
XNOR(a, b, c)
=SELECT * FROM data WHERE id NOT IN (SELECT c1 FROM pivot WHERE c2 IN ('a', 'b', 'c')) OR (id IN (SELECT c1 FROM pivot WHERE c2='a') AND id IN (SELECT c1 FROM pivot WHERE c2='b') AND id IN (SELECT c1 FROM pivot WHERE c2='c'))
数据目前约为 30K 行,标签约为 1.5K 行。我目前正在使用 100K 行填充此数据透视表 table 以对上述查询进行一些测试。我觉得多个 IN()
语句会使速度变慢。
是否可以使用视图、联接或其他 MySQL 操作对这些查询进行微调?
此外,如果您有更好的结构建议,我会洗耳恭听。我以前曾尝试在数据中使用 JSON 字段来避免使用枢轴 table,但结果非常慢。
EDIT:虽然 OR(...)
和 NOR(...)
查询工作得很好(~80ms),但 AND(...)
查询表现不佳(~ 1300 毫秒)。查看 EXPLAIN
并尝试遵循 MySQL subquery opimization suggestions 使用 DISTINCT ... INNER JOIN
生成更好的单个子查询实际上使事情变得更糟。
我通过测试偶然发现的是,与在纯 MySQL.[=49= 中相比,我可以在 Node 中使用 lodash 的 _.intersection(...)
更快地对多个 id 列表进行交集]
因此,我可以从 MySQL 中提取单独的子查询,然后在 API 本身中使用 lodash 来执行,而不是使用子查询来形成 AND(...)
逻辑交集,然后生成一个列表以在最后的 IN(...)
语句中使用以进行最终过滤。
OR(a, b, c) = [t, u, v, w, x, y, z]
SELECT GROUP_CONCAT(DISTINCT c1)
FROM pivot
WHERE p2 IN ('a', 'b', 'c');
其余的比较乱,所以让我问一下可以存在多少个不同的c1和c2。如果不超过 64,可以对 BIGINT UNSIGNED
.
另请参阅数据类型 ENUM
和 SET
。
如果您有 MySQL 8.0,布尔运算符适用于 BLOB
,从而远远超过 64。
VIEWs
是语法糖,不是性能工具。
IN(SELECT ...)
通常效率很低;尝试使用 EXISTS( SELECT 1 ... )
或 JOIN
(或 LEFT JOIN
)来避免它。
AND(a, b, c) = [t]
可以这样实现:
SELECT GROUP_CONCAT(DISTINCT c1)
FROM (
SELECT c1
FROM pivot
WHERE c2 IN ('a', 'b', 'c')
GROUP BY c1
HAVING COUNT(*) = 3 -- the number of items in a,b,c
) AS x ;
注:
c2 IN ('a', 'b', 'c')
可以这样写:
FIND_IN_SET(c2, 'a,b,c')
这可能更容易将您的运算符转换为存储例程。