DB中笛卡尔积的作用

学习过数据库和SQL知识的程序员都应该知道笛卡尔积是什么,就是将两张表中数据进行组合然后返回。我一直觉得笛卡尔积没啥鸟用,只是学术上的东西,直到通宵加班时,遇到一个祖传系统的需求。

祖传系统中包含了一个表,里面是群组信息。需求中需要新增一个表,保存群组和角色key以及角色是否启用的标识。

简化后的表结构如下:

mysql> describe t_group;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(45) | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+

mysql> describe t_group_roles;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| group_id | int(11)     | NO   | PRI | NULL    |       |
| role_key | varchar(45) | NO   | PRI | NULL    |       |
| is_open  | tinyint(4)  | NO   |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+

t_group_roles中包含了群组id,角色key和是否启用的标识,群组id和角色key构成联合主键。

测试人员插入了一条测试数据,根据业务规则每个群组对应的角色启用标识默认都是相同的,developer、cie、admin和sl都为1,表示启用,其他角色默认不启用。如下:

mysql> select * from t_group_roles;
+----------+-----------+---------+
| group_id | role_key  | is_open |
+----------+-----------+---------+
|        1 | admin     |       1 |
|        1 | ba        |       0 |
|        1 | cie       |       1 |
|        1 | cmo       |       0 |
|        1 | developer |       1 |
|        1 | mde       |       0 |
|        1 | member    |       0 |
|        1 | pd        |       0 |
|        1 | QA        |       0 |
|        1 | sl        |       1 |
+----------+-----------+---------+

现在需要开发人员做的时,根据这个规则,将所有的group对应的数据都插入t_group_roles 表中。

实现这个需求有多种方法,最容易想到的就是写Java代码或者存储过程,获取所有群组信息,然后根据角色启用规则构造SQL语句,insert到t_group_roles中。

但是这个场景如果使用笛卡尔积,一个SQL就能实现。

首先构造查询语句,select from t_group,t_group_roles 即可使笛卡尔积构造出每个群组及其对应的默认启用规则,由于存在联合主键,排除掉t_group_roles的群组即可,SQL语句如下:

SELECT
  a.id,
  c.role_key,
  c.is_open
FROM t_group a,
  (SELECT
     b.role_key,
     b.is_open
   FROM t_group_roles b
   WHERE b.group_id = 1) c -- 获取角色启用规则
WHERE NOT exists(SELECT 1
                 FROM t_group_roles d
                 WHERE d.group_id = a.id); -- 不进行重复插入

查询出的部分数据如下:可以看到,id为1的群组不会参与构建,因为这个群组已经包含在t_group_roles中了。

此时只需要再构建一个insert语句即可:

insert into  t_group_roles(group_id, role_key, is_open)
SELECT
  a.id,
  c.role_key,
  c.is_open
FROM t_group a,
  (SELECT DISTINCT
     b.role_key,
     b.is_open
   FROM t_group_roles b
   WHERE b.group_id = 1) c -- 获取角色启用规则
WHERE NOT exists(SELECT 1
                 FROM t_group_roles d
                 WHERE d.group_id = a.id); -- 不进行重复插入

执行后的效果:select * from t_group_roles;

并且只要t_group_roles中group_id等于1的数据不被删除,这个SQL就是正确的,可以在群组中新增了数据之后运行,为新增群组添加默认的角色启用规则(实际上不会这样使用,反正规则是写死的,为group表加个after的triger即可)。

results matching ""

    No results matching ""