0

私は2つのテーブルを持っています(質問に重要なフィールドのみをリストしています):

t_groups

  • INT グループ ID プライマリ
  • VARCHAR(255) grname

t_グッズ

  • INT グッド ID プライマリ
  • INT グループ ID
  • INT 価格
  • VARCHAR(255) 名

次に、グループ名と各グループで最も安い商品の名前を選択するクエリが必要です。このようにしてみました:

SELECT gr.groupId, grname, g.name
FROM t_groups AS gr
LEFT JOIN (SELECT * FROM t_goods ORDER BY PRICE ASC LIMIT 1) AS g
ON g.groupId = gr.groupId

g.name フィールドに NULL を返します。簡単に説明できます:

JOIN ステートメント内の SELECT は、最初に最も安い商品を選択し、次に groupId で「フィルタリング」しようとします。明らかに、それは最も安価な財が属するグループに対してのみ機能します。

タスクを解決するにはどうすればよいですか?

4

2 に答える 2

6

クエリが機能しない理由

SELECT gr.groupId, grname, g.name
FROM t_groups AS gr
LEFT JOIN (SELECT * FROM t_goods ORDER BY PRICE ASC LIMIT 1) AS g
ON g.groupId = gr.groupId

内部クエリは、データベース内で (グループに関係なく)絶対に安い商品を選択します。したがって、LEFT JOINこの結果セットにグループ化すると、世界的に最も安価な商品を実際に含むグループのみが一致する行になります (そのグループのg.name列は適切に埋められるはずです)。ただし、LEFT JOIN仕組みにより、他のすべてのグループはNULLのすべての列の値として取得されますg

正しい解決策

まず、各グループで最も安い価格を選択する必要があります。かんたんだよ:

SELECT groupId, MIN(price) AS minPrice FROM t_goods GROUP BY (groupId)

ただし、関連する がなければ、最も安い価格は役に立ちませんgoodId。問題は、次のようなものを書くのは意味がないということです:

/* does not make sense, although MySql has historically allowed it */
SELECT goodId, groupId, MIN(price) AS minPrice FROM t_goods GROUP BY (groupId)

goodIdその理由は、集計関数 ( などMIN)でラップしない限り、グループ化されていない列 (つまり ) 選択できないためgoodIdですgroupId

goodId各グループで最も安い商品を入手するための正しい、ポータブルな方法は次のとおりです。

SELECT goodId, temp.groupId, temp.minPrice
FROM (SELECT groupId, MIN(price) AS minPrice FROM t_goods GROUP BY groupId) temp
      JOIN t_goods ON temp.groupId = t_goods.groupId AND temp.minPrice = t_goods.price)

上記のクエリは、最初にグループごとに最も安い価格を見つけ、次に商品テーブルに再び結合して、goodIdそのグループ内でその価格を持つ商品の を見つけます。

重要:グループ内で複数の商品の最低価格が等しい場合、このクエリはそれらすべてを返します。グループごとに1 つの結果のみが必要な場合は、タイブレーカーを指定する必要があります。次に例を示します。

SELECT MIN(goodId), temp.groupId, MIN(temp.minPrice)
FROM (SELECT groupId, MIN(price) AS minPrice FROM t_goods GROUP BY groupId) temp
      JOIN t_goods ON temp.groupId = t_goods.groupId AND temp.minPrice = t_goods.price)
GROUP BY temp.groupId

このクエリを使用すると、各グループで最も安い商品の名前と価格を見つけることができます (最も低い商品がgoodIdタイブレーカーとして使用されます)。

SELECT groupId, grname, gd.name, t3.minPrice
FROM t_groups AS gr
LEFT JOIN (SELECT MIN(goodId) AS goodId, t1.groupId, MIN(t1.minPrice) AS minPrice
           FROM (SELECT groupId, MIN(price) AS minPrice FROM t_goods GROUP BY groupId) t1
                 JOIN t_goods ON t1.groupId = t_goods.groupId AND t1.minPrice = t_goods.price
           ) t2
) t3 ON gr.groupId = t3.groupId
LEFT JOIN t_goods gd ON t3.goodId = gd.goodId

この最後のクエリは、「外部」レベルで 2 つの結合を実行します。

  • goodId「各グループの最安価格」テーブルを使用してグループを結合し、最安goodId価格を取得します
  • 次に、商品テーブルと結合して、これで商品の名前を取得しますgoodId

複数の商品が最安値で並んでいても、グループごとに 1 つの商品のみが生産されます。

于 2012-04-12T11:15:33.123 に答える
1

方法は次のとおりです。

select 
  t_groups.grname as `name of group`,
  t_goods.name as `name of good`
from (
  select 
    groupId, 
    min(price) as min_price
  from t_goods 
  group by groupId
) as mins
inner join t_goods
  on mins.groupId = t_goods.groupId and mins.min_price = t_goods.price
inner join t_groups
  on mins.groupId = t_groups.groupId

仕組み:

  • minsサブクエリは、各 groupId の最低価格を取得します
  • に参加minsするt_goodsと、グループ内で最低価格を持つすべての商品が取り出されます。最低価格の商品が複数ある場合、これは単一のグループで複数の商品を返す可能性があることに注意してください
  • t_groupsグループ名を取得するために結合されます

left joinあなたのクエリは、1 行しかないサブクエリを対象としていたため、おそらく NULL を返していました。

于 2012-04-12T11:15:54.540 に答える