- PR -

複数テーブルからうまくデータが取得できない

投稿者投稿内容
けん
会議室デビュー日: 2004/10/13
投稿数: 8
お住まい・勤務地: 岡山県
投稿日時: 2006-07-21 17:17
はじめまして、けんといいます。

現在、PHP4+MySQL4.0で、あるサッカーチームの対戦成績を管理するシステムを作成します。
その中で「得点ランキング」の作成がうまくいかないので質問させていただきます。

使用しているテーブルは以下

コード:

-- 試合情報テーブル
CREATE TABLE Game(
    id              smallint unsigned   AUTO_INCREMENT,
    date            date,
    pitchId         smallint unsigned   NOT NULL,
    category        smallint unsigned   NOT NULL,
    gameTitle       char(255),
    opponent        char(255)           NOT NULL,
    gameType        char(3)             NOT NULL,
    
    PRIMARY KEY(id),
    INDEX(pitchId),
    FOREIGN KEY(pitchId) REFERENCES Pitch(id)
) TYPE=INNODB;


-- 試合ごとの試合参加メンバーを保持するテーブル
CREATE TABLE GameEntry(
    gameId          smallint unsigned,
    memberId        smallint unsigned,
    
    PRIMARY KEY(gameId, memberId),
    INDEX(gameId),
    INDEX(memberId),
    FOREIGN KEY(gameId) REFERENCES Game(id),
    FOREIGN KEY(memberId) REFERENCES Member(id)
) TYPE=INNODB;


-- 試合ごとの得点情報を保持するテーブル
CREATE TABLE Score(
    gameId          smallint unsigned,
    sequence        tinyint  unsigned,
    goalMember      smallint unsigned,
    assistMember    smallint unsigned,
    
    PRIMARY KEY(gameId, sequence),
    INDEX(goalMember),
    INDEX(assistMember),
    FOREIGN KEY(gameId) REFERENCES Game(id),
    FOREIGN KEY(goalMember) REFERENCES Member(id),
    FOREIGN KEY(assistMember) REFERENCES Member(id)
) TYPE=INNODB;


-- 選手情報テーブル
CREATE TABLE Member(
    id              smallint unsigned   AUTO_INCREMENT,
    name            char(32),
    
    PRIMARY KEY(id)
) TYPE=INNODB;



最終的に取得したいテーブルは、以下のような感じです。

コード:

┏━━┯━━━┯━━┯━━━┯━━━┓
┃順位│選手名│得点│試合数│得点率┃
┣━━┿━━━┿━━┿━━━┿━━━┫
┃   1│Aくん │  24│    18│ 0.750┃
┃   2│Bくん │  20│    17│ 1.176┃
┃   3│Cくん │  17│    18│ 0.944┃
┃   4│Dくん │  10│    20│ 0.500┃
┃   5│Eくん │   7│    19│ 0.368┃
┗━━┷━━━┷━━┷━━━┷━━━┛



選手名・得点のみでしたら

コード:

SELECT M.id, M.name, COUNT(S.goalMember) AS goals
FROM Score AS S, Member AS M, Game AS G
WHERE M.id = S.goalMember
AND G.id = S.gameId
GROUP BY S.goalMember
ORDER BY goals DESC



の SQL により取得できたのですが、試合数が絡んでくると
どのように SQL を記述すればよいのか分かりません。
(一つの SQL でやろうとするから無理があるのか、
そもそもテーブルの設計が悪いのかもしれませんが。。。)

よろしくお願いします。
とんくま
ベテラン
会議室デビュー日: 2005/08/02
投稿数: 56
お住まい・勤務地: 東京
投稿日時: 2006-07-21 19:57
DB2なら、
コード:
SELECT M.id, M.name

, S.cnt
, G.cnt
, S.cnt*1.000/G.cnt
FROM Member M
, TABLE
(SELECT COUNT(S.goalMember)
FROM Score S
WHERE M.id = S.goalMember
) S (cnt)
, TABLE
(SELECT COUNT(GE.gameId)
FROM GameEntry GE
WHERE M.id = GE.memberId
) G (cnt)
ORDER BY
S.cnt DESC


みたいな感じで出来ますが、MySQLでサポートしているかわかりません。
言い換えますと、SQL標準では TABLE キーワードは LATERAL としてサポートしているので、MySQLがSQL標準のこの機能に準拠しているか、が使えるかどうかの分かれ目と思います。


[ メッセージ編集済み 編集者: とんくま 編集日時 2006-07-21 19:58 ]
けん
会議室デビュー日: 2004/10/13
投稿数: 8
お住まい・勤務地: 岡山県
投稿日時: 2006-07-21 20:22
とんくま様、返信ありがとうございます。

掲載して頂いたコードを実行してみましたが

