我有一个看起来像这样的表:
CREATE TABLE SCHEDULE (
RCDNO INTEGER NOT NULL,
MASTCONNO INTEGER NOT NULL,
ROWNO INTEGER DEFAULT 0 NOT NULL,
EDITTIMESTAMP TIMESTAMP
)
ALTER TABLE SCHEDULE ADD CONSTRAINT PK_SCHEDULE PRIMARY KEY (RCDNO);
CREATE UNIQUE INDEX IDX_SCHEDULE_3 ON SCHEDULE (MASTCONNO, ROWNO);
我可以运行这样的查询:
select mastconno, rowno, rcdno, edittimestamp
from schedule
where mastconno = 12
order by rowno desc
我得到了这个:
由于应用代码中存在错误,因此缺少edittimestamp的时间部分,但这并不重要。尽管如此,记录仍以ROWNO的进入顺序列出。这就是本表的设计旨在促进。
我试图做的是......
update SCHEDULE
set ROWNO = (ROWNO + 1)
where MASTCONNO = 12
...为准备插入新的ROWNO = 0记录,我收到此错误:
attempt to store duplicate value (visible to active transactions) in unique index "IDX_SCHEDULE_3".
Problematic key value is ("MASTCONNO" = 12, "ROWNO" = 3).
我在MS SQL Server中有一个完整的表格副本,我没有遇到这个问题。这似乎与Firebird的工作方式有关。
然后我尝试了这一点,希望Firebird能够以非违规顺序将IN
谓词中的值“馈送”到UPDATE
。
update SCHEDULE
set ROWNO = (ROWNO + 1)
where MASTCONNO = 12 and
ROWNO in (select ROWNO from SCHEDULE
where MASTCONNO = 12 order by ROWNO DESC)
可悲的是,回应和以前一样。 ROWNO 3正在被更新声明复制。
答案 0 :(得分:3)
使用唯一索引(MASTCONNO,ROWNO),我测试了以下内容:
update SCHEDULE
set ROWNO = (ROWNO + 1)
where MASTCONNO = 12
order by ROWNO DESC
正常工作!
update SCHEDULE
set ROWNO = (ROWNO - 1)
where MASTCONNO = 12
order by ROWNO ASC
也正常工作!
非常感谢Mark Rotteveel。
答案 1 :(得分:2)
实际上,约束检查和触发Firebird每行都运行,而不是每个事务或每个表。
因此,您必须使用在处理列表和数组的应用程序中使用的基于循环的方法。
您必须使用EXECUTE BLOCK
和正确定向的循环。
您获取感兴趣的MAXimum ID,然后递增它。 然后你采取以前的价值。 然后是......
EXECUTE BLOCK
AS
DECLARE ID INTEGER;
BEGIN
ID = ( SELECT MAX(ROWNO) FROM SCHEDULE WHERE MASTCONNO = 12 );
/*
ID = NULL;
SELECT MAX(ROWNO) FROM SCHEDULE WHERE MASTCONNO = 12 INTO :ID;
IF (ID IS NULL) raise-some-error-somehow
*/
While (ID >= 0)
Begin
update SCHEDULE set ROWNO = (ID + 1)
where MASTCONNO = 12 and :ID = ROWNO;
ID = ID - 1;
End
END
危险!
如果您在开始时正在执行另一个事务(可能来自在另一台计算机上工作的另一个程序),请插入一些带有MASTCONNO = 12
的新行并提交它 - 您有问题。
Firebird是多版本服务器,而不是表阻塞服务器,因此服务器中没有任何内容禁止此插入,因为您的过程正在对表进行处理。然后你有竞争条件,最快的事务会提交自己,而较慢的事件会因唯一索引违规而失败。
您也可以使用FOR-SELECT循环而不是WHILE循环。
喜欢这个
EXECUTE BLOCK
AS
BEGIN
FOR
SELECT * FROM SCHEDULE
WHERE MASTCONNO = 12
ORDER BY ROWNO DESCENDING -- proper loop direction: ordering is key!
AS CURSOR PTR
DO
UPDATE SCHEDULE SET ROWNO = ROWNO + 1
WHERE CURRENT OF PTR;
END
然而,在Firebird 3中,基于游标的定位变得相当缓慢。