尝试仅 return 与 table 不同的 ID 条目,其中必须匹配特定列的所有值
Trying to only return distinct ID entries from a table where all values of a certain column must be matched
所以情况是我有 3 个 Table,其中 2 个是“助手”table,而另一个是主要 table 我正在尝试从中获取不同的 ID:
Main Table dbo.recipes 包含 ID、Name 和其他一些列,例如:
ID NAME
5 Veggie Cassola
6 Mozzarella Penne
7 Wiener Schnitzel with Fries
8 Grilled Salmon with Rice
9 Greek style Salad
帮手是dbo.stock:
ID_USER ID_INGREDIENT
1 225
1 585
1 607
1 643
1 763
1 874
1 937
1 959
1 960
2 225
2 246
2 331
2 363
2 511
2 585
和dbo.content:
ID_INGREDIENT ID_RECIPE
98 5
196 5
333 5
607 5
608 5
613 5
627 5
643 5
763 5
874 5
951 5
956 5
225 6
585 6
607 6
基本上 dbo.stock 是用户拥有的成分清单,因此用户 ID 和成分 ID。
dbo.content是做某道菜所需要的原料。
我想查询的只是用户实际拥有其配料的食谱,因此这意味着应返回所有配料与(特定用户)匹配的所有食谱。我目前的程序代码如下:
SELECT * FROM [dbo].[recipe]
WHERE [recipe].[id] NOT IN
(SELECT DISTINCT [content].[id_recipe] FROM [dbo].[content]
WHERE [content].[id_ingredient] NOT IN
(SELECT [stock].[id_ingredient] FROM [dbo].[stock]
WHERE [stock].[id_user] = @userID))
这可行,但我怀疑这是实现此目标的最佳方法。有没有更好的方法达到同样的效果?
MS SQL 服务器 Express 2019
基本上,您想要查找内容中没有库存成分的所有食谱。这不是你用英语思考它的方式,但如果你在 SQL:
中那样思考它就会导致这个
DECLARE @userID int = 1;
SELECT ID, NAME
FROM dbo.recipe AS r
WHERE NOT EXISTS
(
SELECT id_ingredient FROM dbo.content WHERE id_recipe = r.ID
EXCEPT
SELECT id_ingredient FROM dbo.stock WHERE id_user = @userID
);
但是,此查询更符合您的要求,只是没有上述两个计划 (EXCEPT
is sneaky like that) 中的昂贵 DISTINCT
s,因此可能是最佳选择:
DECLARE @userID int = 1;
SELECT ID, NAME
FROM dbo.recipe AS r
WHERE NOT EXISTS
(
SELECT 1 FROM dbo.content AS c
WHERE id_recipe = r.ID AND NOT EXISTS
(
SELECT 1 FROM dbo.stock
WHERE id_ingredient = c.id_ingredient
AND id_user = @userID
)
);
这是一道经典的Relational Division With Remainder题。
@AaronBertrand 为您提供了几个很好的解决方案。这是另一个经常使用的。
DECLARE @userID int = 1;
SELECT
r.Id,
r.Name
FROM dbo.recipe AS r
JOIN dbo.content AS c ON c.id_recipe = r.ID
LEFT JOIN dbo.stock AS s ON s.id_ingredient = c.id_ingredient
AND s.id_user = @userID
GROUP BY
r.Id,
r.Name
HAVING COUNT(*) = COUNT(s.id_ingredient);
这会将所有内容连接在一起(左连接 stock
),仅按 recipe
和 return 分组-null stock
行。也就是说,每一个content
都必须匹配,并且必须至少有一个content
.
有一个语义上的差异:如果你还想要所有 recipes
有 no `content,你可以稍微改变它。
DECLARE @userID int = 1;
SELECT
r.Id,
r.Name
FROM dbo.recipe AS r
LEFT JOIN dbo.content AS c ON c.id_recipe = r.ID
LEFT JOIN dbo.stock AS s ON s.id_ingredient = c.id_ingredient
AND s.id_user = @userID
GROUP BY
r.Id,
r.Name
HAVING COUNT(c.id_recipe) = COUNT(s.id_ingredient);
所以情况是我有 3 个 Table,其中 2 个是“助手”table,而另一个是主要 table 我正在尝试从中获取不同的 ID:
Main Table dbo.recipes 包含 ID、Name 和其他一些列,例如:
ID NAME
5 Veggie Cassola
6 Mozzarella Penne
7 Wiener Schnitzel with Fries
8 Grilled Salmon with Rice
9 Greek style Salad
帮手是dbo.stock:
ID_USER ID_INGREDIENT
1 225
1 585
1 607
1 643
1 763
1 874
1 937
1 959
1 960
2 225
2 246
2 331
2 363
2 511
2 585
和dbo.content:
ID_INGREDIENT ID_RECIPE
98 5
196 5
333 5
607 5
608 5
613 5
627 5
643 5
763 5
874 5
951 5
956 5
225 6
585 6
607 6
基本上 dbo.stock 是用户拥有的成分清单,因此用户 ID 和成分 ID。 dbo.content是做某道菜所需要的原料。
我想查询的只是用户实际拥有其配料的食谱,因此这意味着应返回所有配料与(特定用户)匹配的所有食谱。我目前的程序代码如下:
SELECT * FROM [dbo].[recipe]
WHERE [recipe].[id] NOT IN
(SELECT DISTINCT [content].[id_recipe] FROM [dbo].[content]
WHERE [content].[id_ingredient] NOT IN
(SELECT [stock].[id_ingredient] FROM [dbo].[stock]
WHERE [stock].[id_user] = @userID))
这可行,但我怀疑这是实现此目标的最佳方法。有没有更好的方法达到同样的效果?
MS SQL 服务器 Express 2019
基本上,您想要查找内容中没有库存成分的所有食谱。这不是你用英语思考它的方式,但如果你在 SQL:
中那样思考它就会导致这个DECLARE @userID int = 1;
SELECT ID, NAME
FROM dbo.recipe AS r
WHERE NOT EXISTS
(
SELECT id_ingredient FROM dbo.content WHERE id_recipe = r.ID
EXCEPT
SELECT id_ingredient FROM dbo.stock WHERE id_user = @userID
);
但是,此查询更符合您的要求,只是没有上述两个计划 (EXCEPT
is sneaky like that) 中的昂贵 DISTINCT
s,因此可能是最佳选择:
DECLARE @userID int = 1;
SELECT ID, NAME
FROM dbo.recipe AS r
WHERE NOT EXISTS
(
SELECT 1 FROM dbo.content AS c
WHERE id_recipe = r.ID AND NOT EXISTS
(
SELECT 1 FROM dbo.stock
WHERE id_ingredient = c.id_ingredient
AND id_user = @userID
)
);
这是一道经典的Relational Division With Remainder题。
@AaronBertrand 为您提供了几个很好的解决方案。这是另一个经常使用的。
DECLARE @userID int = 1;
SELECT
r.Id,
r.Name
FROM dbo.recipe AS r
JOIN dbo.content AS c ON c.id_recipe = r.ID
LEFT JOIN dbo.stock AS s ON s.id_ingredient = c.id_ingredient
AND s.id_user = @userID
GROUP BY
r.Id,
r.Name
HAVING COUNT(*) = COUNT(s.id_ingredient);
这会将所有内容连接在一起(左连接 stock
),仅按 recipe
和 return 分组-null stock
行。也就是说,每一个content
都必须匹配,并且必须至少有一个content
.
有一个语义上的差异:如果你还想要所有 recipes
有 no `content,你可以稍微改变它。
DECLARE @userID int = 1;
SELECT
r.Id,
r.Name
FROM dbo.recipe AS r
LEFT JOIN dbo.content AS c ON c.id_recipe = r.ID
LEFT JOIN dbo.stock AS s ON s.id_ingredient = c.id_ingredient
AND s.id_user = @userID
GROUP BY
r.Id,
r.Name
HAVING COUNT(c.id_recipe) = COUNT(s.id_ingredient);