更新具有不同随机日期的 PostgreSQL 表

Say*_*akh 5 postgresql update

我在 PostgreSQL、Employee 和 Leave 中有两个表,如下所示:

CREATE TEMP TABLE employee_table AS SELECT
  id::int,
  name::text
FROM ( VALUES
  (1, 'John' ),
  (2, 'David')  
) AS t(id, name);

CREATE TEMP TABLE leave_table AS SELECT
  id::int,
  leave_date::date,
  emp_id::int
FROM ( VALUES
  (1, '01/10/1993' ,1),
  (2, Null         ,1),
  (3, Null         ,1),
  (4, '02/12/1990' ,2),
  (5, Null         ,2),
  (6, Null         ,2) 
) AS t(ID,Leave_Date,Emp_ID);
Run Code Online (Sandbox Code Playgroud)

我想更新 Leave 表并将 Leave_Date 列设置为每个员工的随机日期,可能是 01/01/2000,但如果员工在 Leave 表中有多个空条目,我想用两个不同的日期更新他的空条目这意味着一名员工在休假表中不应有两个相同的 Leave_Date 值,更新后我的休假表应如下所示:

ID        Leave_Date    Emp_ID  
1         01/10/1993      1  
2         01/01/2000      1  
3         01/01/2001      1  
Run Code Online (Sandbox Code Playgroud)

如上所示,最初 john 在 Leave 表中有两个空条目,结果显示这些条目更新了两个不同的日期。有什么办法吗?

Eva*_*oll 6

首先,当只有一个日期并且它为空时,您从未提供过测试用例,因此我们创建了它。

INSERT INTO employee_table (id, name) VALUES (3, 'Evan Carroll');
INSERT INTO leave_table VALUES ( 10, null, 3 );
Run Code Online (Sandbox Code Playgroud)

然后我们运行一个命令来检查 .a 中是否emp_id有多个条目leave_table。结果在那个派生表中。我们相应地更新。在这里,我们生成一个日期,表示 1900 年至 2020 年之间的年份开始。只需根据您没有在问题中定义的“随机日期”来更新它。

UPDATE leave_table
SET leave_date = CASE
  WHEN t.count = 1 OR t.count IS NULL
  THEN '01/01/2000'::date
  ELSE '1/1/1900'::date + ('1 year'::interval*floor(random()*120))
END
FROM (
  SELECT emp_id, count(*) FROM leave_table
  WHERE leave_date IS NULL
  GROUP BY emp_id
) AS t
WHERE leave_date IS NULL
AND t.emp_id = leave_table.emp_id;
Run Code Online (Sandbox Code Playgroud)

然后我们有了

TABLE leave_table;
 id | leave_date | emp_id 
----+------------+--------
  1 | 1993-01-10 |      1
  4 | 1990-02-12 |      2
  2 | 1964-01-01 |      1
  3 | 1929-01-01 |      1
  5 | 1933-01-01 |      2
  6 | 1902-01-01 |      2
 10 | 2000-01-01 |      3
Run Code Online (Sandbox Code Playgroud)

现在,正如@McNets 昨天指出的那样,我有点作弊。相反,试试这个(更复杂的查询),这足以让问题用两个不同的日期更新他的 [ emp_id] 空条目

WITH t AS (
  SELECT
    id,
    emp_id,
    leave_date,
    count(*) OVER (PARTITION BY emp_id) AS max_nulls,
    row_number() OVER (PARTITION BY emp_id)
  FROM leave_table
  WHERE leave_table.leave_date IS NULL
)
UPDATE leave_table
SET leave_date = CASE
  WHEN t.max_nulls = 1 OR t.max_nulls IS NULL
  THEN '01/01/2000'::date
  ELSE date_series_emp.ds
END
FROM t
INNER JOIN (
  SELECT distinct_emps.emp_id,
    gs.ds,
    count(*) OVER (PARTITION BY emp_id ORDER BY random()) AS row_number
  FROM ( SELECT DISTINCT emp_id FROM leave_table ) AS distinct_emps
  CROSS JOIN generate_series('1/1/1900'::date, '1/1/1990'::date, '1 month')
    AS gs(ds)
) AS date_series_emp
  USING ( emp_id, row_number )
WHERE t.id = leave_table.id;
Run Code Online (Sandbox Code Playgroud)

分解它,CTE 这样做

  SELECT
    id,
    emp_id,
    leave_date,
    count(*) FILTER (WHERE leave_date IS NULL) OVER (PARTITION BY emp_id) AS max_nulls,
    row_number() OVER (PARTITION BY emp_id)
  FROM leave_table
Run Code Online (Sandbox Code Playgroud)

这会生成集合中有多少空值,以及集合中的行号,我们可以通过更新查询以 1:1 的比例加入这些行号,

 id ? emp_id ? leave_date ? max_nulls ? row_number 
???????????????????????????????????????????????????
  2 ?      1 ?            ?         2 ?          1
  3 ?      1 ?            ?         2 ?          2
  5 ?      2 ?            ?         2 ?          1
  6 ?      2 ?            ?         2 ?          2
 10 ?      3 ?            ?         1 ?          1
Run Code Online (Sandbox Code Playgroud)

唯一的另一个棘手部分是内连接选择,

  SELECT distinct_emps.emp_id,
    gs.ds,
    count(*) OVER (PARTITION BY emp_id ORDER BY random()) AS row_number
  FROM ( SELECT DISTINCT emp_id FROM leave_table ) AS distinct_emps
  CROSS JOIN generate_series('1/1/1900'::date, '1/1/1990'::date, '1 month')
    AS gs(ds)
Run Code Online (Sandbox Code Playgroud)

在那里,我们采用不同的 emp_ids,并在您称为random的一系列日期上加入它们。我们对该序列进行计数(*)以从生成的基数序列中为其提供相应的随机数。

然后我们将其加入表并执行更新..

这种方法确实有一个缺点,如果输入大小耗尽了您的“随机日期”池(只有 1081 个),则根本不会执行超过最大值的行的更新。