使用 SQLite 进行位掩码分组
Posted
技术标签:
【中文标题】使用 SQLite 进行位掩码分组【英文标题】:Bitmask grouping using SQLite 【发布时间】:2012-02-20 13:56:58 【问题描述】:当每个表都有一个用于保存位掩码枚举的字段时,我有一个 sqlite 表。
我想执行一个查询,它将返回所有结果并将所有枚举分组到一个字段中:
服务:TEXT 名称、INTEGER 服务、INTEGER 掩码
SELECT service,XXX FROM Services GROUP BY service
基本上,我希望 XXX 是每个服务的所有掩码的位掩码 OR (|) 的结果。
数据:
'a1',1,1
'a2',1,2,
'a3',1,4,
'a4',1,8,
'b1',2,1,
'b2',2,3,
'b3',2,2
因此,我想得到以下行:
1,15 (1|2|4|8)
2,3 (1|3|2)
谢谢
编辑: 在我最初的问题中,我忘了提到每个掩码不是单个位,而是可以是多个位的掩码(我已更改第二个示例以反映这一点)。
【问题讨论】:
我认为在你的例子中你的意思是结果显示 2,3 因为它是基于你的例子的 (1|2|2) 你是对的 - 谢谢。我已经解决了。 您是否提前知道所有可能使用的位图掩码值?即 1, 2, 4, ... 4096? 【参考方案1】:SQLite 支持自定义聚合函数;根据您的设置,您可以register a custom function 轻松完成此操作。使用 C API:
void bitwise_or_step(sqlite3_context *context, int argc, sqlite3_value** argv)
int *buffer = (int *)sqlite3_aggregate_context(context, sizeof(int));
int x = sqlite3_value_int(argv[0]);
*buffer |= x;
void bitwise_or_final(sqlite3_context *context)
int *buffer = (int *)sqlite3_aggregate_context(context, sizeof(int));
sqlite3_result_int(context, *buffer);
sqlite3_create_function_v2(db, "BITWISE_OR", 1, SQLITE_ANY, NULL,
NULL, bitwise_or_step, bitwise_or_final, NULL);
然后在你的 SQL 中,你应该能够做到:
SELECT service,BITWISE_OR(mask) FROM Services GROUP BY service
如果你使用 php,你也可以define a custom aggregate function from PHP。
【讨论】:
【参考方案2】:SQL 中整数的位掩码 OR (|) 操作很简单,只需将每个键的 2 个值的唯一幂相加即可:
todd$ sqlite3 ex1
SQLite version 3.7.5
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table Services(Name varchar(100), Service int, mask int);
sqlite> insert into Services values("a1", 1, 1);
sqlite> insert into Services values("a2", 1, 2);
sqlite> insert into Services values("a3", 1, 4);
sqlite> insert into Services values("a4", 1, 8);
sqlite> insert into Services values("b1", 2, 1);
sqlite> insert into Services values("b2", 2, 3);
sqlite> insert into Services values("b3", 2, 2);
sqlite> select * from Services;
a1|1|1
a2|1|2
a3|1|4
a4|1|8
b1|2|1
b2|2|3
b3|2|2
编辑:当您知道位图域时,您可以将值分成其部分并求和:
sqlite> select Service, max(mask&1) + max(mask&2) + max(mask&4) + max(mask&8) from Services group by Service;
1|15
2|3
sqlite>
您可以为存储在位图掩码中的 2 的所有已知幂扩展 max(mask&bit) 逻辑。
【讨论】:
谢谢,但是当我的掩码为 7 时,此解决方案将不起作用 @Gilad 7 不是 2 的幂,所以我不遵循您的逻辑。基于您的数据集的假设是每行将包含每行一个位图值。当行具有组合位图时,您仍然可以使用仅 SQL 方法,但使用 bitand 检查等会更加精细。需要更好的样本数据集来说明。 谢谢你的例子。您的解决方案确实只能使用 SQL,但在我的应用程序中,我更喜欢注册一个自定义函数(根据 @benzado 的建议),因为它最终会使 SQL 查询更短且更具可读性。 @Gilad 不用担心;仅 SQL 的方法也适用于其他数据库,因为几年前我在 Oracle 解决方案中使用该概念来应用角色为位图的安全模型。以上是关于使用 SQLite 进行位掩码分组的主要内容,如果未能解决你的问题,请参考以下文章