2015-02-12 4 views
0

Я охота на лучший способ реализации модели данных для «рецептов»модели данных для уникальных наборов

думать, как приложение для пиццы, где вы можете составить свою собственную пиццу. вы выбираете, возможно, 5 из 100 ингредиентов, и вы выбираете сумму для каждого. Мне нужно проверить, видел ли я раньше эту комбинацию пиццы, присваиваю идентификатор, если нет, и получить идентификатор, если у меня есть.

У нас есть n ингредиенты.

Рецепт определяется набором ингредиентов и соответствующей суммой.

Может выглядеть следующим образом:

Ingr1 90
Ingr2 10

или
Ingr1 90
Ingr2 10
Ingr3 10

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

Я хочу хранимую процедуру, которая принимает набор данных как параметр и возвращает идентификатор, который является новым, если рецепт был неизвестным и существующим, если рецепт уже существует.

Я ищу наиболее эффективный способ сделать это. Моя лучшая идея до сих пор заключается в том, чтобы либо закодировать рецепт как строку (json), либо использовать это как уникальное ограничение, либо иметь хранимую процедуру, которая выполняет итерацию через набор данных рецептов и строит инструкцию n уровня, если существует.

Итак, я уверен, что смогу решить проблему, но ищу красивый метод.

ответ

1

Насколько я могу судить, у вас есть отношения между рецептами и ингредиентами и M: M между ними. Модель данных может выглядеть следующим образом (PK выделены жирным шрифтом):

Рецепт (RecipeID, RecipeName)

Ингредиент (IngredientID, IngredientName)

RecipeIngredients (RecipeID, IngredientID, Сумма)

Вы можете решить задачу выяснить, присутствует ли тот же рецепт в базе данных с использованием запроса, но th запрос будет непростым. Это хорошо известная проблема, реляционное деление. Существует несколько подходов. Один из самых популярных - подсчет. Если какой-то рецепт имеет такое же количество ингредиентов, как и целевое, и все ингредиенты одинаковы, тогда они равны. Такие запросы часто включают в себя агрегацию данных и выполняют не очень быстро на большом количестве данных.

Вы можете помочь решить эту проблему со стороны приложения, и вы думаете в правильном направлении. Представьте рецепт в виде строки, указав значения IngredientID (чтобы получить ту же строку, даже если ингредиенты были добавлены в другом порядке), конвертируя Amount в некоторую стабильную форму (не для того, чтобы получить 0.499999 вместо 0.5), вычислите некоторый хэш из строки и сохраните это значение в Рецепте. В простой форме хеш представляет собой целочисленное значение, поэтому вы можете найти удваивается очень быстро.

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

Пример запроса (новый рецепт в #tmp):

;with totals as 
(
    select RecipeID, count(*) totals 
    from RecipeIngredients 
    group by RecipeID 
), matched_totals as 
(
    select i.RecipeID, count(*) matched_totals 
    from RecipeIngredients i 
    join #tmp t 
     on i.IngredientID = t.IngredientID 
     and i.Amount = t.Amount 
    group by i.RecipeID 
) 
select t.* 
from totals t 
    join matched_totals m 
    on m.RecipeID = t.RecipeID 
where 
    totals = matched_totals 
    and totals = (select count(*) from #tmp) 

Это решение более элегантный, но гораздо менее интуитивным:

select * 
from Recipe r 
where 
    not exists 
    (select 1 
     from RecipeIngredients ri 
     where 
     r.RecipeID = ri.RecipeID 
     and not exists 
     (select 1 from #tmp t where t.IngredientID = ri.IngredientID) 
    )  
+0

Спасибо - Я рад видеть, что я не обойдена простое и элегантное решение :) –

+0

В реляционных базах данных не так много вариантов. – Alsin

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