2017-07-24 85 views
1

我一直在使用Python在内存中执行此操作,但我想知道在Postgres中设置员工映射表的正确方法。适当的表来跟踪员工随着时间的变化?

row_id | employee_id | other_id | other_dimensions | effective_date | expiration_date | is_current 
    上(EMPLOYEE_ID,other_id),这样一个新的行会被插入每当有变化
  • 我想从上一行的截止日期被更新为新EFFECTIVE_DATE
  • 唯一约束减去1天,和is_current应更新为False
  • 最终目的是希望能够给每位员工在给定日期准确地映射回

很想听到一些最好的practi因此我可以离开我的基于文件的方法,将整个名单读入内存并使用熊猫进行更改,然后截断原始表并插入新表。

+1

我很困惑你的意思是“other_id”和“other_dimensions”。你能澄清一下你的意图吗? – Feneric

+0

这可能是字面上任何可能会改变的东西。例如,经理或地点。在我的具体情况中,它包含未纳入我们员工管理系统的其他系统的ID。我们需要将这些ID(来自各种自定义工具,电话系统,Salesforce等)追溯到官方员工ID,但其中一些会被回收或更改。 – trench

+1

最佳做法是使用存储过程以原子方式执行操作。我必须想一点,但用一个例子来说明它是一个答案。 – Feneric

回答

1

下面是一个使用您提供的列名构建的通用示例,我认为这些列名或多或少都是您想要的。不要把它看作是一个立即可用的解决方案,而是一个如何做出类似这样的工作的例子,你必须修改一下你自己的实际用例。

大致的想法是创建一个基本的原始表,它保存所有的数据,并建立一个用于普通访问的视图。无论多么复杂,您仍然可以使用原始表格执行任何您需要对数据执行的操作,但该视图为常规使用提供更严格的访问权限。规则在视图上就位,以执行这些限制并执行所需的特殊操作。尽管对于当前的应用程序来说听起来并不重要,但需要注意的是,这些限制可以通过PostgreSQL的角色和特权以及SQL GRANT命令来实施。

我们从制作原始表格开始。由于is_current列很可能会被用作参考,因此我们会为其添加索引。我们将利用PostgreSQL的SERIAL类型来管理我们的原始表row_id。该视图甚至不需要参考底层row_id。我们会将is_current默认为True值,因为我们预计大部分时间我们都会添加当前记录,而不是过去的记录。

CREATE TABLE raw_employee (
    row_id SERIAL PRIMARY KEY, 
    employee_id INTEGER, 
    other_id INTEGER, 
    other_dimensions VARCHAR, 
    effective_date DATE, 
    expiration_date DATE, 
    is_current BOOLEAN DEFAULT TRUE 
); 

CREATE INDEX employee_is_current_index ON raw_employee (is_current); 

现在我们定义我们的视图。对于大多数人来说,这将是访问员工数据的正常方式。在内部,它是针对我们已经定义的底层raw_employee表按需运行的特殊SELECT。如果我们有理由,我们可以进一步优化这个视图来隐藏更多的数据(它已经隐藏了前面提到的低级row_id),或者显示通过计算或与其他表的关系产生的额外数据。

CREATE OR REPLACE VIEW employee AS 
SELECT employee_id, other_id, 
    other_dimensions, effective_date, expiration_date, 
    is_current 
    FROM raw_employee; 

现在我们的规则。我们构造这些内容,以便每当有人试图对我们的观点进行操作时,内部会根据我们定义的限制对我们的原始表执行操作。第一个INSERT;它大多只是通过没有改变的数据,但它必须考虑到隐藏row_id

CREATE OR REPLACE RULE employee_insert AS ON INSERT TO employee DO INSTEAD 
    INSERT INTO raw_employee VALUES (
    NEXTVAL('raw_employee_row_id_seq'), 
    NEW.employee_id, NEW.other_id, 
    NEW.other_dimensions, 
    NEW.effective_date, NEW.expiration_date, 
    NEW.is_current 
); 

NEXTVAL部分使我们对PostgreSQL的精益生产row_id处理。接下来是我们最复杂的一个:UPDATE。根据您描述的意图,它必须与employee_id,other_id对匹配并执行两个操作:将旧记录更新为不再最新,并插入具有更新日期的新记录。你没有指定你想如何管理新的到期日期,所以我猜测了一下。改变它很容易。

CREATE OR REPLACE RULE employee_update AS ON UPDATE TO employee DO INSTEAD (
    UPDATE raw_employee SET is_current = FALSE 
    WHERE raw_employee.employee_id = OLD.employee_id AND 
     raw_employee.other_id = OLD.other_id; 
    INSERT INTO raw_employee VALUES (
    NEXTVAL('raw_employee_row_id_seq'), 
    COALESCE(NEW.employee_id, OLD.employee_id), 
    COALESCE(NEW.other_id, OLD.other_id), 
    COALESCE(NEW.other_dimensions, OLD.other_dimensions), 
    COALESCE(NEW.effective_date, OLD.expiration_date - '1 day'::INTERVAL), 
    COALESCE(NEW.expiration_date, OLD.expiration_date + '1 year'::INTERVAL), 
    TRUE 
); 
); 

采用COALESCE能使我们更新有明确的更新列,但保留旧的值是那些不。最后,我们需要为DELETE制定一个规则。既然你说过你想确保你可以追踪员工的历史,那么最好的办法也是最简单的:我们只是禁用它。

CREATE OR REPLACE RULE employee_delete_protect AS 
    ON DELETE TO employee DO INSTEAD NOTHING; 

现在我们应该能够通过我们的观点进行INSERT操作将数据插入到我们的原始表。这里有两名样本员工;第一个还剩几个星期,但第二个即将到期。请注意,在此级别,我们不需要关心row_id。这是低级原始表的内部实现细节。

INSERT INTO employee VALUES (
    1, 1, 
    'test', CURRENT_DATE - INTERVAL '1 week', CURRENT_DATE + INTERVAL '3 weeks', 
    TRUE 
); 

INSERT INTO employee VALUES (
    2, 2, 
    'another test', CURRENT_DATE - INTERVAL '1 month', CURRENT_DATE, 
    TRUE 
); 

最后一个例子在我们完成的所有构建完成后看起来很简单。它对视图执行UPDATE操作,并在内部对现有员工#2进行更新,并为员工#2添加新条目。

UPDATE employee SET expiration_date = CURRENT_DATE + INTERVAL '1 year' 
    WHERE employee_id = 2 AND other_id = 2; 

我再次强调,这并不意味着不经过修改就直接使用。尽管你应该有足够的信息来为你的特定情况做些工作。

+0

是的,非常酷,乐于助人。我将以此作为参考并进行测试。谢谢! – trench

相关问题