#1064 - You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'TABLE
(SELECT COUNT(S.goalMember)
FROM Score

とエラーになってしまいましたので、サポートされていないと思われます。
さてどうしたものでしょうか・・・。
とんくま
ベテラン
会議室デビュー日: 2005/08/02
投稿数: 56
お住まい・勤務地: 東京
投稿日時: 2006-07-21 21:27
TABLE の代わりに LATERAL でも駄目ですか?
 
これもありと思いますが、パフォーマンスが悪いかもしれません。
(JOINした結果が、3テーブルそれぞれの行数の掛け算になる)
コード:
SELECT M.id, M.name

, COUNT(DISTINCT S.gameId*100+S.sequence) AS goals
, COUNT(DISTINCT E.gameId) AS games
, COUNT(DISTINCT S.gameId*100+S.sequence)
/ COUNT(DISTINCT E.gameId) AS ratio
FROM Member M
, Score S
, GameEntry E
WHERE M.id = S.goalMember
AND M.id = E.memberId
GROUP BY
M.id, M.name
ORDER BY
goals DESC




[ メッセージ編集済み 編集者: とんくま 編集日時 2006-07-21 21:29 ]
今川 美保(夏椰)
ぬし
会議室デビュー日: 2004/06/10
投稿数: 363
お住まい・勤務地: 神奈川県茅ヶ崎市
投稿日時: 2006-07-23 02:54
MySQL5で確認しているので、4でいけるかはわかりませんが・・・・

ScoreテーブルをgoalMemberでGROUP化し、COUNT(goalMember)すると
選手ごとの得点数が取れるんですよね。
====================
SELECT goalMember, COUNT(goalMember) From Score Group By goalMember ;
====================

これに試合数をつけようとすると
====================
SELECT
goalMember,
COUNT(goalMember),
COUNT(DISTINCT gameID) gameCount
FROM
Score
GROUP BY
goalMember ;
====================
ってかけると思います。
#テーブル定義上、1試合で複数選手がScoreテーブルに登場しそうなので、
#COUNT(DISTINCT 〜)で重複を省いた形で試合数のカウントを取らせて見ました。

後は名前を出すのに、
====================
SELECT
goalMember,
( SELECT name FROM Member Where id = Score.goalMember) name,
COUNT(goalMember) score,
COUNT(DISTINCT gameID) gameCount
FROM
Score
GROUP BY
goalMember ;
====================
とサブクエリでつければいいかと。

得点率はこれ自体をFROM句に指定し、
得点と試合数の列から計算できる・・・・のかな?
#すみません、サッカーはわからないので。

イメージはこんな感じです。
====================
SELECT goalMember,name,score,gameCount, score/gameCount rate
FROM
(
SELECT
goalMember,
( SELECT name FROM Member Where id = Score.goalMember) name,
COUNT(goalMember) score,
COUNT(DISTINCT gameID) gameCount
FROM
Score
GROUP BY
goalMember ;
) t
ORDER BY score ;
====================

こんな感じでどうでしょうか?


_________________
夏椰 @ わんくま同盟
夏椰の庵
Microsoft MVP for Windows Server System - SQL Server ( Jul 2006 - Jun 2008 )
とんくま
ベテラン
会議室デビュー日: 2005/08/02
投稿数: 56
お住まい・勤務地: 東京
投稿日時: 2006-07-23 11:59
Scoreテーブルの GameID を COUNT した場合、
ある選手が、試合には出たが1点も取れなかったら、その選手の GameCount が実際より少なくなりませんか?
今川 美保(夏椰)
ぬし
会議室デビュー日: 2004/06/10
投稿数: 363
お住まい・勤務地: 神奈川県茅ヶ崎市
投稿日時: 2006-07-24 00:18
引用:

とんくまさんの書き込み (2006-07-23 11:59) より:
Scoreテーブルの GameID を COUNT した場合、
ある選手が、試合には出たが1点も取れなかったら、その選手の GameCount が実際より少なくなりませんか?


あっっと、そうですね。失礼しました。

そう考えるとGameEntryをJOINしないとまずいっすねぇ。
_________________
夏椰 @ わんくま同盟
夏椰の庵
Microsoft MVP for Windows Server System - SQL Server ( Jul 2006 - Jun 2008 )
けん
会議室デビュー日: 2004/10/13
投稿数: 8
お住まい・勤務地: 岡山県
投稿日時: 2006-07-24 11:14
返信ありがとうございます。

とんくま様>
LATERAL でも駄目でした。

また提示して頂いた SQL についてですが、
実は GameEntry テーブルは最近になって追加したテーブルで、
現時点では 2006年度試合分のレコードしか作成できていないので、
提示して頂いた SQL を実行した結果、試合数が合いませんでした。
ですが、必要なレコードをすべて追加すればこれでいけそうです。

ちなみに、パフォーマンスについてですが、0.01秒ほどで取得できましたので
それほど悪いというわけでもなさそうです。
取得してきたレコードが少なかった(数十件程度)のもあると思いますが、
そもそもこのアプリは、特定の1チームについてのみの扱いなので、
選手数は多くなっても 50 程度にしかならないので問題ないのかなと考えています。


夏椰様>
返信ありがとうございます。
実は、MySQL 4.0 だとサブクエリが使えないんです。
バージョンが 4.1 以上だと使えるみたいなのですが、環境は 4.0 が前提ですので・・・。
SQL まで提示して頂いたのに申し訳ないです。。。



あと、話はちょっと変わるのですが、今までテーブルを結合してデータを取得する際には、
全部外部結合で行ってきたのですが、外部結合でもかまわないのでしょうか?
絶対に外部結合じゃないといけないというわけではありませんが、気になったので。
(そもそも、内部結合と外部結合の使い分けがよく分かってなかったり。。。)

スキルアップ/キャリアアップ(JOB@IT)