我有 3 张桌子:
- 食谱:
- 身份证,姓名
- 成分:
- 身份证,姓名
- 配方成分:
- id、recipeId、componentId、数量
每次,客户创建一个新配方时,我都需要检查recipeingredient
表格以验证该配方是否存在。如果ingredientId
和quantity
完全相同,我会告诉客户配方已经存在。由于我需要检查多行,因此需要帮助来编写此查询。
我有 3 张桌子:
每次,客户创建一个新配方时,我都需要检查recipeingredient
表格以验证该配方是否存在。如果ingredientId
和quantity
完全相同,我会告诉客户配方已经存在。由于我需要检查多行,因此需要帮助来编写此查询。
了解您的成分和数量后,您可以执行以下操作:
select recipeId as ExistingRecipeID
from recipeingredient
where (ingredientId = 1 and quantity = 1)
or (ingredientId = 8 and quantity = 1)
or (ingredientId = 13 and quantity = 1)
group by recipeId
having count(*) = 3 --must match # of ingeredients in WHERE clause
我最初认为以下查询会找到具有完全相同成分的食谱对:
select ri1.recipeId, ri2.recipeId
from RecipeIngredient ri1 full outer join
RecipeIngredient ri2
on ri1.ingredientId = ri2.ingredientId and
ri1.quantity = ri2.quantity and
ri1.recipeId < ri2.recipeId
group by ri1.recipeId, ri2.recipeId
having count(ri1.id) = count(ri2.id) and -- same number of ingredients
count(ri1.id) = count(*) and -- all r1 ingredients are present
count(*) = count(ri2.id) -- all r2 ingredents are present
但是,此查询无法正确计算事物,因为不匹配项没有正确的 id 对。唉。
以下确实进行了正确的比较。它在连接之前计算每个配方中的成分,因此可以在所有匹配的行上比较这个值。
select ri1.recipeId, ri2.recipeId
from (select ri.*, COUNT(*) over (partition by recipeid) as numingredients
from @RecipeIngredient ri
) ri1 full outer join
(select ri.*, COUNT(*) over (partition by recipeid) as numingredients
from @RecipeIngredient ri
) ri2
on ri1.ingredientId = ri2.ingredientId and
ri1.quantity = ri2.quantity and
ri1.recipeId < ri2.recipeId
group by ri1.recipeId, ri2.recipeId
having max(ri1.numingredients) = max(ri2.numingredients) and
max(ri1.numingredients) = count(*)
该having
子句保证每个食谱的成分数量相同,并且匹配成分的数量是总数。这一次,我在以下数据上进行了测试:
insert into @recipeingredient select 1, 1, 1
insert into @recipeingredient select 1, 2, 10
insert into @recipeingredient select 2, 1, 1
insert into @recipeingredient select 2, 2, 10
insert into @recipeingredient select 2, 3, 10
insert into @recipeingredient select 3, 1, 1
insert into @recipeingredient select 4, 1, 1
insert into @recipeingredient select 4, 3, 10
insert into @recipeingredient select 5, 1, 1
insert into @recipeingredient select 5, 2, 10
如果你有一个新的配方,你可以修改这个查询,只在一个表(比如 ri1)中使用on
子句上的附加条件来查找配方。
如果将成分放在临时表中,则可以用新表替换其中一个表,例如 ri1。
您可以尝试这样的方法来查找是否有重复项:
-- Setup test data
declare @recipeingredient table (
id int not null primary key identity
, recipeId int not null
, ingredientId int not null
, quantity int not null
)
insert into @recipeingredient select 1, 1, 1
insert into @recipeingredient select 1, 2, 10
insert into @recipeingredient select 2, 1, 1
insert into @recipeingredient select 2, 2, 10
-- Actual Query
if exists (
select *
from @recipeingredient old
full outer join @recipeingredient new
on old.recipeId != new.recipeId -- Different recipes
and old.ingredientId = new.ingredientId -- but same ingredients
and old.quantity = new.quantity -- and same quantities
where old.id is null -- Match not found
or new.id is null -- Match not found
)
begin
select cast(0 as bit) as IsDuplicateRecipe
end
else begin
select cast(1 as bit) as IsDuplicateRecipe
end
由于这实际上只是在搜索重复项,因此您可能希望替换一个临时表或为“新”表传递一个表变量。这样您就不必在进行搜索之前插入新记录。您还可以插入基表,将整个事物包装在事务中并根据结果回滚。