【问题标题】:Is it possible to update rows randomly with a group of set values?是否可以使用一组设置值随机更新行?
【发布时间】:2020-01-18 16:07:27
【问题描述】:

我有一张需要帮助的工作分配表。我想做的是将人们的名字随机分配给表中的行。例如,该表当前如下所示:

        TASK     |NAME
  1 Get Chicken  |
  2 Clean Chicken|
  3 Cook Chicken |
  4 Eat Chicken  |
  5 Eat Corn     |
  6 Takeout Trash|

我想将这些任务分配给 4 名员工,但不想表现出任何偏袒。该表如下所示:

NAME
John 
Lucy 
Fred 
Jasmine

如何根据上述名称随机更新 NAME 字段?

基于 cmets 进行编辑。我将任务数更改为不能被 4 整除的值。在这种情况下,任务数现在是 6。我想做到没有人能比其他同事获得 2 个或更多任务。但在这种情况下,某人比他们的同事多完成一项任务是可以的。他的结果应该类似于(但随机):

        TASK     |NAME
  1 Get Chicken  |John 
  2 Clean Chicken|Jasmine
  3 Cook Chicken |Lucy 
  4 Eat Chicken  |Fred 
  5 Eat Corn     |Fred
  6 Takeout Trash|Jasmine

【问题讨论】:

  • 分配表中是否只有 2 列?员工姓名是否存储在员工表中?
  • 还没有;我将更新原始文件以生成带有名称的表格。
  • 纯随机分配意味着每个员工有 25% 的机会获得每个分配。这意味着完全有可能一名员工将获得 2 或 3 个分配,而另一名将获得 0。我猜您想要添加额外的规则,例如每个员工必须被分配到一个任务。
  • @JustinCave,我猜 OP 正是需要这个。 1 项任务将仅分配给 1 名员工,但顺序必须完全随机。
  • 要根据这些来编辑我的问题,但是如果任务数不能被 4 整除怎么办?例如,如果有 6 个任务。我希望最终结果是工人之间最多有 1 个任务差异。

标签: sql oracle


【解决方案1】:

您可以给任务随机序列号和员工另一个随机序列号,然后使用这些数字连接两个表,然后使用MERGE 语句将与ROWID 伪列相关的表更新为唯一识别每个任务。

Oracle 设置

CREATE TABLE table_name ( task VARCHAR2(20), name VARCHAR2(20) ); 

INSERT INTO table_name ( TASK )
  SELECT 'Get Chicken'   FROM DUAL UNION ALL
  SELECT 'Clean Chicken' FROM DUAL UNION ALL
  SELECT 'Cook Chicken'  FROM DUAL UNION ALL
  SELECT 'Eat Chicken'   FROM DUAL UNION ALL
  SELECT 'Eat Corn'      FROM DUAL UNION ALL
  SELECT 'Takeout Trash' FROM DUAL;

CREATE TABLE employees ( NAME ) AS
  SELECT 'John'    FROM DUAL UNION ALL
  SELECT 'Lucy'    FROM DUAL UNION ALL
  SELECT 'Fred'    FROM DUAL UNION ALL
  SELECT 'Jasmine' FROM DUAL;

合并

MERGE INTO table_name dst
USING (
  WITH random_tasks ( rid, rn ) AS (
    SELECT ROWID,
           ROW_NUMBER() OVER ( ORDER BY DBMS_RANDOM.VALUE )
    FROM   table_name
  ),
  random_names ( name, rn, num_employees ) AS (
    SELECT name,
           ROW_NUMBER() OVER ( ORDER BY DBMS_RANDOM.VALUE ),
           COUNT(*) OVER ()
    FROM   employees
  )
  SELECT rid,
         name
  FROM   random_tasks t
         INNER JOIN
         random_names n
         ON ( MOD( t.rn, n.num_employees ) + 1 = n.rn )
) src
ON ( src.rid = dst.ROWID )
WHEN MATCHED THEN
  UPDATE SET name = src.name;

结果

SELECT * FROM table_name;
任务 |姓名 :------------ | :------ 买鸡 |约翰 清鸡 |茉莉花 煮鸡 |弗雷德 吃鸡 |露西 吃玉米 |弗雷德 外卖垃圾 |露西

db小提琴here

