4

给定 4 个表,每个表包含项目并代表一组,如何获得绘制维恩图所需的每个隔间中的项目数,如下所示。计算应在 MySQL 服务器中进行,避免将项目传输到应用程序服务器。

示例表:

s1:         s2:         s3:         s4:
+------+    +------+    +------+    +------+
| item |    | item |    | item |    | item |
+------+    +------+    +------+    +------+
| a    |    | a    |    | a    |    | a    |
+------+    +------+    +------+    +------+
| b    |    | b    |    | b    |    | c    |
+------+    +------+    +------+    +------+
| c    |    | c    |    | d    |    | d    |
+------+    +------+    +------+    +------+
| d    |    | e    |    | e    |    | e    |
+------+    +------+    +------+    +------+
| ...  |    | ...  |    | ...  |    | ...  |

现在,我想我会计算一些设定的权力。一些与Ito s1IIto s2IIItos3IVto相对应的例子s4

四维维恩图 - 使用椭圆由 4 组组成的维恩图

如果我重新解释sx为一个集合,我会写:

  1. |s1 ∩ s2 ∩ s3 ∩ s4|- 中间的白色 25
  2. |(s1 ∩ s2 ∩ s4) \ s3|- 右下方相对于中心的白色 15
  3. |(s1 ∩ s4) \ (s2 ∪ s3)|- 底部的白色 5
  4. |s1 \ (s2 ∪ s3 ∪ s4)|- 蓝底深蓝60
  5. ……直到 15 点。

如何在 MySQL 服务器上有效地计算这些权力?MySQL 是否提供了辅助计算的函数?

一种天真的方法是运行 1 的查询。

SELECT count(*) FROM(
SELECT item FROM s1
INTERSECT
SELECT item FROM s2
INTERSECT
SELECT item FROM s3
INTERSECT
SELECT item FROM s4);

以及对 2 的另一个查询。

SELECT count(*) FROM(
SELECT item FROM s1
INTERSECT
SELECT item FROM s2
INTERSECT
SELECT item FROM s4
EXCEPT
SELECT item FROM s3);

以此类推,产生 15 个查询。

4

3 回答 3

2

尝试这样的事情:

with universe as (
    select * from s1 
    union
    select * from s2
    union
    select * from s3
    union
    select * from s4
),
regions as (
    select
        case when s1.item is null then '0' else '1' end
        ||
        case when s2.item is null then '0' else '1' end
        ||
        case when s3.item is null then '0' else '1' end
        ||
        case when s4.item is null then '0' else '1' end as Region
    from universe u
    left join s1 on u.item = s1.item
    left join s2 on u.item = s2.item
    left join s3 on u.item = s3.item
    left join s4 on u.item = s4.item
)
select Region, count(*) from regions group by Region

免责声明:我只在 SQLite 中对此进行了测试。您可能需要SET sql_mode='PIPES_AS_CONCAT'ANSI 字符串连接才能在 MySQL 中工作,或者改用该concat函数。WITH仅从 MySQL 8.0 版开始支持该语法,但您可以适当地使用临时表或嵌套查询。

如果集合非常大,您可能希望item在查询之前对列进行索引,以防 SQL 优化器无法自行解决。

于 2018-11-16T23:07:37.577 回答
1

这个问题有点复杂,所以答案是。让我解释一下KT的答案

with universe as (
    select * from s1 
    union
    select * from s2
    union
    select * from s3
    union
    select * from s4
),
regions as (
    select
        case when s1.item is null then '0' else '1' end
        ||
        case when s2.item is null then '0' else '1' end
        ||
        case when s3.item is null then '0' else '1' end
        ||
        case when s4.item is null then '0' else '1' end as Region
    from universe u
    left join s1 on u.item = s1.item
    left join s2 on u.item = s2.item
    left join s3 on u.item = s3.item
    left join s4 on u.item = s4.item
)
select Region, count(*) from regions group by Region

universe所有表的 UNION 中的结果(消除了重复项),例如

+------+
| item |
+------+
| a    |
+------+
| b    |
+------+
| c    |
+------+
| d    |
+------+
| e    |
+------+
| ...  |
+------+

然后,连接 s1、s2、s3 和 s4

+------+---------+---------+---------+---------+
| item | s1.item | s2.item | s3.item | s4.item |
+------+---------+---------+---------+---------+
| a    | a       | a       | a       | a       |
+------+---------+---------+---------+---------+
| b    | b       | b       | b       | NULL    |
+------+---------+---------+---------+---------+
| c    | c       | c       | NULL    | c       |
+------+---------+---------+---------+---------+
| d    | d       | NULL    | d       | d       |
+------+---------+---------+---------+---------+
| e    | NULL    | e       | e       | e       |
+------+---------+---------+---------+---------+
| ...  | ...     | ...     | ...     | ...     |
+------+---------+---------+---------+---------+

并转换为二进制字符串(0:如果单元格为 NULL;1:否则)调用Region其中第一个数字对应于 s1,第二个对应于 s2,依此类推

+------+--------+
| item | Region |
+------+--------+
| a    | 1111   |
+------+--------+
| b    | 1110   |
+------+--------+
| c    | 1101   |
+------+--------+
| d    | 1011   |
+------+--------+
| e    | 0111   |
+------+--------+
| ...  | ...    |
+------+--------+

最后按Region聚合分组

+--------+-------+
| Region | count |
+--------+-------+
| 1111   | 1     |
+--------+-------+
| 1110   | 1     |
+--------+-------+
| 1101   | 1     |
+--------+-------+
| 1011   | 1     |
+--------+-------+
| 0111   | 1     |
+--------+-------+
| ...    |       |
+--------+-------+

请注意,其中包含 0 个集合元素的区域不会显示在结果中,并且0000永远不会出现(=项目不是任何集合 s1、s2、s3、s4 的一部分),因此有 15 个区域。

具有二进制表示的区域的 4 组维恩图

于 2018-11-25T20:26:08.117 回答
0

以下程序:

  1. 创建了一个存储过程,该过程创建包含集合的临时内存表。
  2. 请注意,MySQL 不允许您在查询中多次引用临时内存表。
  3. 如前所述,MySQL 没有INTERSECTor EXCEPT。但是你可以模仿他们。通过从原始数据/原始集中删除重复项,可以更加简化仿真。
  4. 决定将计算值存储到每个变量中,并输出一个包含所有 15 个与组件相对应的值的表。

我想出的目前是https://gist.github.com/Rillke/c2da0921f8f2a047615f41fab8781c11

于 2018-11-14T10:26:43.370 回答