2016-01-14 4 views
0

Каков наилучший подход с точки зрения производительности и ремонтопригодности для подсчета количества вхождений одного и того же значения в таблицу, группировка результатов с той же ссылкой, которая группирует записи стол?SQL count вхождения значений, сгруппированных по внешним таблицам, ссылки

Скажем, у меня есть три таблицы (понятия были усаживается, чтобы представить сценарий, который похож на тот, над которым я работаю):

|----------| |----------------| |-----------------------------------| 
| MEAL | |  RECIPE | |   INGREDIENT_ENTRY   | 
|----------| |----------------| |-----------------------------------| 
| ID | ... | | ID | ID_m | ...| | ID | ID_r | amount and description| 
|----------| |----------------| |-----------------------------------| 
| 1 | ... | | 1 | 1 | ...| | 1 | 1 |  '15gr of yeast' | 
| 2 | ... | | 2 | 2 | ...| | 2 | 4 |    '2 eggs' | 
| 3 | ... | | 3 | 3 | ...| | 3 | 1 |  '300cl of water' | 
| 4 | ... | | 4 | 4 | ...| | 4 | 2 |  '300cl of beer' | 
|----------| | 5 | 1 | ...| | 5 | 3 |  '250cl of milk' | 
       | 6 | 4 | ...| | 6 | 5 | '100gr of biscuits' | 
       | 7 | 5 | ...| | 7 | 2 |  '15gr of yeast' | 
       | 8 | 6 | ...| | 8 | 1 |  '500gr of flour' | 
       |----------------| | 9 | 2 |  '500gr of flour' | 
            | 10 | 2 |  '10gr of salt' | 
            | 11 | 4 |  '15gr of yeast' | 
            |-----------------------------------| 

То же ЕДА можно приготовить с другим RECIPE, и каждый RECIPE состоит из разных INGREDIENT_ENTRY, организованных в одном и том же RECIPE, путем использования одного и того же значения ID_r.

INGREDIENT_ENTRY. [Количество и описание] - столбец типа VARCHAR (MAX), это значение, которое необходимо сравнить.

В примере, что делает запрос с (MEAL 1, RECIPE 1):

Он имеет 3 ингредиенты (1,3,8), а акции:

  • Два ингредиенты с RECIPE 2 (7,9) -> и так можно найти в ЕДЫ 2
  • Один ингредиент с РЕЦЕПТ 4 (11) -> и так можно найти в ЕДЫ 3

Результат должен выглядеть примерно так:

|------| |--------| |-------| 
| MEAL | | RECIPE | | COUNT | 
|------| |--------| |-------| 
| 2 | |  2 | |  2 | 
| 4 | |  4 | |  1 | 
|------| |--------| |-------| 

Я экспериментирую с представлениями, чтобы уменьшить сложность SQL, но я не могу сделать это с помощью одного оператора SQL, и я хотел бы избегать повторения кода (C#) и выполнять несколько запросов (например, запрос для каждого ингредиента, и примирить результаты с HashMaps или аналогичными).

Пожалуйста, обратите внимание, что я не могу изменить структуру БД.

+0

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

+0

@ jae555 Я не могу вносить изменения в структуру БД, я отредактирую вопрос, чтобы уточнить это, спасибо. – BeerBaron

ответ

0

Вы можете найти общие ингредиенты, используя EXISTS. В ниже я просто использовал общее табличное выражение, так что я не должен выписывать стыки более чем один раз, чтобы вернуться к еде ID:

DECLARE @SelectedMealID INT = 1; 

WITH LinkedData AS 
(
    SELECT MealID = r.ID_m, 
      RecipeID = r.ID, 
      Ingredient = i.[amount and description] 
    FROM RECIPE AS r 
      INNER JOIN INGREDIENT_ENTRY AS i 
       ON i.ID_r = r.ID 
) 
SELECT a.MealID, 
     a.RecipeID, 
     CommonIngedients = COUNT(*) 
FROM LinkedData AS a 
WHERE a.MealID != @SelectedMealID 
AND  EXISTS 
     ( SELECT 1 
      FROM LinkedData AS b 
      WHERE b.Ingredient = a.Ingredient 
      AND  b.MealID = @SelectedMealID 
     ) 
GROUP BY a.MealID, a.RecipeID; 

Я испытал это с ниже образца:

-- GENERATE TABLES AND DATA 
DECLARE @Meal TABLE (ID INT); 
INSERT @Meal (ID) VALUES (1), (2), (3), (4); 

DECLARE @Recipe TABLE (ID INT, ID_m INT); 
INSERT @Recipe (ID, ID_m) 
VALUES (1, 1), (2, 2), (3, 3), (4, 4), (5, 1), (6, 4), (7, 5), (8, 6); 

DECLARE @Ingredient TABLE (ID INT, ID_r INT, AmountAndDescription VARCHAR(MAX)); 
INSERT @Ingredient (ID, ID_R, AmountAndDescription) 
VALUES 
    (1, 1, '15gr of yeast'), (2, 4, '2 eggs'), 
    (3, 1, '300cl of water'), (4, 2, '300cl of beer'), 
    (5, 3, '250cl of milk'), (6, 5, '100gr of biscuits'), 
    (7, 2, '15gr of yeast'), (8, 1, '500gr of flour'), 
    (9, 2, '500gr of flour'), (10, 2, '10gr of salt'), 
    (11, 4, '15gr of yeast'); 


-- TEST QUERY 
DECLARE @SelectedMealID INT = 1; 

WITH LinkedData AS 
(
    SELECT MealID = r.ID_m, 
      RecipeID = r.ID, 
      Ingredient = i.AmountAndDescription 
    FROM @Recipe AS r 
      INNER JOIN @Ingredient AS i 
       ON i.ID_r = r.ID 
) 
SELECT a.MealID, 
     a.RecipeID, 
     CommonIngedients = COUNT(*) 
FROM LinkedData AS a 
WHERE a.MealID != @SelectedMealID 
AND  EXISTS 
     ( SELECT 1 
      FROM LinkedData AS b 
      WHERE b.Ingredient = a.Ingredient 
      AND  b.MealID = @SelectedMealID 
     ) 
GROUP BY a.MealID, a.RecipeID; 

ВЫВОД

MealID RecipeID CommonIngedients 
------------------------------------------ 
2  2   2 
4  4   1 

NB Ожидаемый результат в вопросе отличается немного, но я думаю, что вопрос может содержать опечатку (состояния Рецепт 4 относится к муке 3, но это не похоже на случайные данные)

+0

Да, была опечатка, извините ... В попытке перенести мой сценарий на эту упрощенную структуру я пропустил это, спасибо. Позвольте мне адаптировать ваше решение к моей среде, чтобы убедиться, что оно может поместиться ... – BeerBaron

+0

Спасибо, это именно то, что я искал. – BeerBaron