2011-04-06 5 views
4

안녕하세요. 쿼리/PHP 조합을 효율적으로 실행하는 데 문제가 있습니다. 내 PHP에서 내부 루프에 너무 많은 결과 집합을 반복하는 것 같습니다. 이렇게하는 것이 더 효율적인 방법이라고 확신합니다. 어떤 도움이라도 대단히 감사합니다.
rid | recipe_name
레시피에 성분이 포함되어 있는지 확인 - MYSQL

그리고 600 개 가지 성분 ([재료])
iid | i_name

각을 보유하고 다른 테이블 :

나는 3500 개 요리법 ([레시피]) 보유하고 테이블을 가지고있다 조리법에 x 개의 재료가 연결되어 있으며 좋은 조인 테이블을 사용하여 연관성을 만듭니다 ([recipe_ingredients])
uid | rid | iid
01 예를 들어

(UID 테이블 단지 고유 한 ID입니다) 23,516,는 :

rid: 1 | recipe_name: Lemon Tart 
..... 
iid: 99 | i_name: lemon curd 
iid: 154 | i_name: flour 
..... 
1 | 1 | 99 
2 | 1 | 154 

내가 실행하는 데 노력하고있어 쿼리는 사용자들이 가지고있는 성분을 입력 할 수 있습니다, 그것은 것입니다 당신이 그 성분으로 만들 수있는 것을 말해주십시오. 그것은 모든 재료를 사용하지 않아도되지만 조리법에 필요한 모든 재료가 있어야합니다.

예를 들어 밀가루, 계란, 소금, 우유 및 레몬 커드가 있다면 '레몬 케이크'(레몬 타트는 다른 성분이 없다고 가정하면 :)) 만들 수는 없습니다. 'Risotto'(내가 didnt하는 것에 따라 어떤 쌀이나 그것에서 필요로하게되는 그 것이있다).

내 PHP에는 사용자가 가지고있는 모든 성분이 들어있는 배열이 있습니다. 현재 내가 실행하고있는 방식대로 모든 레서피 (루프 1)를 통과 한 다음 해당 레시피의 모든 성분을 확인하여 각 성분이 제 성분 계획 (루프 2)에 포함되어 있는지 확인합니다. 레시피에서 성분을 발견하자마자, 그것은 내 배열에있는 것이 아니며, "아니오"라고 말하고 다음 레시피로갑니다. 그럴 경우, 새로운 배열에 rid를 저장하고 나중에 그 결과를 표시합니다.

그러나 효율성을 보면 3500 개의 레시피가 있고 40 개의 구성 요소가 배열에 포함되어있는 경우 최악의 시나리오는 3500 x 40n을 실행하는 것입니다. 여기서 n은 레시피의 재료 수입니다 . 가장 좋은 경우는 여전히 3500 x 40입니다 (모든 제조법에 대해 재료가 처음 나오지는 않습니다).

내 모든 접근 방식이 잘못되었다고 생각하며 여기에 실종 된 영리한 SQL이 있어야한다고 생각합니다. 이견있는 사람?

select recipes.rid, count(recipe_ingredients.iid) as cnt 
from recipes 
left join recipe_ingredients on recipes.rid = recipe_ingredients.rid 
where recipes_ingredients in any (the,list,of,ingredients,the,user,hash) 
group by recipes.rid 
having cnt > some_threshold_amount 
order by cnt desc 
: 난 항상 ...... 내가 가지고있는 성분 배열에서

덕분에 많은

답변

2
나는 (그것을 빨리는 계산이없는 경우 쿼리를 만들 것입니다 단지 효율성을 위하여, 레시피 테이블의 레시피 성분의 수의 수를 저장하는 게 좋을 것

이 정보는 매번). 이는 데이터 무결성에 좋지 않지만 성능에 좋은 비정규 화입니다. 레시피가 업데이트되고 모든 관련 위치에서 번호가 업데이트되는지주의하지 않으면 데이터 불일치가 발생할 수 있음을인지해야합니다. 나는 여러분이 recipe 테이블에 ing_count로 설정된 새로운 컬럼으로 이것을 수행했다고 가정했습니다.

사용자 입력을 통해 제공된 경우 NAME1, NAME2 등의 값을 이스케이프 처리해야합니다. 그렇지 않으면 SQL 삽입에 위험합니다. 래서 수를 저장하지 않으려면

select recipe.rid, recipe.recipe_name, recipe.ing_count, count(ri) as ing_match_count 
from recipe_ingredients ri 
inner join (select iid from ingredients where i.name='NAME1' or i.name='NAME2' or i.NAME='NAME3') ing 
on ri.iid = ing.iid 
inner join recipe 
on recipe.rid = ri.rid 
group by recipe.rid, recipe.recipe_name, recipe.ing_count 
having ing_match_count = recipe.ing_count 

, 당신은 같은 것을 할 수있는이 같은

select recipe.rid, recipe.recipe_name, count(*) as ing_count, count(ing.iid) as ing_match_count 
from recipe_ingredients ri 
inner join (select iid from ingredients where i.name='NAME1' or i.name='NAME2' or i.NAME='NAME3') ing 
on ri.iid = ing.iid 
right outer join recipe 
on recipe.rid = ri.rid 
group by recipe.rid, recipe.recipe_name 
having ing_match_count = ing_count 
+0

