首页 > 解决方案 > 我们如何随机分配员工的职责

问题描述

我正试图允许三班倒值班

有以下约束:

1st shift will have 5 employee 
>2nd shift will have 3 employee
3rd shift will have 2 employee 

如果一个人在任何班次中被分配了职责,他将不会被允许再工作两班。前任。如果 X 先生在 8 月 3 日的第 2 班值班,那么他将不会在 8 月 3 日的第 3 班班次和 8 月 4 日的第 1 班班次值班

我已经使用rand()函数来随机选择员工。我正面临约束 4 的问题。即,我如何确保在第一班被分配职责的员工不会在第二或第三班被分配

我的 MySQL 查询是:

SELECT * FROM `employee` ORDER BY rand()

标签: phpmysql

解决方案


我认为这是一个很难用 SQL 解决的问题,但如果我要尝试它,我可能会从随机分配员工开始......

DROP TABLE IF EXISTS employees;

CREATE TABLE employees 
(employee_id SERIAL PRIMARY KEY);

INSERT INTO employees VALUES
(101),(102),(103),(104),(105),(106),(107),(108),(109),(110);

SELECT employee_id,@i:=@i+1 i FROM (SELECT employee_id FROM employees ORDER BY RAND()) x,(SELECT @i:=0) vars;
+-------------+------+
| employee_id | i    |
+-------------+------+
|         108 |    1 |
|         109 |    2 |
|         110 |    3 |
|         103 |    4 |
|         105 |    5 |
|         106 |    6 |
|         102 |    7 |
|         104 |    8 |
|         107 |    9 |
|         101 |   10 |
+-------------+------+

现在,只要我们有至少 3 天的员工(5+3+2=10),我们就可以循环遍历列表,并且知道每个员工在每次工作之间总会有至少两个连续的休息日天。

这是关于问题的另一方面的想法......

DROP TABLE IF EXISTS shifts;

CREATE TABLE shifts 
(shift_id SERIAL PRIMARY KEY
,shift_size INT NOT NULL
);

INSERT INTO shifts VALUES
(1,5),(2,3),(3,2),(4,3),(5,2);

SELECT shift_id
     , ROUND(COALESCE(@j:=@prev+1,1),0) range_start
     , range_end, @prev:=range_end 
  FROM 
     ( SELECT x.*
            , SUM(y.shift_size) range_end 
         FROM shifts x 
         JOIN shifts y 
           ON y.shift_id <= x.shift_id 
        GROUP 
           BY x.shift_id
     ) b
     , ( SELECT @j:=1,@prev:=null) vars
 ORDER 
    BY shift_id;
+----------+-------------+-----------+------------------+
| shift_id | range_start | range_end | @prev:=range_end |
+----------+-------------+-----------+------------------+
|        1 |           1 |         5 |                5 |
|        2 |           6 |         8 |                8 |
|        3 |           9 |        10 |               10 |
|        4 |          11 |        13 |               13 |
|        5 |          14 |        15 |               15 |
+----------+-------------+-----------+------------------+

因此,现在您有了一个员工序列 ID 和这些 ID 可以落入的范围。范围大于雇员列表,因此您必须使用雇员表的大小作为范围的模数。

 SELECT a.shift_id
      , MOD(a.range_start-1,(SELECT COUNT(*) FROM employees))+1 range_start
      , MOD(a.range_end-1,(SELECT COUNT(*) FROM employees))+1 range_end
   FROM
      ( SELECT shift_id
      , ROUND(COALESCE(@j:=@prev+1,1),0) range_start
      , range_end, @prev:=range_end
   FROM
      ( SELECT x.*
             , SUM(y.shift_size) range_end
          FROM shifts x
          JOIN shifts y
            ON y.shift_id <= x.shift_id
         GROUP
            BY x.shift_id
      ) b
      , ( SELECT @j:=1,@prev:=null) vars
  ORDER
     BY shift_id
     ) a
   ORDER BY shift_id;
+----------+-------------+-----------+
| shift_id | range_start | range_end |
+----------+-------------+-----------+
|        1 |           1 |         5 |
|        2 |           6 |         8 |
|        3 |           9 |        10 |
|        4 |           1 |         3 |
|        5 |           4 |         5 |
+----------+-------------+-----------+

我想我可以将最后一步作为练习留给读者。


推荐阅读