MySQL - 查询将整数按位字段拆分为每个位的布尔列

时间:2014-06-26 16:47:42

标签: mysql sql query-optimization

给出以下示例数据:

mysql> select * from test2;
+-------+--------+
| name  | status |
+-------+--------+
| bob   |      0 |
| jim   |      2 |
| karen |      5 |
| jane  |      1 |
| roy   |      7 |
+-------+--------+

我想提出一个查询,它会将状态列的值拆分为整数的每个位的伪布尔值列。

我现在要做的是让应用程序生成一个查询,该查询使用case语句和每列的按位和运算符来获取结果,如下所示:

mysql> select 
    ->     name, 
    ->     (case when (status & 1) then 'yes' else 'no' end) as bit1, 
    ->     (case when (status & 2) then 'yes' else 'no' end) as bit2, 
    ->     (case when (status & 4) then 'yes' else 'no' end) as bit4, 
    ->     (case when (status & 8) then 'yes' else 'no' end) as bit8 
    -> from test2;
+-------+------+------+------+------+
| name  | bit1 | bit2 | bit4 | bit8 |
+-------+------+------+------+------+
| bob   | no   | no   | no   | no   |
| jim   | no   | yes  | no   | no   |
| karen | yes  | no   | yes  | no   |
| jane  | yes  | no   | no   | no   |
| roy   | yes  | yes  | yes  | no   |
+-------+------+------+------+------+
5 rows in set (0.00 sec)

现在这种方法很有效,并且因为没有子查询或连接而有效,但它不是很优雅。

我想要一个更优雅的解决方案来动态地执行此操作,而无需创建case语句并测试每一位。

这是只有几位的示例数据,但在实现中,将会有数万行,还有更多可以设置的位。

加成:

将列的生成(列名和测试)基于包含每个位定义的表,如下所示:

mysql> select * from bit_spec;
+-----+------+
| bit | desc |
+-----+------+
|   1 | bit1 |
|   2 | bit2 |
|   4 | bit3 |
|   8 | bit4 |
+-----+------+

目前,应用程序处理这个问题,并不是要求MySQL服务器完成此处理的硬性要求,但如果是的话,这将是有利的。

这里是包含示例数据的SQL Fiddle,示例位定义表和我当前的解决方案。

1 个答案:

答案 0 :(得分:2)

这可以通过将每个用户/位/值组合检索到行中然后将这些行旋转到列中来实现。

select 
  name, 
  `desc`,
  ((status & bit) > 0) value
from test2, bit_spec

上述查询以下列格式生成结果

name  |  desc  |  value
bob   |  bit1  |  0
bob   |  bit2  |  0

从这里我们可以将结果转换为所需的格式,其中每个desc / value对转换为名为desc的列,其值为value

使用以下帖子https://stackoverflow.com/a/12005676/3574819

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'SUM(case when `desc` = ''',
      `desc`,
      ''' then value end) AS ',
      replace(`desc`, ' ', '')
    )
  ) INTO @sql
from bit_spec;

SET @sql = CONCAT('SELECT t1.name, ', @sql, ' from
(select 
  name, 
  `desc`,
  ((status & bit) > 0) value
from test2, bit_spec) t1
group by t1.name
order by t1.name');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

http://sqlfiddle.com/#!2/5f6d0/22

NAME        BIT1    BIT2    BIT3    BIT4
bob         0       0       0       0
jane        1       0       0       0
jim         0       1       0       0
karen       1       0       1       0
roy         1       1       1       0

<强>旁注

如果你需要按权限搜索(即显示所有拥有bit1&amp; bit2但不是bit3的用户),那么我不建议将你的权限存储为一个整数中的位,因为db不能索引结果。在这种情况下,由列(user_id,permission_id)组成的索引连接表将产生更快的结果。