在 SQL 中插入时更新子索引
Update sub-index when inserting in SQL
我有一个 table 简化后的样子:
group index value
1 1 text 1
1 2 text 2
1 3 text 3
2 1 text 4
2 2 text 5
2 3 text 6
Group 是外键,也用于对 table 中的项目进行分组。
索引是用于对组内的项目进行排序的内部索引。
文本只是一个值。
然后如果我执行插入或 运行 执行插入的存储过程
INSERT INTO Table VALUES (1, 2, 'new text')
我想更新第 1 组项目的索引,因此 table 如下所示:
group index value
1 1 text 1
1 2 new text (inserted)
1 3 text 2 (index updated)
1 4 text 3 (index updated)
2 1 text 4
2 2 text 5
2 3 text 6
(运行 在 MS SQL-Server 2008 上)
在这个答案中,我假设第一列名称是 grp 和 idx,如 group和index是SQL.
中的保留字
您可以使用 MAX(idx)+1
公式使用下一个可用的 idx 值,如下所示:
INSERT INTO t1(grp, idx, value)
SELECT 1, IFNULL(MAX(idx),0)+1, 'new text'
FROM t1
WHERE grp = 1;
但是这样,大约同时插入的两条记录可能会得到相同的 idx 值。为避免这种情况,您可以在插入记录时锁定 table。你可以通过用 table lock/unlock 语句包装上面的语句来做到这一点:
LOCK TABLES t1;
...
UNLOCK TABLES;
但是,此方法不能在过程中使用。那里的解决方案涉及创建交易和 SELECT ... FOR UPDATE
。请注意,您需要 InnoDB Storage 为此:
CREATE PROCEDURE ins_val(IN pgrp int, IN pvalue varchar(200))
BEGIN
DECLARE newidx INT;
START TRANSACTION;
SELECT IFNULL(MAX(idx),0)+1
INTO newidx
FROM t1
WHERE grp = pgrp;
FOR UPDATE;
INSERT INTO t1(grp, idx, value)
VALUES (pgrp, newidx, pvalue);
COMMIT;
END;
请注意,如果您稍后删除记录,这些将在 idx 编号中留下空隙,上述方法将无法再次填充这些空隙。另一方面,如果删除具有最高 idx 值的记录,下一个插入的记录将重新使用该 idx 值。
这是一个您可以使用的存储过程。采用 3 个参数,grp、idx 和值:
CREATE PROCEDURE ReIndex
@grp int,
@idx int,
@value nvarchar(30)
AS
IF NOT EXISTS(SELECT 1 FROM [tbl] WHERE grp = @grp and idx =@idx)
INSERT INTO tbl VALUES (@grp, @idx, @value)
else
update tbl
set idx = idx+1
where
grp =@grp and
idx >= @idx
INSERT INTO tbl VALUES (@grp, @idx, @value)
用法:
exec reindex 1, 2, 'new text'
sqlfiddle: http://sqlfiddle.com/#!3/3323d/11
我有一个 table 简化后的样子:
group index value
1 1 text 1
1 2 text 2
1 3 text 3
2 1 text 4
2 2 text 5
2 3 text 6
Group 是外键,也用于对 table 中的项目进行分组。
索引是用于对组内的项目进行排序的内部索引。
文本只是一个值。
然后如果我执行插入或 运行 执行插入的存储过程
INSERT INTO Table VALUES (1, 2, 'new text')
我想更新第 1 组项目的索引,因此 table 如下所示:
group index value
1 1 text 1
1 2 new text (inserted)
1 3 text 2 (index updated)
1 4 text 3 (index updated)
2 1 text 4
2 2 text 5
2 3 text 6
(运行 在 MS SQL-Server 2008 上)
在这个答案中,我假设第一列名称是 grp 和 idx,如 group和index是SQL.
中的保留字您可以使用 MAX(idx)+1
公式使用下一个可用的 idx 值,如下所示:
INSERT INTO t1(grp, idx, value)
SELECT 1, IFNULL(MAX(idx),0)+1, 'new text'
FROM t1
WHERE grp = 1;
但是这样,大约同时插入的两条记录可能会得到相同的 idx 值。为避免这种情况,您可以在插入记录时锁定 table。你可以通过用 table lock/unlock 语句包装上面的语句来做到这一点:
LOCK TABLES t1;
...
UNLOCK TABLES;
但是,此方法不能在过程中使用。那里的解决方案涉及创建交易和 SELECT ... FOR UPDATE
。请注意,您需要 InnoDB Storage 为此:
CREATE PROCEDURE ins_val(IN pgrp int, IN pvalue varchar(200))
BEGIN
DECLARE newidx INT;
START TRANSACTION;
SELECT IFNULL(MAX(idx),0)+1
INTO newidx
FROM t1
WHERE grp = pgrp;
FOR UPDATE;
INSERT INTO t1(grp, idx, value)
VALUES (pgrp, newidx, pvalue);
COMMIT;
END;
请注意,如果您稍后删除记录,这些将在 idx 编号中留下空隙,上述方法将无法再次填充这些空隙。另一方面,如果删除具有最高 idx 值的记录,下一个插入的记录将重新使用该 idx 值。
这是一个您可以使用的存储过程。采用 3 个参数,grp、idx 和值:
CREATE PROCEDURE ReIndex
@grp int,
@idx int,
@value nvarchar(30)
AS
IF NOT EXISTS(SELECT 1 FROM [tbl] WHERE grp = @grp and idx =@idx)
INSERT INTO tbl VALUES (@grp, @idx, @value)
else
update tbl
set idx = idx+1
where
grp =@grp and
idx >= @idx
INSERT INTO tbl VALUES (@grp, @idx, @value)
用法:
exec reindex 1, 2, 'new text'
sqlfiddle: http://sqlfiddle.com/#!3/3323d/11