Группировка битовой маски с использованием SQLite

У меня есть таблица sqlite, когда в каждой таблице есть поле, которое я использую для хранения некоторого перечисления битовой маски.

Я хотел бы выполнить запрос, который вернет мне все результаты и сгруппирует все перечисления в одно поле:

Сервисы: имя TEXT, сервис INTEGER, маска INTEGER

SELECT service,XXX FROM Services GROUP BY service

По сути, я хотел бы, чтобы XXX был результатом битовой маски ИЛИ (|) всех масок для этой службы.

Данные:

'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)

Спасибо

EDIT: В своем первоначальном вопросе я забыл упомянуть, что каждая маска не является одним битом, но может быть маской из нескольких битов (я изменил второй пример, чтобы отразить это).


person Gilad Novik    schedule 27.01.2012    source источник
comment
Я думаю, что в вашем примере вы имели в виду, что результаты показывают 2,3, поскольку это (1 | 2 | 2) на основе вашего примера.   -  person tawman    schedule 27.01.2012
comment
вы правы - спасибо. Я исправил это.   -  person Gilad Novik    schedule 31.01.2012
comment
Знаете ли вы заранее все возможные значения растровых масок, которые будете использовать? то есть 1, 2, 4, ... 4096?   -  person tawman    schedule 31.01.2012


Ответы (2)


SQLite поддерживает пользовательские агрегатные функции; в зависимости от вашей настройки вы можете зарегистрировать пользовательскую функцию, чтобы сделать это довольно легко. Используя 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, вы можете определить пользовательскую агрегатную функцию из PHP тоже.

person benzado    schedule 30.01.2012

Битовая маска ИЛИ (|) с целыми числами в SQL — это просто суммирование уникальной мощности двух значений для каждого ключа:

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> 

Вы можете расширить логику max(mask&bit) для всех известных степеней двойки, которые вы храните в растровой маске.

person tawman    schedule 27.01.2012
comment
спасибо, но, например, когда моя маска равна 7, это решение не сработает - person Gilad Novik; 31.01.2012
comment
@ Гилад 7 не является степенью двойки, поэтому я не следую вашей логике. Предположение, основанное на вашем наборе данных, состоит в том, что каждая строка будет содержать одно значение растрового изображения для каждой строки. Вы по-прежнему можете использовать подход только с SQL, когда строка имеет комбинированное растровое изображение, но это более сложно с проверкой битов и др. Нужен лучший пример набора данных для иллюстрации. - person tawman; 31.01.2012
comment
спасибо за пример. ваше решение действительно может работать только с SQL, но в моем приложении я предпочел зарегистрировать пользовательскую функцию (согласно предложению @benzado), поскольку в конечном итоге это делает SQL-запрос короче и читабельнее. - person Gilad Novik; 31.01.2012
comment
@ Гилад, не беспокойся; подход только к SQL работает и для других баз данных, поскольку я использовал эту концепцию в решении Oracle много лет назад для применения модели безопасности с ролями, представляющими собой растровое изображение. - person tawman; 31.01.2012