了解sql

时间:2017-02-19 23:15:39

标签: sql postgresql

关系R(x)由一组整数组成 - 即具有整数分量的单组件元组。 Alice的交易是一个查询:

SELECT SUM(x) FROM R;
COMMIT;

贝蒂的交易是一系列插入:

INSERT INTO R VALUES(10);
INSERT INTO R VALUES(20);
INSERT INTO R VALUES(30);
COMMIT;

Carol的交易是一系列删除:

DELETE FROM R WHERE x=30;
DELETE FROM R WHERE x=20;
COMMIT;

在执行任何这些事务之前,R中的整数之和为1000,并且这些整数都不是10,20或30.如果Alice's,Betty和Carol的交易大约在同一时间运行,并且每个在隔离级别READ COMMITTED下运行,哪些总和可以由Alice的交易产生?从下面的列表中确定其中一个总和。

一)1040 B)950 C)1030 d)1080

这个问题对我来说有点混乱,因为没有任何东西告诉我们哪个事务首先提交或者它们是否同时提交。你怎么解释这个问题?

3 个答案:

答案 0 :(得分:1)

这个问题有两条规则可以发挥作用:

  • 读取提交的隔离级别:在事务内部,每个SQL语句 看到一个新版本的数据(一个新的“快照”),包括另一个的变化 并发事务可能刚刚提交。 这意味着要回答这个问题,必须考虑每个交易中的报表的关联顺序,而不仅仅是每个交易开始或结束时。

  • 原子性:其他人看不到交易中的更改 交易直到它提交。脏的读取是不可能的 PostgreSQL,如Transaction Isolation

  • 上的文档章节所述

让我们列举一下这些陈述:

 Alice
A1: SELECT SUM(x) FROM R;
A2: COMMIT;

 Betty
B1: INSERT INTO R VALUES(10);
B2: INSERT INTO R VALUES(20);
B3: INSERT INTO R VALUES(30);
B4: COMMIT;

 Carol
C1: DELETE FROM R WHERE x=30;
C2: DELETE FROM R WHERE x=20;
C3: COMMIT;

对于每个值a)1040 b)950 c)1030 d)1080,您要查找是否存在执行这些语句的顺序,以便A1步骤中的SELECT SUM(x) FROM R恰好产生该值,如上所述交易规则。

<强> A = 1040

1040可以按此执行顺序(以及其他)读取:

B1
B2
C1 -- DELETE WHERE x=30 does nothing because x=30 does not exist in R.
B3 
B4 -- now A1 would see 10,20,30 in R so SUM(x) is 1060 at this point
C2 -- DELETE WHERE x=20 does remove x=20 which exists and is visible by C
C3 -- commits deletion of x=20
A1 -- now A1 can see 10,30 so SUM(x)=1040
A2 -- commit (no effect, A only reads)
编辑:有些人不相信这个结果是可能的,所以请在问题的底部看到从postgres会话中复制粘贴的实际演示。

<强> B = 950

值20,30不在表中,因此如果C1和C2可以删除30和20,那将是因为B2和B3添加了它们,如果他们这样做,则B1首先添加10,所以结果至少是960。 在C1和C2因为尚未写入而无法移除30或20的情况下,结果SUM(x)必然大于960。 所以A1永远不会看到小到950的SUM(x),这是不可能的。

<强> C = 1030

对于要由A1读取的1030,它必须在表中看到两行(10),(20)或一行(30),不包括任何其他行,因为其他组合不总和达到30。 / p>

由于原子性,B1,B2,B3插入的(10,20,30)行将同时可见。 C1和C2可以分别撤消B3和B2的效果,但是如果C1找到并删除x = 30,那么C2也必须找到并删除x = 20。 在C3运行之前A1无法看到C1的结果,如果C3已经运行,C2也有,所以30和20都从A的可见性消失,所以它只看到(10),而不是(10),(20) 。 单独看(30)也是不可能的,因为如果(30)存在,那么(10)也是如此, 由于x = 10由B1插入,之后从未删除。

所以A1永远不会看到SUM(x)= 1030。

d = 1080

1000 + 10 + 20 + 30 = 1060并且代码不再添加任何行,因此无论执行顺序如何,都不可能获得超过1060个。

编辑:

使用postgresql 9.5的实际演示

# Init 
$ psql -d test

test=# create table R(x int);
CREATE TABLE
test=# insert into R values(1000);
INSERT 0 1
test=# show transaction_isolation ;
 transaction_isolation 
-----------------------
 read committed
(1 row)

test=# \q

**启动3个不同的并发会话A,B,C,用不同的提示来区分:

$ psql -d test
\set PROMPT1 'alice%R%# '
alice=# 

$ psql -d test
\set PROMPT1 'betty%R%# '
betty=# 

$ psql -d test
\set PROMPT1 'carol%R%# '
carol=# 

现在让我们按照上面提到的顺序运行命令,通过提示显示什么会话显而易见。

alice=# begin;
BEGIN

betty=# begin;
BEGIN

carol=# begin;
BEGIN

betty=# INSERT INTO R VALUES(10);
INSERT 0 1

betty=# INSERT INTO R VALUES(20);
INSERT 0 1

carol=# DELETE FROM R WHERE x=30;
DELETE 0

betty=# INSERT INTO R VALUES(30);
INSERT 0 1

betty=# commit;
COMMIT

carol=# DELETE FROM R WHERE x=20;
DELETE 1

carol=# commit;
COMMIT

alice=# select sum(x) from r;
 sum  
------
 1040
(1 row)

alice=# commit;
COMMIT

答案 1 :(得分:0)

如果它们同时运行并同时提交,则Alice应该获得1000的值。这是因为隔离级别是READ COMMITTED,它不会读取任何插入或删除的其他交易,直到它们被提交。

这实际上取决于COMMIT的顺序。在不知情的情况下,您无法确切知道答案是什么。

所以,我的答案是E)1000。你可以向谁提出这个问题澄清提交命令是什么?

答案 2 :(得分:0)

对于CRUD交易,没有“大致相同的时间”。

只要它是READ COMMITTED它取决于在Alice的SELECT运行之前首先提交的CRUD事务。没有答案是正确的。

对于SELECT事务,COMMIT没有意义。

只有3个可能的答案

1000,1060,1010