【讨论】:

    【解决方案2】:

    这是一种纯 SQL 方法。

    MERGE INTO so_tasks t USING (
    WITH numbered_tasks AS ( SELECT t.*, 
                                    row_number() OVER (ORDER BY dbms_random.value) task_number, 
                                    count(*) OVER () total_tasks FROM so_tasks t ),
         numbered_employees AS ( SELECT e.*, 
                                 row_number() OVER (ORDER BY dbms_random.value) employee_number, 
                                 count(*) OVER () total_employees FROM so_employees e)
    SELECT     nt.task, 
               ne.name
    FROM       numbered_tasks nt 
    INNER JOIN numbered_employees ne 
          ON ne.employee_number-1 = mod(nt.task_number-1, ne.total_employees) ) u
    ON ( t.task = u.task ) 
    WHEN MATCHED THEN UPDATE SET t.name = u.name;
    

    它对每个列表进行随机排序,并为每个列表中的每一行分配一个数字。然后从员工列表中获取编号与任务编号MOD员工总数匹配的行。

    这是一个完整的例子:

    创建表格

    CREATE TABLE so_tasks 
      ( task VARCHAR2(30) NOT NULL PRIMARY KEY,
        name VARCHAR2(30) );
    
    INSERT INTO so_tasks ( task ) VALUES ('Get Chicken');
    INSERT INTO so_tasks ( task ) VALUES ('Clean Chicken');
    INSERT INTO so_tasks ( task ) VALUES ('Cook Chicken');
    INSERT INTO so_tasks ( task ) VALUES ('Eat Chicken');
    INSERT INTO so_tasks ( task ) VALUES ('Eat Corn');
    INSERT INTO so_tasks ( task ) VALUES ('Takeout Trash');
    
    
    CREATE TABLE so_employees 
      ( name  VARCHAR2(30) NOT NULL PRIMARY KEY );
    
    INSERT INTO so_employees ( name ) VALUES ('John');
    INSERT INTO so_employees ( name ) VALUES ('Lucy');
    INSERT INTO so_employees ( name ) VALUES ('Fred');
    INSERT INTO so_employees ( name ) VALUES ('Jasmine');
    
    COMMIT;
    

    合并

    MERGE INTO so_tasks t USING (
    WITH numbered_tasks AS ( SELECT t.*, 
                                    row_number() OVER (ORDER BY dbms_random.value) task_number, 
                                    count(*) OVER () total_tasks FROM so_tasks t ),
         numbered_employees AS ( SELECT e.*, 
                                 row_number() OVER (ORDER BY dbms_random.value) employee_number, 
                                 count(*) OVER () total_employees FROM so_employees e)
    SELECT     nt.task, 
               ne.name
    FROM       numbered_tasks nt 
    INNER JOIN numbered_employees ne 
          ON ne.employee_number-1 = mod(nt.task_number-1, ne.total_employees) ) u
    ON ( t.task = u.task ) 
    WHEN MATCHED THEN UPDATE SET t.name = u.name;
    

    结果

    SELECT * FROM so_tasks;
    
    +---------------+---------+
    |     TASK      |  NAME   |
    +---------------+---------+
    | Get Chicken   | John    |
    | Clean Chicken | Jasmine |
    | Cook Chicken  | Lucy    |
    | Eat Chicken   | Fred    |
    | Eat Corn      | Jasmine |
    | Takeout Trash | Fred    |
    +---------------+---------+
    

    您对每项任务的确切分配会有所不同,但任何两名员工之间的任务差异永远不会超过一项。

    【讨论】:

      【解决方案3】:

      假设您对 PL/SQL 解决方案没问题(您可以在单个 update 语句中完成,但除非它对性能至关重要,否则我会发现循环更容易遵循)

      begin
        for src in (select t.task_id, e.name
                      from (select t.*,
                                   row_number() over (order by dbms_random.value) rnk
                              from task t) t
                           join
                           (select e.*,
                                   row_number() over (order by dbms_random.value) rnk,
                                   count(*) over () num_emps
                              from employee e) e
                           on( mod( t.rnk, e.num_emps ) = e.rnk - 1 ) )
        loop
          update task
             set name = src.name
            where task_id = src.task_id;
        end loop;
      end;
      /
      

      基本上,这是对两个列表进行随机排序,然后沿着任务列表向下排列,并将下一位员工分配给该任务。如果任务数量不是员工数量的倍数,则一些员工将获得额外的任务,但没有员工会比另一个员工多出 1 个以上的任务。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-10-10
        • 2014-12-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多