2

私は従業員のための出勤/退勤システムを作成しています。

tbl_clockinsには、各出勤/退勤セッションの記録と、各セッションが支払われているかどうか、従業員がそのセッションにどれだけ遅れたか、またはどのくらいの残業をしたかなどの情報が含まれています。

tbl_user_work_settingsと呼ばれる別のテーブルがあり、マネージャーは、従業員が休日である日、または病気などで離陸した日を設定できます。

各従業員の合計が必要なレポートをいくつか作成しています。たとえば、特定の日付範囲で各従業員が休日として取った合計日数などです。私は実際に必要なすべての情報を取得する非常に長いクエリを持っていますが、それは巨大でやや非効率的です。それをより小さく/より効率的にする方法はありますか?どんな助けでも大歓迎です。

// get total days worked, unpaid days, bank holidays, holidays, sicknesses
// and absences within given date range for given users            
$sql = "SELECT us.username, daysWorked, secondsWorked,
            unpaidDays, bankHolidays, holidays, sicknesses, absences
FROM
  (SELECT username FROM users WHERE clockin_valid='1') us
  LEFT JOIN (
    SELECT   username, selectedDate, count(isUnpaid) AS unpaidDays
    FROM     tbl_user_work_settings
    WHERE    isUnpaid = '1'
         AND selectedDate>='$startDate'
         AND selectedDate<='$endDate'
    GROUP BY username
  ) u ON us.username=u.username
  LEFT JOIN (
    SELECT   username, count(isBankHoliday) AS bankHolidays
    FROM     tbl_user_work_settings
    WHERE    isBankHoliday='1'
         AND selectedDate>='$startDate'
         AND selectedDate<='$endDate'
    GROUP BY username
  ) bh ON us.username=bh.username
  LEFT JOIN (
    SELECT   username, count(isHoliday) AS holidays
    FROM     tbl_user_work_settings
    WHERE    isHoliday='1'
         AND selectedDate>='$startDate'
         AND selectedDate<='$endDate'
    GROUP BY username
  ) h ON us.username=h.username
  LEFT JOIN (
    SELECT   username, count(isSickness) AS sicknesses
    FROM     tbl_user_work_settings
    WHERE    isSickness='1'
         AND selectedDate>='$startDate'
         AND selectedDate<='$endDate'
    GROUP BY username
  ) s ON us.username=s.username
  LEFT JOIN (
    SELECT   username, count(isOtherAbsence) AS absences
    FROM     tbl_user_work_settings
    WHERE    isOtherAbsence='1'
         AND selectedDate>='$startDate'
         AND selectedDate<='$endDate'
    GROUP BY username
  ) a ON us.username=a.username
  LEFT JOIN (
    SELECT   username, count(DISTINCT DATE(in_time)) AS daysWorked,
                SUM(seconds_duration) AS secondsWorked
    FROM     tbl_clockins
    WHERE    DATE(in_time)>='$startDate'
         AND DATE(in_time)<='$endDate'
    GROUP BY username
  ) dw ON us.username=dw.username";

if(count($selectedUsers)>0)
  $sql .= " WHERE (us.username='"
       .  implode("' OR us.username='", $selectedUsers)."')";

$sql .= " ORDER BY us.username ASC";
4

2 に答える 2

2

テーブルSUM(condition)の1回の使用で使用できます。tbl_user_work_settings

// get total days worked, unpaid days, bank holidays, holidays, sicknesses
// and absences within given date range for given users            
$sql = "
  SELECT      users.username,
              SUM(ws.isUnpaid      ='1')       AS unpaidDays,
              SUM(ws.isBankHoliday ='1')       AS bankHolidays,
              SUM(ws.isHoliday     ='1')       AS holidays,
              SUM(ws.isSickness    ='1')       AS sicknesses,
              SUM(ws.isOtherAbsence='1')       AS absences,
              COUNT(DISTINCT DATE(cl.in_time)) AS daysWorked,
              SUM(cl.seconds_duration)         AS secondsWorked
  FROM        users
    LEFT JOIN tbl_user_work_settings           AS ws
           ON ws.username = users.username
          AND ws.selectedDate  BETWEEN '$startDate' AND '$endDate'
    LEFT JOIN tbl_clockins                     AS cl
           ON cl.username = users.username
          AND DATE(cl.in_time) BETWEEN '$startDate' AND '$endDate'
  WHERE       users.clockin_valid='1'";

if(count($selectedUsers)>0) $sql .= "
          AND users.username IN ('" . implode("','", $selectedUsers) . "')";

$sql .= "
  GROUP BY    users.username
  ORDER BY    users.username ASC";

ちなみに(そしておそらく他の読者の利益のために)、SQLに挿入する前にPHP変数を適切にエスケープすることで、SQLインジェクション攻撃を回避していることを願っています理想的には、それをまったく行わないでください。代わりに、プリペアドステートメントのパラメーター(SQLでは評価されません)としてそのような変数をMySQLに渡します。BobbyTablesの詳細を参照してください

また、余談ですが、なぜ整数型を文字列として(一重引用符で囲むことによって)処理するのですか?これは不要であり、MySQLでは不要な型変換を実行する必要があるためリソースが無駄になります。実際、さまざまなisUnpaidetc列がすべて0/1である場合は、上記を変更して同等性テストを削除し、SUM(ws.isUnpaid)etcを直接使用することができます。

于 2012-05-03T11:05:23.803 に答える
0

一時テーブルに結合する各テーブルを配置します...次に、一時テーブルの結合可能なフィールドにインデックスを作成します...そして一時テーブルを使用してクエリを作成します。

例:

SELECT   username, selectedDate, count(isUnpaid) AS unpaidDays
INTO     #TempTable1
FROM     tbl_user_work_settings
WHERE    isUnpaid = '1'
     AND selectedDate>='$startDate'
     AND selectedDate<='$endDate'
GROUP BY username
create clustered index ix1 on #TempTable1 (username)
于 2012-05-03T11:08:10.233 に答える