私は3つのテーブルを持っています:
- レシピ:
- ID、名前
- 成分:
- ID、名前
- レシピ成分:
- ID、レシピ ID、材料 ID、数量
顧客が新しいレシピを作成するたびに、recipeingredient
テーブルをチェックして、このレシピが存在するかどうかを確認する必要があります。ingredientId
とがまったく同じである場合quantity
は、レシピが既に存在することを顧客に伝えます。複数の行をチェックする必要があるため、このクエリを作成するには助けが必要です。
私は3つのテーブルを持っています:
顧客が新しいレシピを作成するたびに、recipeingredient
テーブルをチェックして、このレシピが存在するかどうかを確認する必要があります。ingredientId
とがまったく同じである場合quantity
は、レシピが既に存在することを顧客に伝えます。複数の行をチェックする必要があるため、このクエリを作成するには助けが必要です。
私は当初、次のクエリで、まったく同じ材料を含むレシピのペアを見つけることができると考えていました。
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
新しいレシピがある場合は、このクエリを変更して、on
句に追加の条件を使用して、テーブルの 1 つ (ri1 など) でレシピを検索するだけにすることができます。
材料を一時テーブルに配置する場合、これらのテーブルの 1 つ (ri1 など) を新しいテーブルに置き換えることができます。
材料と量がわかれば、次のようなことができます。
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
重複があるかどうかを確認するには、次のような方法を試してください。
-- 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
これは実際には重複を検索するだけなので、一時テーブルを置き換えるか、「新しい」テーブルの代わりにテーブル変数を渡すことをお勧めします。これにより、検索を行う前に新しいレコードを挿入する必要がなくなります。また、ベーステーブルに挿入し、トランザクションですべてをラップして、結果に基づいてロールバックすることもできます。