我一直在使用Python在内存中执行此操作,但我想知道在Postgres中设置员工映射表的正确方法。
row_id | employee_id | other_id | other_dimensions | effective_date | expiration_date | is_current
很想听听一些最佳做法,所以我可以放弃基于文件的方法,在那里我将整个名单读入内存并使用pandas进行更改,然后截断原始表并插入新表。
答案 0 :(得分: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);
现在我们定义视图。对于世界上大多数人来说,这将是访问员工数据的常用方式。在内部,它是针对我们已经定义的基础SELECT
表的特殊raw_employee
运行。如果我们有理由,我们可以进一步优化此视图以隐藏更多数据(它已经隐藏了前面提到的低级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;
我再次强调,这并不意味着只是在没有修改的情况下使用和使用。这里应该有足够的信息,但是你可以根据具体情况制作一些东西。