2

以下のようなmysqlデータベースに学生の出席ビューがあります

id    date         grade
239   01/09/2012   1
239   02/09/2012   0
239   04/09/2012   0
239   05/09/2012   1
239   07/09/2012   0
239   25/09/2012   0
239   26/09/2012   0

また、以下のようなデータベースに休暇申請テーブルもあります。ここで、承認済み = 1 および拒否済み = 0 です。

id  uid    from            to           status
 1   239    04/09/2012    07/09/2012  approved
 2   239    26/09/2012    26/09/2012  rejected

今、私は承認された休暇と学生の最終成績を以下のように表示するクエリを作成したいと思います

id    date       grade    leave   Fgrade
239   01/09/2012   1                 1
239   02/09/2012   0                 0
239   04/09/2012   0       1         1
239   05/09/2012   1       1         1    
239   07/09/2012   0       1         1
239   25/09/2012   0                 0
239   26/09/2012   0                 0

この結果を得るにはどうすればよいか教えてください。

4

2 に答える 2

4

修正済み

(後のコメントで提供された説明に基づく変更:)

SELECT a.id
     , a.date
     , a.grade
     , NULLIF(MAX(l.id) IS NOT NULL,0) AS leave
     , IF(MAX(l.id) IS NULL,a.grade,1) AS fgrade
  FROM student_attendance a
  LEFT
  JOIN leave_application l 
    ON l.uid = a.id
   AND l.from <= a.date
   AND l.to + INTERVAL 1 DAY > a.date
   AND l.status = 'Approved'
 GROUP BY a.id, a.date, a.grade

パフォーマンスのために、おそらくインデックスが必要です

... ON `student_attendance` (`id`, `date`, `grade`) 
... ON `leave_application` (`uid`, `status`, `from`, `to`, `uid`)

EXPLAIN SELECT ... を使用して、アクセス プランに関する情報を取得できます。

8.8.1 Explain を使用したクエリの最適化 http://dev.mysql.com/doc/refman/5.5/en/using-explain.html


ついさっき:

これは指定された結果セットを返すと思います。

SELECT a.id
     , a.date
     , a.grade
     , l.id AS leave
     , IF(l.id IS NULL,a.grade,1) AS fgrade
  FROM student_attendance a
  LEFT
  JOIN leave_application l 
    ON l.uid = a.id
   AND l.from <= a.date
   AND l.to >= a.date
   AND l.status = 1 /* approved */

JOIN 述語は、学生 ID と一致し、休暇期間内の出席日の範囲チェックと、休暇が承認されています。

一致する (重複する) 休暇行がない場合、fgrade には grade 列の値が割り当てられます。それ以外の場合は、fgrade に 1 を割り当てます。

あなたの質問に残されたコメントに基づいて、IF 関数の 3 番目の引数としてのリテラル 1 は、leave_application からのステータス列への参照に置き換えることができます。成績と休暇ステータスを追加すると、学生が休暇を許可されたが成績も取得した場合、希望よりも高い値になる可能性があります。1+1=2。

IF(l.id IS NULL,a.grade,l.status) AS fgrade

Student_attendance の行が leave_application テーブルの複数の行と一致する可能性があります。GROUP BY と集計でこれに対処できます...

SELECT a.id
     , a.date
     , a.grade
     , MAX(l.id) AS leave
     , IF(MAX(l.id) IS NULL,a.grade,1) AS fgrade
  FROM student_attendance a
  LEFT
  JOIN leave_application l 
    ON l.uid = a.id
   AND l.from <= a.date
   AND l.to >= a.date
   AND l.status = 1 /* approved */
 GROUP BY a.id, a.date, a.grade

statusが実際に を含む文字列の場合'Approved'、クエリを調整できます。leave 列のその値は ID ではない可能性があります。leave の値は ID とステータス「承認済み」の値 1 の両方と一致するため、サンプル データからはわかりません。したがって、その値は実際には

 l.status AS leave
 NULLIF(l.id IS NOT NULL,0) AS leave
 IF(l.id IS NOT NULL,1,NULL) AS leave
 (l.id/l.id) AS leave

これらの式のいずれでも、サンプル データに示されている結果が得られます。


繰り返しますが、質問へのコメントで提供された追加情報に基づいて...

SELECT a.id
     , a.date
     , a.grade
     , NULLIF(MAX(l.id) IS NOT NULL,0) AS leave
     , IF(MAX(l.id) IS NULL,a.grade,1) AS fgrade
  FROM student_attendance a
  LEFT
  JOIN leave_application l 
    ON l.uid = a.id
   AND l.from <= a.date
   AND l.to >= a.date
   AND l.status = 'Approved'
 GROUP BY a.id, a.date, a.grade
于 2013-07-13T05:01:57.677 に答える
3

試す

SELECT a.id, a.date, a.grade,
       CASE WHEN l.status = 'approved' THEN 1 END `leave`,
       CASE WHEN l.status = 'approved' THEN 1 ELSE a.grade END fgrade
  FROM attendance a LEFT JOIN `leave` l
    ON a.id = l.uid 
   AND a.date BETWEEN l.from AND l.to

出力:

| | ID | 日付 | グレード | 離れる | Fグレード |
----------------------------------------------
| | 239 | 2012-09-01 | 1 | (ヌル) | 1 |
| | 239 | 2012-09-02 | 0 | (ヌル) | 0 |
| | 239 | 2012-09-04 | 0 | 1 | 1 |
| | 239 | 2012-09-05 | 1 | 1 | 1 |
| | 239 | 2012-09-07 | 0 | 1 | 1 |
| | 239 | 2012-09-25 | 0 | (ヌル) | 0 |
| | 239 | 2012-09-26 | 0 | (ヌル) | 0 |

これがSQLFiddleのデモです

于 2013-07-13T05:12:05.703 に答える