问题摘要
这是一个关于 SQL 事务中查询的可序列化性的问题。
具体来说,我使用的是 PostgreSQL。可以假设我使用的是最新版本的 PostgreSQL。根据我的阅读,我相信用于支持我正在尝试做的事情的技术被称为“多版本并发控制”或“MVCC”。
总结一下:如果我有一个主表,并且有多个外键链接表连接到该主表,我如何保证,对于表中的给定键,以及任意数量的 SELECT 语句在一个事务中使用该键,每个事务都从任何链接表中进行选择,我将获得启动事务时存在的数据?
其他问题
这个问题类似,但范围更广,而且问题和答案并没有专门与 PostgreSQL 相关: Transaction isolation and reading from multiple tables on SQL Server Express and SQL Server 2005
例子
假设我有 3 张桌子:
bricks
brickworks (primary key)
completion_time (primary key)
has_been_sold
brick_colors
brickworks (primary key, foreign key pointing to "bricks")
completion_time (primary key, foreign key pointing to "bricks")
quadrant (primary key)
color
brick_weight
brickworks (primary key, foreign key pointing to "bricks")
completion_time (primary key, foreign key pointing to "bricks")
weight
砖厂一次生产一块砖。它在其 4 个象限中的每个象限中制作可能具有不同颜色的砖块。
稍后有人分析砖块以确定它们的颜色组合,并将结果写入brick_colors 表。
其他人分析砖块以确定它们的重量,并将结果写入brick_weight 表。
在任何给定时间,现有的砖块可能有也可能没有记录的颜色,可能有也可能没有记录的重量。
存在一个应用程序,并且该应用程序接收到有人想要购买特定砖块的消息(此时应用程序已经通过它的砖厂/完成时间组合键知道)。
应用程序希望在它开始查询的确切时间选择砖块的所有已知属性。
如果在 MID-TRANSACTION 中添加颜色或重量信息,应用程序不想知道它。
应用程序想要执行 SEPARATE QUERIES(而不是具有多个 JOIN 到外键链接表的 SELECT,这可能会因为 brick_colors 表而返回多行)。
这个例子故意简单;如果我的示例包括 10 个外键链接表,并且它们中的许多或全部可以为同一个主键返回多行(就像 brick_colors 在上面的例子)。
尝试的解决方案
到目前为止,这是我想出的:
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY ;
-- All this statement accomplishes is telling the database what rows should be returned from the present point-in-time in future queries within the transaction
SELECT DISTINCT true
FROM bricks b
LEFT JOIN brick_colors bc ON bc.brickworks = b.brickworks AND bc.completion_time = b.completion_time
LEFT JOIN brick_weight bw ON bw.brickworks = b.brickworks AND bw.completion_time = b.completion_time
WHERE b.brickworks = 'Brick-o-Matic' AND b.completion_time = '2017-02-01T07:35:00.000Z' ;
SELECT * FROM brick_colors WHERE b.brickworks = 'Brick-o-Matic' AND b.completion_time = '2017-02-01T07:35:00.000Z' ;
SELECT * FROM brick_weight WHERE b.brickworks = 'Brick-o-Matic' AND b.completion_time = '2017-02-01T07:35:00.000Z' ;
COMMIT ;
仅出于确保可序列化的目的而将第一个 SELECT 与 JOIN 一起使用似乎很浪费。
有没有其他方法可以做到这一点?