2012-09-25 2 views
2

J'ai 3 tables:SQL Server 2008, comment vérifier si plusieurs enregistrements existent dans la base de données?

  • recette:
      id
    • , nom
  • ingrédient
  • :
      id
    • , nom
  • recipeingredient:
    • id, recipeId, ingredientId, quantité

Chaque fois, un client crée une nouvelle recette, je dois vérifier la table recipeingredient pour vérifier si cette recette existe ou non. Si ingredientId et quantity sont exactement les mêmes, je vais dire au client que la recette existe déjà. Comme j'ai besoin de vérifier plusieurs lignes, j'ai besoin d'aide pour écrire cette requête.

+2

Comment représentez-vous la nouvelle recette? Est-ce dans les mêmes tables, dans les tables temporaires, en mémoire? –

+0

@GordonLinoff Je suppose que c'est un post de formulaire ... mais bonne question. – RedFilter

+0

oui, c'est un poste – qinking126

Répondre

2

Connaître vos ingrédients et les quantités, vous pouvez faire quelque chose comme ceci:

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 
2

Je pensais à l'origine que la requête suivante trouver des paires de recettes qui ont exactement les mêmes ingrédients:

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 

Cependant, cette requête ne compte pas les choses correctement, car les discordances n'ont pas les bonnes paires d'identifiants. Hélas.

Le fait la comparaison correcte. Il compte les ingrédients dans chaque recette avant la jointure, donc cette valeur peut simplement être comparée sur toutes les rangées correspondantes.

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(*) 

La clause having garantit que chaque recette que le même nombre de composants, et que le nombre de composants correspondant est total. Cette fois, je l'ai testé sur les données suivantes:

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 

Si vous avez une nouvelle recette, vous pouvez modifier cette requête à regarder juste pour la recette dans l'une des tables (par exemple ri1) en utilisant une somme supplémentaire condition sur la clause on.

Si vous placez les ingrédients dans une table temporaire, vous pouvez remplacer l'une de ces tables, disons ri1, par la nouvelle table.

+0

Après avoir testé cela, je ne pouvais pas le faire fonctionner. –

+0

@TimLehner. . . Pouvez-vous élaborer sur l'erreur ou le problème avec les données? –

+0

Bien sûr, essayez-le avec les données de ma réponse (qui est essentiellement la même direction que la vôtre). Puis changez une quantité. Ou ajoutez/supprimez une ligne. Votre «groupe par» pourrait effectivement masquer les différences entre les recettes. –

0

Vous pouvez essayer quelque chose comme ça pour trouver si vous avez un double:

-- 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 

Puisque c'est à la recherche vraiment que pour un double, vous pouvez remplacer une pour la table temporaire ou pass a table variable « nouveau » table. De cette façon, vous n'aurez pas à insérer les nouveaux enregistrements avant d'effectuer votre recherche. Vous pouvez également insérer dans les tables de base, envelopper le tout dans une transaction et une restauration basée sur les résultats.

Questions connexes