按外部表引用的值的SQL计数出现引用

时间:2016-01-14 17:02:24

标签: sql sql-server self-join

在性能和可维护性方面,最佳方法是计算表中相同值的出现次数,使用对表条目进行分组的相同引用对结果进行分组?

假设我有三个表(为了表示与我正在处理的方案类似的方案,概念已被缩小):

|----------|   |----------------|   |-----------------------------------|
|   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烹饪相同的MEAL,并且每个RECIPE由不同的INGREDIENT_ENTRY组成,通过共享相同的ID_r值在相同的RECIPE中组织。

INGREDIENT_ENTRY。[金额和描述]是VARCHAR(MAX)类型的列,这是必须比较的值。

在示例中,使用(MEAL 1,RECIPE 1)进行查询:

它有3种成分(1,3,8),并分享:

  • 使用RECIPE 2(7,9)的两种成分 - >因此可以在MEAL 2中找到。
  • 使用RECIPE 4(11)的一种成分 - >所以可以在MEAL 3中找到。

结果应该类似于:

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

我正在尝试使用视图来降低SQL复杂性,但是我不能用单个SQL语句来实现它,我想避免来回执行代码(C#)并执行多个查询(例如查询每个成分) ,并使用HashMaps或类似方法协调结果。

请注意,我无法修改数据库结构。

1 个答案:

答案 0 :(得分: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

N.B。问题中的预期输出略有不同,但我认为这个问题可能包含一个拼写错误(状态配方4与膳食3有关,但在样本数据中似乎并非如此)