使用MySQL中的bitfield列将行分解为多行

时间:2011-12-15 14:47:57

标签: mysql sql bit-manipulation

我有一个名为warnings的bitfield存储为int。 我想获得每个人的警告列表

假设我们有一张桌子

NAME,  WARNINGS
alex,  0
mike,  5
sarah, 2

其中整数的每个位对应一个警告#(位位置)。 目前这是在带有for循环的perl中完成的

for(my $i=0; $i < $warning_size;$i++ ){
     if( (1 << $i ) & $warning != 0){
         print "$name\t" . $i+1 ."\n";
     }
}

有没有办法可以通过mysql查询处理这个问题。

对于上面的例子,我想要以下输出:

name, warning
-------------
mike  1
mike  3
sarah 2

我试图将其归结为一个选择语句,

由于

4 个答案:

答案 0 :(得分:2)

试试这个 -

CREATE TABLE warn_bits(
  b INT(11) NOT NULL
);

INSERT INTO warn_bits VALUES 
  (1),
  (2),
  (3),
  (4),
  (5),
  (6),
  (7),
  (8);

SELECT w.name, wb.b FROM warnings w
  JOIN warn_bits wb
    ON ((w.WARNINGS >> wb.b - 1) & 1) > 0
ORDER BY w.name, wb.b;

+-------+---+
| name  | b |
+-------+---+
| mike  | 1 |
| mike  | 3 |
| sarah | 2 |
+-------+---+

您可以扩展warn_bits表以支持INT或BIGINT数字。


<强> EDIT2

SELECT w.name, wb.b FROM warnings w
  JOIN (
  SELECT 1 b UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION
  SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8
  ) wb
    ON ((w.WARNINGS >> wb.b - 1) & 1) > 0
ORDER BY w.name, wb.b;

答案 1 :(得分:0)

制作临时表以进行压碎。

mysql> create table bitcrush( bit int );
Query OK, 0 rows affected (0.02 sec)

mysql> insert into bitcrush values (1),(2),(4),(8),(16),(32),(64),(128),(256);
Query OK, 8 rows affected (0.01 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> select name, log2(bit)+1 as warn from c821885 join bitcrush on warn & bit order by name;
+-------+------+
| name  | warn |
+-------+------+
| mike  |    1 |
| mike  |    3 |
| sarah |    2 |
+-------+------+
3 rows in set (0.00 sec)

答案 2 :(得分:0)

我认为你只是在寻找power(2, x);,试试这个: -

mysql> select length(conv(0, 10, 2));
+------------------------+
| length(conv(0, 10, 2)) |
+------------------------+
|                      1 |
+------------------------+
1 row in set (0.00 sec)

mysql> select length(conv(2, 10, 2));
+------------------------+
| length(conv(2, 10, 2)) |
+------------------------+
|                      2 |
+------------------------+
1 row in set (0.00 sec)

mysql> select length(conv(5, 10, 2));
+------------------------+
| length(conv(5, 10, 2)) |
+------------------------+
|                      3 |
+------------------------+
1 row in set (0.00 sec)

答案 3 :(得分:0)

如果您确定每个警告都有一列,您还可以使用export_set()和substring()来分解这些位。

我假设您正在使用4位。

bit 1 = "fatal"
bit 2 = "warning"
bit 3 = "error"
bit 4 = "user error"
SELECT
name,
EXPORT_SET(warning,1,0,'',4) AS debugBits,
SUBSTRING(EXPORT_SET(warning,1,0,'',4),1,1) AS hasFatal,
SUBSTRING(EXPORT_SET(warning,1,0,'',4),2,1) AS hasWarning,
SUBSTRING(EXPORT_SET(warning,1,0,'',4),3,1) AS hasError,
SUBSTRING(EXPORT_SET(warning,1,0,'',4),4,1) AS hasUserError
FROM warnings;

应该输出如下内容:

------|----------|---------|-----------|---------|-------------|
name  |debugBits | hasFatal| hasWarning| hasError| hasUserError|
alex  |      0000|        0|          0|        0|            0|
mike  |      1010|        1|          0|        1|            0|
sarah |      0010|        0|          0|        1|            0|
------|----------|---------|-----------|---------|-------------|

请注意,export_set()会在最左侧显示第1位。 http://dev.mysql.com/doc/refman/5.5/en/string-functions.html#function_export-set

如果您对警告的CSV列没有问题,可以使用make_set()

SELECT
name,
MAKE_SET(warning,
'fatal',
'warning',
'error',
'userError') AS warningText
FROM warnings

这将为您提供一列警告,其中包含设置位标签的CSV列表。 http://dev.mysql.com/doc/refman/5.5/en/string-functions.html#function_make-set