그래, 실제로 레시피의 성분 수를 테이블에 저장하고 정규화 (또는 부족)에주의해야합니다. "ing"생성 된 테이블에서 쿼리를 읽는 것으로부터 완전히 확신 할 수는 없지만 실행하고 알립니다 .... –

+0

ing 테이블의 조인은 재료 목록을 필터링하는 방법입니다. 내부 조인이므로 사용자가 제공하는 목록과 일치하지 않는 모든 요리법이 중단되고 다른 재료에 조제 할 필요가 없습니다. – schizodactyl

+0

다른 곳에서 논의 된 것과 같은 IN ANY 쿼리를 사용할 수 있지만 실행 속도가 중요하다면 쿼리가 동일 할 것이라는 점에 유의하십시오. 대부분 의미가 있습니다. 준비된 문장은 재료의 수가 다양하기 때문에 당신의 경우에 그렇게 유용하지는 않지만 준비된 문장과 함께 사용할 수 없기 때문에 모든 쿼리를 피하는 경향이 있습니다. – schizodactyl

1

당신 수있는 유형의 쿼리 "ANY IN"

감사 사전에 많은 SQL 문을 구축 할 수 있습니다

내 머리 꼭대기에서 벗어나지 만, 기본적으로 사용자가 제공 한 성분 중 적어도 하나가 나열된 조리법을 꺼내 총 성분 수를 기준으로 정렬 한 다음 임계 값을 초과하는 조리법 만 반환합니다 의 성분은 존재한다.

나는 임계 값 비트를 틀리게 잡았을 것입니다. 사용자가 제공 한 재료가 아닌 조리법의 재료를 계산할 것입니다. 그러나 나머지 쿼리는 필요한 것을 시작하기에 충분합니다.

+0

나는 당신이 말하는 것을보고 있습니다. 그러나 나는 카운트 비트의 요점을 이해하지 못합니다. IN ANY는 사용자가 가지고있는 성분 중 적어도 1 개 (3500에서 적은 양으로 줄임)를 가진 것들만 보여주기 위해 초기 레시피를 줄이지 만, 레시피의 재료 양은 실제 재료에 불필요합니다 손에 질문. 레시피에 2 개 또는 12 개의 성분이 들어있는 경우, 다시 확인해야합니다. 그냥 "threshold_amount"로 혼란스러워. –

+0

이 솔루션은 실제로 묻는 질문을 해결하지 않습니다. 물어 보는 사람은 목록에 자신의 성분이 모두 들어있는 요리법 만 원할 것입니다. 그러면 목록에있는 일부 재료가 조리법으로 돌아갑니다. – schizodactyl

+0

@Schizodactyl : 사실, 아니요. 찬장에 20 병의 물건이 있다면, 나는 20 개를 모두 사용하는 요리법을 원하지 않습니다. 적어도 하나, 바람직하게는 3 개 또는 10 개를 사용하는 조리법을 원합니다. 따라서 임계치 수에서 시도하십시오. "나는 다음의 40 가지 성분을 가지고 있으며 적어도 5 가지를 사용하는 조리법을 원합니다." –

0

질문 : SQL 직접 쿼리가 아닌 이유는 무엇입니까?

  • IID |
    • 최초의 제거를 선택 :
    • 가에 의해 재귀 욕심 만들 먼저 당신 사용자 성분보다 더 ingridients이 조리법을 제거

      • : 당신은 잘못된 조리법을 제거하여 최적화 할 수 있습니다 사용자 성분에있는 경우
      • 이 아닌 경우 Recipe_Ingredients 테이블에서 rid => new_table 인 모든 행을 제거하십시오.
      • new_table 사용 | 그것은 가장 통계 결과 있어야 0

    = NEW_TABLE 카운트를 멈춘다.

    도움이 희망
  • 0

    뭔가 : 그것은 이해하기가 매우 쉽다

    SELECT r.*, COUNT(ri.iid) AS count FROM recipe r 
        INNER JOIN recipe_ingredient ri ON r.rid = ri.rid 
        INNER JOIN ingredient i ON i.iid = ri.iid 
        WHERE i.name IN ('milk', 'flour') 
        GROUP BY r.rid 
        HAVING count = 2 
    

    합니다. count에는 각 레시피와 일치하는 목록의 성분 수 (우유, 밀가루)가 있습니다. count이 WHERE 절의 성분 수와 일치하면 (이 경우 : 2), 처방을 반환하십시오.

    +0

    실례가 안되면, 내가 가진 모든 재료가 들어있는 요리법 만 반환 할 것입니다. 내 찬장에 40 가지 성분이 들어 있으면 40 가지를 모두 포함하는 요리법을 보여주고 싶지 않지만 40 가지를 만들 수있는 요리법을 보여주고 싶습니다. –

    0
    SELECT irl.ingredient_amount, r . * , i.thumbnail 
    FROM recipes r 
    LEFT JOIN recipe_images i ON (i.recipe_id = r.recipe_id) 
    LEFT JOIN ingredients_recipes_link irl ON (irl.recipe_id = r.recipe_id) 
    WHERE irl.recipe_id 
    IN (
    
    SELECT recipe_id 
    FROM `ingredients_recipes_link` 
    WHERE ingredient_id 
    IN (24, 21, 22) 
    HAVING count(*) =3 
    ) 
    GROUP BY r.recipe_id 
    
    관련 문제