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)在表的单次使用中使用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";

顺便说一句(也许更多的是为了其他读者的利益),我真的希望您通过在将 PHP 变量插入 SQL 之前正确转义它们来避免 SQL 注入攻击。理想情况下,您根本不应该这样做,而是将这些变量作为准备好的语句的参数传递给 MySQL(不会针对 SQL 进行评估):阅读有关Bobby Tables的更多信息。

另外,顺便说一句,您为什么将整数类型作为字符串处理(通过将它们括在单引号字符中)?这是不必要的,而且在 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 回答