35

私は3つのテーブルを持っています:

users(id, account_balance)
grocery(user_id, date, amount_paid)
fishmarket(user_id, date, amount_paid)

fishmarketとテーブルの両方groceryで、同じuser_idに対して複数のオカレンスがあり、支払った日付と金額が異なる場合や、特定のユーザーに対して何も含まれていない場合があります。次のクエリを試してみると:

SELECT
     t1."id" AS "User ID",
     t1.account_balance AS "Account Balance",
     count(t2.user_id) AS "# of grocery visits",
     count(t3.user_id) AS "# of fishmarket visits"
FROM users t1
LEFT OUTER JOIN grocery t2 ON (t2.user_id=t1."id") 
LEFT OUTER JOIN fishmarket t3 ON (t3.user_id=t1."id") 
GROUP BY t1.account_balance,t1.id
ORDER BY t1.id

誤った結果が生成されます:"1", "12", "12"
しかし、1つのテーブルだけを作成しようとすると、または訪問LEFT JOINのいずれかに対して正しい結果が生成されます。groceryfishmarket"1", "3", "4"

私はここで何が間違っているのですか?
PostgreSQL9.1を使用しています。

4

3 に答える 3

66

結合は左から右に処理されます (括弧で別の指示がない限り)。1 人のユーザーに 3 つの食料品を販売する場合LEFT JOIN(または同様の効果)、3 つの行 ( 1 x 3 ) が得られます。その後、同じユーザーの 4 つの fishmarkets に参加すると、12 ( 3 x 4 ) 行が得られ、結果の前のカウントが乗算され、期待どおりに加算されません。 これにより、食料品店や魚市場への訪問が倍増します。JOIN

次のように機能させることができます。

SELECT u.id
     , u.account_balance
     , g.grocery_visits
     , f.fishmarket_visits
FROM   users u
LEFT   JOIN (
   SELECT user_id, count(*) AS grocery_visits
   FROM   grocery
   GROUP  BY user_id
   ) g ON g.user_id = u.id
LEFT   JOIN (
   SELECT user_id, count(*) AS fishmarket_visits
   FROM   fishmarket
   GROUP  BY user_id
   ) f ON f.user_id = u.id
ORDER  BY u.id;

1 人または数人のユーザーの集計値を取得するには、@Vince などの相関サブクエリ を使用すると問題ありません。テーブル全体またはその主要部分の場合、n-tables を集約して結果に一度結合する方が (はるかに) 効率的です。このようにして、外側のクエリにも別のものは必要ありません。GROUP BY

grocery_visitsまた、fishmarket_visitsそれぞれNULLのテーブルに関連するエントリがないユーザー用です。0代わりに(または任意の数)必要な場合COALESCEは、外側で使用しますSELECT

SELECT u.id
     , u.account_balance
     , COALESCE(g.grocery_visits   , 0) AS grocery_visits
     , COALESCE(f.fishmarket_visits, 0) AS fishmarket_visits
FROM   ...
于 2012-09-17T17:15:43.273 に答える
13

元のクエリの場合、事前にグループ化された結果を確認するためにgroup byを削除すると、受け取っていたカウントが作成された理由がわかります。

おそらく、サブクエリを利用する次のクエリは、意図した結果を達成するでしょう。

SELECT
 t1."id" AS "User ID",
 t1.account_balance AS "Account Balance",
 (SELECT count(*) FROM grocery     t2 ON (t2.user_id=t1."id")) AS "# of grocery visits",
 (SELECT count(*) FROM fishmarket  t3 ON (t3.user_id=t1."id")) AS "# of fishmarket visits"
FROM users t1
ORDER BY t1.id
于 2012-09-17T17:24:06.660 に答える
3

これは、ユーザー テーブルが食料品テーブルに結合するときに、3 つのレコードが一致するためです。次に、これら 3 つのレコードのそれぞれが fishmarket の 4 つのレコードと一致し、12 のレコードが生成されます。探しているものを取得するには、サブクエリが必要です。

于 2012-09-17T17:18:21.857 に答える