1

次の (非常に単純化された) mysql テーブル構造があるとします。

製品

  • ID

製品カテゴリ

  • ID
  • 製品番号
  • 状態 (整数)

商品タグ

  • ID
  • 製品番号
  • some_other_numeric_value

特定のproduct_tagに関連付けられており、ステータス属性が1である少なくとも1つのカテゴリとの関係があるすべての製品を見つけようとしています.

次のクエリを試しました。

SELECT *

FROM `product` p

JOIN `product_categories` pc
ON p.`product_id` = pc.`product_id`

JOIN `product_tags` pt
ON p.`product_id` = pt.`product_id`

WHERE pt.`some_value` = 'some comparison value'

GROUP BY p.`product_id`

HAVING SUM( pc.`status` ) > 0

ORDER BY SUM( pt.`some_other_numeric_value` ) DESC

今私の問題は次のとおりSUM(pt.some_other_numeric_value)です。予期しない値が返されます。

問題の製品に product_categories テーブルとの関係が複数ある場合、product_tagsテーブルとのすべての関係が、product_categoriesテーブルとの関係と同じ回数カウントされることに気付きました。

例: id=1 の製品に、ids = 2、3、および 4 の product_categories との関係があり、ids 5 と 6 の product_tags との関係がある場合、a を挿入するGROUP_CONCAT(pt.id)と、5,6,5になります。 ,6,5,6ではなく、予想される5,6

最初は、結合タイプ (左結合、右結合、内部結合など) に問題があるのではないかと疑ったので、知っているすべての結合タイプを試しましたが、役に立ちませんでした。また、句にさらに id フィールドをGROUP BY含めようとしましたが、これでも問題は解決しませんでした。

誰かがここで実際に何がうまくいかないのか説明できますか?

4

2 に答える 2

5

「メイン」( product) テーブルを 2 つのテーブル (tagsおよびcategories) にリレーションシップを介して結合する1:nため、これは予期されることであり、ミニデカルト積を作成しています。複数の関連付けられたタグと複数の関連付けられたカテゴリの両方を持つ製品の場合、結果セットに複数の行が作成されます。グループ化すると、集計関数で間違った結果が得られます。


これを回避する 1 つの方法は、2 つの結合のいずれかを削除することです。これは、そのテーブルからの結果が必要ない場合に有効なスターテジーです。SELECTテーブルのリストには何も必要ないとしproduct_categoriesます。次に、セミ結合を使用できます(EXISTS subquery)そのテーブルへ:

SELECT p.*,
       SUM( pt.`some_other_numeric_value` )

FROM `product` p

JOIN `product_tags` pt
  ON p.`product_id` = pt.`product_id`

WHERE pt.`some_value` = 'some comparison value'

  AND EXISTS
      ( SELECT *
        FROM product_categories pc
        WHERE pc.product_id = pc.product_id
         AND  pc.status = 1
      ) 

GROUP BY p.`product_id`

ORDER BY SUM( pt.`some_other_numeric_value` ) DESC ;

この問題を回避する別の方法は、 - の後に-または集約関数内でGROUP BY MainTable.pk使用することです。これは機能しますが、 では使用できません。したがって、特定のクエリでは役に立ちません。DISTINCTCOUNT()GROUP_CONCAT()SUM()


常に機能する 3 番目のオプションは、最初に 2 つ (またはそれ以上) のサイド テーブルでグループ化し、次にメイン テーブルに結合することです。あなたの場合、このようなもの:

SELECT p.* ,
       COALESCE(pt.sum_other_values, 0) AS sum_other_values
       COALESCE(pt.cnt, 0) AS tags_count,
       COALESCE(pc.cnt, 0) AS categories_count,
       COALESCE(category_titles, '') AS category_titles

FROM `product` p

JOIN 
    ( SELECT product_id
           , COUNT(*) AS cnt
           , GROUP_CONCAT(title) AS category_titles
      FROM `product_categories` pc
      WHERE status = 1
      GROUP BY product_id
    ) AS pc
  ON p.`product_id` = pc.`product_id`

JOIN 
    ( SELECT product_id
           , COUNT(*) AS cnt
           , SUM(some_other_numeric_value) AS sum_other_values
      FROM `product_tags` pt
      WHERE some_value = 'some comparison value'
      GROUP BY product_id
    ) AS pt
ON p.`product_id` = pt.`product_id`

ORDER BY sum_other_values DESC ;

内部結合を外部結合に変更する場合に備えて、COALESCE()厳密には必要ありません。LEFT

于 2013-01-03T14:30:43.073 に答える
0

合計関数で注文することはできません

代わりに、そのようにすることができます

 SELECT * ,SUM( pt.`some_other_numeric_value` ) as sumvalues

 FROM `product` p

 JOIN `product_categories` pc
 ON p.`product_id` = pc.`product_id`

 JOIN `product_tags` pt
 ON p.`product_id` = pt.`product_id`

 WHERE pt.`some_value` = 'some comparison value'

 GROUP BY p.`product_id`

 HAVING SUM( pc.`status` ) > 0

 ORDER BY sumvalues DESC
于 2013-01-03T14:21:04.610 に答える