DB中位掩码的替代设计

时间:2012-11-06 08:31:11

标签: database oracle database-design oracle11g bitmask

我有一个users表格,格式如下:

users(id,name.....,settings)

settings字段的类型为number,并且包含设置的位掩码。

我必须支持(除其他外)查询,如:

  • 找到所有拥有setting1,setting23,setting125
  • 的用户

今天这样的查询看起来像:

 select * from users where bit_and(settings,2^1+2^23+2^125) = 2^1+2^23+2^125

当然它不是一个完美的实现,但它已经很多时候以这种方式工作。

问题在于,今天我们有126个不同的设置,它正是按位运算的oracle 11g的限制。这意味着我们不能再添加新设置了。

我正在尝试寻找此问题的替代解决方案。 显而易见的方法是设置字段,创建映射表(user - > setting),如:

user_id | setting
  128   |   1
  128   |   23
  128   |   125

但是,上面的查询将如下:

select * 
from users u1 join settings s1 on u1.id = s1.user_id and s1.setting  = 1
              join settings s2 on u1.id = s2.user_id and s2.setting  = 23
              join settings s3 on u1.id = s3.user_id and s3.setting  = 125

看起来不太好......

因此,如果有人可以就此问题提出任何解决方案/方法,那将非常有帮助......

3 个答案:

答案 0 :(得分:3)

Here我对相关问题的回答。

您可以轻松简化查询:

select * 
from  users     u 
join  settings  s 
on    u.id = s1.user_id 
and   s1.setting  in (1, 23, 125)

这会给你一个"或"查询的版本。

select u.userid, sum(s.setting) 
from  users     u 
join  settings  s 
on    u.id = s1.user_id 
and   s.setting  in (1, 23, 125)
group by u.userid
having sum(s.setting) = 149

给你"和"查询的版本。

答案 1 :(得分:1)

您的新设计基本上没问题,但假设“获取给定设置的用户”将是一个主要的查询,您可以通过以下方式对其进行微调......

CREATE TABLE "user" (
    user_id INT PRIMARY KEY
    -- Other fields ...
);

CREATE TABLE user_setting (
    setting INT,
    user_id INT,
    PRIMARY KEY(setting, user_id),
    CHECK (setting BETWEEN 1 AND 125),
    FOREIGN KEY (user_id) REFERENCING "user" (user_id)
) ORGANIZATION INDEX COMPRESS;

注意PRIMARY KEY和ORGANIZATION INDEX COMPRESS子句中字段的顺序:

  • 组织索引将cluster(物理上靠近在一起)具有相同setting的行。
  • COMPRESS将最小化重复setting字段的存储(和缓存!)成本。

然后,您可以将用户连接到任何给定的设置,例如此...

SELECT * FROM "user"
WHERE user_id IN (
    SELECT user_id FROM user_setting
    WHERE setting IN (1, 23, 125)
);

...由于有利的索引和最小化的I / O,这将非常快。

您还可以获得具有全部的给定设置的用户:

SELECT * FROM "user"
WHERE user_id IN (
    SELECT user_id
    FROM user_setting
    WHERE setting IN (1, 23, 125)
    GROUP BY user_id
    HAVING COUNT(setting) = 3
);

对所有设置使用位域使查询难以进行,并且难以针对查询性能进行优化(在旧设计中,每个查询都是表扫描!)。 OTOH,“每个设置的列”设计需要每列单独的索引以获得良好的性能,并且您仍然会有一些不那么优雅的查询。

此外,这些方法不灵活,不像您的新设计可以轻松扩展以接受更多设置或通过添加另一个表并从{{1引用它来存储有关每个设置的附加信息(而不仅仅是数字) }}

答案 2 :(得分:0)

将每个设置存储在自己的列中。