1

複数の左外部結合を使用して平均スコアとカード数を計算する方法を見つけようとしています。次のスキーマとテスト データがあります。各デッキには 0 以上のスコアと 0 以上のカードがあります。各デッキの平均スコアとカード数を計算する必要があります。便宜上 mysql を使用していますが、最終的にはこれを Android フォンの sqlite で実行したいと考えています。

mysql> デッキから * を選択します。
+----+---------+
| | ID | 名前 |
+----+---------+
| | 1 | 1 つ |
| | 2 | 2 |
| | 3 | 3 |
+----+---------+
mysql> select * from score;
+---------+-------+---------------------+--------+
| | スコア ID | 値 | 日付 | デッキ ID |
+---------+-------+---------------------+--------+
| | 1 | 6.58 | 2009-10-05 20:54:52 | 1 |
| | 2 | 7 | 2009-10-05 20:54:58 | 1 |
| | 3 | 4.67 | 2009-10-05 20:55:04 | 1 |
| | 4 | 7 | 2009-10-05 20:57:38 | 2 |
| | 5 | 7 | 2009-10-05 20:57:41 | 2 |
+---------+-------+---------------------+--------+
mysql> select * from card;
+--------+-------+------+--------+
| | カード ID | フロント | 戻る | デッキ ID |
+--------+-------+------+--------+
| | 1 | フロン | 戻る | 2 |
| | 2 | フロン | 戻る | 1 |
| | 3 | f1 | b2 | 1 |
+--------+-------+------+--------+

次のクエリを実行します...

mysql> デッキ名を選択, sum(スコア.値)/カウント(スコア.値) "Ave",
    -> count(card.front) "Count"
    →デッキから
    -> Deck.id=score.deckId の左外部結合スコア
    -> Deck.id=card.deckId の左外側の参加カード
    -> Deck.id でグループ化;

+------+-----------------+-------+
| | 名前 | アベニュー | カウント |
+------+-----------------+-------+
| | 1 つ | 6.083333333333 | 6 |
| | 2 | 7 | 2 |
| | 3 | ヌル | 0 |
+------+-----------------+-------+

...そして、平均については正しい答えが得られますが、カードの数については間違った答えが得られます。髪を抜く前に、誰かが私が間違っていることを教えてもらえますか?

ありがとう!

ジョン

4

4 に答える 4

1

何が問題になっているのかというと、との間でデカルト積を作成しているということです。scorecard

仕組みは次のとおりです。に参加するdeckscore、複数の行が一致する場合があります。次に、これらの複数の行のそれぞれが、の一致するすべてcardの行に結合されます。これを防ぐ条件はありません。条件がない場合のデフォルトの結合動作は、あるテーブルのすべての行を別のテーブルのすべての行に結合することです。

実際の動作を確認するには、groupbyを使用せずにこのクエリを試してください。

select * 
from deck 
left outer join score on deck.id=score.deckId 
left outer join card on deck.id=card.deckId;

とからの列には、多くの繰り返しデータが表示されscoreますcard。繰り返しのあるオーバーデータを計算するAVG()と、冗長な値は魔法のように消えます(値が均一に繰り返されている限り)。しかし、あなたCOUNT()SUM()彼らの場合、合計はかなり離れています。

不注意なデカルト積に対する救済策があるかもしれません。あなたの場合、あなたはCOUNT(DISTINCT)補償するために使うことができます:

select deck.name, avg(score.value) "Ave", count(DISTINCT card.front) "Count" 
from deck 
left outer join score on deck.id=score.deckId 
left outer join card on deck.id=card.deckId
group by deck.id;

このソリューションは、不注意なデカルト積のすべてのケースを解決するわけではありません。より汎用的な解決策は、それを2つの別々のクエリに分割することです。

select deck.name, avg(score.value) "Ave"
from deck 
left outer join score on deck.id=score.deckId 
group by deck.id;

select deck.name, count(card.front) "Count" 
from deck 
left outer join card on deck.id=card.deckId
group by deck.id;

データベースプログラミングのすべてのタスクを単一のクエリで実行する必要があるわけではありません。複数の統計が必要な場合は、個々のクエリを使用する方が効率的です(また、単純で、変更が簡単で、エラーが発生しにくくなります)。

于 2009-10-10T05:22:54.017 に答える
1
select deck.name, coalesce(x.ave,0) as ave, count(card.*) as count -- card.* makes the intent more clear, i.e. to counting card itself, not the field.  but do not do count(*), will make the result wrong
from deck    
left join -- flatten the average result rows first
(
    select deckId,sum(value)/count(*) as ave -- count the number of rows, not count the column name value.  intent is more clear
    from score 
    group by deckId
) as x on x.deckId = deck.id
left outer join card on card.deckId = deck.id -- then join the flattened results to cards
group by deck.id, x.ave, deck.name
order by deck.id

[編集]

SQLには平均関数が組み込まれています。これを使用してください:

select deckId, avg(value) as ave
from score 
group by deckId
于 2009-10-10T02:32:57.500 に答える
1

カード 2 と 3 を結合してスコア 1、2、3 を作成し、6 (2 * 3) のカウントを作成します。カード 1 の場合、スコア 4 と 5 に結合され、2 (1 * 2) のカウントが作成されます。

現在行っているように、カードの数だけが必要な場合は、COUNT(Distinct Card.CardId) を使用します。

于 2009-10-10T01:48:32.147 に答える
0

私の意見では、左結合を使用するのは良い方法ではありません。必要な結果を得るための標準的な SQL クエリを次に示します。

select
  name,
  (select avg(value) from score where score.deckId = deck.id) as Ave,
  (select count(*) from card where card.deckId = deck.id) as "Count"
from deck;
于 2009-10-10T03:41:33.533 に答える