添加 MERGE 命令 GSoC 2010
MERGE 命令
这从未集成到 PostgreSQL 中,需要大量工作才能达到生产质量
SQL MERGE 命令对 PosgreSQL 来说是一个迫切的需求。MERGE 用于根据用户定义的匹配条件将两个表的內容合并到一个表中。
作为 GSoC 2010 的学生,我建议将 MERGE 命令添加到 PostgreSQL 中。现在工作已经接近尾声。
如果您对这个项目感兴趣,您可以在这里找到我们的 MergeImplementationDetails 和 MergeTestExamples。
最新的补丁文件可以在这里下载:
MERGE 语法
我参考了 Simon Riggs 先生提出的语法 [[1]],如下所示:
MERGE INTO table [[AS] 别名]
USING [table-ref | 查询]
ON 连接条件
[WHEN MATCHED [AND condition] THEN MergeUpdate | DELETE | DO NOTHING | RAISE ERROR]
[WHEN NOT MATCHED [AND condition] THEN MergeInsert | DO NOTHING | RAISE ERROR]
MergeUpdate 是
UPDATE SET { 列 = { 表达式 | DEFAULT } |
( 列 [, ...] ) = ( { 表达式 | DEFAULT } [, ...] ) }
[, ...]
(是的,这里没有 WHERE 子句)
MergeInsert 是
INSERT [ ( 列 [, ...] ) ]
{ DEFAULT VALUES | VALUES ( { 表达式 | DEFAULT } [, ...] )
[, ...]}
(不允许子查询)
注意
1. 同一类型的操作可以多次出现。也就是说,您可以在一个 MERGE 中使用两个 UPDATE。
2. 每个源表元组只会被匹配一次,并且只会导致一个操作。如果一个元组符合多个操作的条件,则将执行第一个操作,而其余操作将被忽略。
3. 当 MATCHED 时,INSERT 操作不可用。此外,UPDATE 和 DELETE 操作仅适用于 MATCHED 案例。
4. DO NOTHING 和 RAISE ERROR 可用于 MATCHED 和 NOT MATCHED 情况。
提案
实现的一般思路
命令定义很明确:我们尝试将要修改的表(目标表)和作为输入的表(源表)连接起来,根据匹配或不匹配条件执行不同的操作。
我进行此操作的一般思路如下:我们在解析器中对目标表和源表构建一个交叉连接查询,称为主查询。并将“WHEN”操作列表打包为更新、删除或插入查询(侧查询),其中 MATCHED 被替换为 ON 连接条件。
使用原始规划器为主查询和侧查询生成查询计划。将所有这些计划放入外部计划结构中,并附加一个计划标签作为 MERGE 命令的新类型。
在执行器中,我们检查主查询返回的每个元组是否符合每个侧查询的所有条件。如果符合条件,则相应地执行操作。
例如,如果我们收到如下所示的 MERGE 查询:
MERGE INTO Stock S
USING DailySales DS ON S.Item = DS.Item
WHEN MATCHED AND (QtyOnHand ‐ QtySold = 0) THEN DELETE
WHEN MATCHED THEN UPDATE SET QtyOnHand = QtyOnHand ‐ QtySold
WHEN NOT MATCHED THEN INSERT VALUES (Item, QtySold);
我们可以执行类似的主查询:
SELECT * FROM Stock S, DailySales DS;
侧查询如下:
1. DELETE FROM Stock S USING DailySales DS WHERE S.item = DS.item AND (QtyOnHand ‐ QtySold = 0);
2. UPDATE Stock S SET QtyOnHand = QtyOnHand – QtySold FROM DailySales DS WHERE S.item = DS.item;
3. INSERT INTO Stock S SELECT Item, QtySold FROM DailySalse DS WHERE not (S.item = DS.item);
请注意,我们不会直接逐个运行侧查询。我们只运行主查询并在主查询的连接循环中执行侧查询。
工作计划
1. 更新 backend/parser/gram.y。在解析器中添加 SQL 风格的 MERGE 命令(遵循上面的定义)。应设计一个新的“MergeStmt”结构来保存转换后的命令信息。除了目标表名和 ON 连接条件外,我们还需要至少一个“RangeSubselect”或“RangeVar”结构来保存源表和一个用于侧查询的 UpdateStmt、InsertStmt 和/或 DeleteStmt 列表。
2. 在 analyze.c 文件中,我们需要添加一个函数将此 MergeStmt 转换为 Query 节点。
有必要为 MERGE 添加一种新的命令类型,这是一种可规划命令。我们需要检查语句的语义正确性。我正在考虑将目标表和源表组合成一个完整的 SELECT 查询。
如果没有 NOT MATCH 选项,我们可以生成类似于以下内容的普通查询节点:
“SELECT * FROM target, source WHERE 匹配条件;”
或者,如果我们想要处理一些 NOT MATCH 操作,我们必须进行交叉连接,这将执行类似于“SELECT * FROM target, source;”的查询。
好处是,我们可以几乎完全重用重写器和规划器来将此生成的查询转换为执行器可接受的结构。
3. 查询需要一个计划。规划器应该接受这个新的可规划命令。但是,如上所述,实际的工作将是:对格式化后的主查询(将目标表和源表连接在一起)进行传统的查询计划。侧查询也以这种方式处理。最后将这些计划打包在一个外部“PlannedStmt”中,该结构专门为 MERGE 命令设计。
4. 在执行器中,基本操作是:对于主查询返回的每个元组(该元组包含源表和目标表中的所有属性),我们可以根据不同侧查询中的条件对其进行测试,并根据测试结果执行相应的操作。
时间线
第 1 周:修改 gram.y,添加命令的语法定义
第 2-3 周:在解析器中添加转换程序
第 4-5 周:修改规划器,使其接受 MERGE 命令查询
第 6-8 周:执行执行器实现,每周一种操作。
第 9-10 周:灵活的用于调试和改进的时间。
TODO
1. 针对 merge 的继承
2. 返回?