2009-12-04 2 views
3

Возможно, решение очевидно, но я не могу найти хороший.Сохранение элемента, помеченного многими категориями - битмаскировка?

В моем предстоящем проекте будет одна основная таблица, ее данные будут часто читаться. Скорость обновления/вставки/удаления не является проблемой.

Элементы этой основной таблицы связаны с 4 или более категориями. У товара может быть 50 - 100 или более отношений в пределах одной категории.

Наиболее распространенные операции, которые будут выполнены на базе:

  • выбрать все элементы, которые были назначены на категории A, B, C, ... с LIMIT X, Y
  • подсчета всех элементы, которые были assignged к категории A, B, C, ...

Моя первая мысль о том, как создать базу данных для вышеперечисленного было что-то вроде этого (классический подход, я думаю):

Во-первых, для каждой из четырех категорий, я создаю category таблицу:

id - PK, int(11), index 
name - varchar(100) 

тогда у меня будет один item стол:

id - PK, int(11), index 
... some more data fields, about 30 or so ... 

и соотносить the category таблицы, будет 4 или больше таблиц поиска/ММ:

id_item  - int(11) 
id_category - int(11) 

Запросов выглядели примерно так:

select 
item.* 

from 
item 

inner mm_1 on mm_1.id_item = item.id 
inner join cat_1 on cat_1.id = mm_1.id_category and cat_1.id in (1, 2, ... , 100) 

inner mm_2 on mm_2.id_item = item.id 
inner join cat_2 on cat_2.id = mm_2.id_category and cat_2.id in (50, 51, ... , 90) 

Конечно вышеописанный подход с таблицами ММ будет работать, но, как приложение должно обеспечить очень хорошую SELECT производительности, я тестировал с реальными объемами данных (100000 записей в таблица item, 50 - 80 отношений в каждой категории), но это было не так быстро, как я ожидал, даже с индексами на месте. Я также пытался использовать WHERE EXISTS вместо INNER JOIN при выборе.


Моя вторая идея состояла в том, чтобы просто использовать item таблицу сверху денормализовать данные.

После прочтения this blog post об использовании битмаски я дал ему попробовать и присваивается каждой категории значение бита:

category 1.1 - 1 
category 1.2 - 2 
category 1.3 - 4 
category 1.4 - 8 
... etc ... 

Таким образом, если item был помечен category 1.1 и category 1.3, он имел битовую маску 5, который то я хранится в поле item.bitmask и я могу запросить его следующим образом:

select count(*) from item where item.bitmask & 5 = 5 

Но производительность была не столь велика либо.

Проблемы с этим bitmasking подход: MySQL не использует никаких индексов, когда битовые операторы участвуют и даже тогда, когда item.bitmask будет иметь тип BIGINT я могу только обрабатывать до 64 отношений, но мне нужно поддерживать до 100 в категория.


Это было о нем. Я не могу думать ни о чем другом, кроме, может быть, загрязнять таблицу item со многими, многими полями, такими как category_1_1 до category_4_100. Каждый из них содержит либо 1, либо 0. Но это может привести к многим AND в предложении выбора, и это не кажется как хорошая идея.

Итак, какие у меня варианты? Какие-нибудь лучшие идеи?


EDIT: как ответ на Cory Petosky комментарий «Что„Элемент может иметь 50 - 100 или более отношений в рамках одной категории“означают?»:

Чтобы сделать его более конкретным. , таблица item представляет изображение. Изображения относятся к числу других критериев, классифицированных по настроениям (настроение будет одной из 4 категорий). Так это будет выглядеть следующим образом:

Image: 
    - Category "mood": 
     - bright 
     - happy 
     - funny 
     - ... 50 or so more ... 
    - Category "XYZ": 
     - ... 70 or so more ... 

Если мой стол изображение будет класс в C#, она будет выглядеть следующим образом:

public class Image { 
    public List<Mood> Moods; // can contain 0 - 100 items 
    public List<Some> SomeCategory; // can contain 0 - 100 items 
    // ... 
} 
+0

Что такое «У предмета может быть 50 - 100 или более отношений внутри одной категории». имею в виду? Я не понимаю все разные категории, которые вы создаете/ссылаетесь. –

ответ

2

Что об этом (псевдокод):

Item (image) 
    Id   PK, int(11) 
    Name  varchar(100) 

Category (mood, xyz) 
    Id   PK, int(11) 
    Name  varchar(100) 

Relations (happy, funny) 
    Id   PK, int(11) 
    Name  varchar(100) 

ItemCategories 
    Id   PK, int(11) 
    ItemId  FK, int(11) 
    CategoryId FK, int(11) 

ItemCategoryRelations 
    ItemCategoriesId FK, int(11) 
    RelationId  FK, int(11) 

SELECT * 
    FROM Item 
    JOIN ItemCategories ON Item.Id = ItemCategories.ItemId 
WHERE ItemCategories.CategoryId IN (1, 2, ..., 10) 

Ниже версия использует одну таблицу меньше, но не поддерживает категории без отношений, и отношения не могут быть повторно использованы. Таким образом, его только в силе, если соответствует вашим требованиям структуры данных:

Item (image) 
    Id   PK, int(11) 
    Name  varchar(100) 

Category (mood, xyz) 
    Id   PK, int(11) 
    Name  varchar(100) 

Relations (happy, funny) 
    Id   PK, int(11) 
    CategoryId FK, int(11) 
    Name  varchar(100) 

ItemRelations 
    ItemId  FK, int(11) 
    RelationId FK, int(11) 

SELECT * 
    FROM Item 
    JOIN ItemRelations ON Item.Id = ItemRelations.ItemId 
    JOIN Relations ON Relations.Id = ItemRelations.RelationsId 
WHERE Relations.CategoryId IN (1, 2, ..., 10) 
+0

Спасибо за ваш ответ, но я не понимаю. Как выглядит запрос sql? Как выглядят определения таблиц? – Max

+0

@max, отредактированный, пожалуйста, посмотрите –

0

Так что, если я понимаю правильно, изображение попадает в одну из четырех ваших основных категорий ... настроения, например. Тогда в настроении его можно связать с «ярким» и «счастливым». и так далее.

Хотя я абсолютно обожаю битмаскировать (программист микропроцессора здесь днем), и, хотя мне всегда нравится использовать его для дизайна db, всегда кажется, что это лучший способ.

Как насчет этого.

tblItems 
------------------ 
    item_id 
    item_name 

tblCategories 
------------------ 
    category_id 
    category_name 

tblRelations 
------------------ 
    relation_id 
    relation_name 

tblCategoryRelationLink (link relations to specific categories) 
------------------ 
    cat_rel_id 
    category_id 
    relation_id 

tblItemRelationLink (set relations to items) 
------------------ 
    item_rel_id 
    item_id 
    rel_id 

Если ваши отношения относятся только к категориям ...., то вы можете просто найти, к какой категории относится конкретное отношение. Если каким-то образом у вас может быть отношение, связанное с двумя категориями, вам также понадобится дополнительная таблица (чтобы связать элемент с категорией).

+0

извините, выложили до того, как я прочитал ответ ruben ... я думаю, что обе наши реализации довольно схожи .... в любом случае битовая маска может не стоить того для этого случая! – espais

+1

отличные мысли думают одинаково =) –

1

Как насчет этого; каждая категория может иметь родительскую категорию. В вашем примере, если bright является ребёнком mood, тогда ссылка на объект bright автоматически сделает это mood\bright. alt text http://www.damirsystems.com/dp_images/itemcategory_model_01.png

Смежные вопросы