添加行时在列中重新编号 sql
renumbering in a column when adding a row sql
一个table喜欢
create table Stations_in_route
(
ID_station_in_route int primary key,
ID_route int,
ID_station int,
Number_in_route int not null
)
有如下触发器在路由添加新行后改变Number_in_route列中的值。路由中的号码列表必须保持一致。
create trigger stations_in_route_after_insert on Stations_in_route
after insert
as
if exists
(select *from Stations_in_route
where Stations_in_route.ID_station_in_route not in (select ID_station_in_route from inserted)
and Stations_in_route.ID_route in (select ID_route from inserted)
and Stations_in_route.Number_in_route in (select Number_in_route from inserted))
begin
update Stations_in_route
set Number_in_route = Number_in_route + 1
where Stations_in_route.ID_station_in_route not in (select ID_station_in_route from inserted)
and Stations_in_route.ID_route in (select ID_route from inserted)
and Stations_in_route.Number_in_route >= (select Number_in_route from inserted where Stations_in_route.ID_route = inserted.ID_route)
end
如果执行插入一个 ID_route,此触发器将抛出错误:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
例如,
Insert into Stations_in_route values(25, 4, 11, 3),(26, 4, 10, 5)
如何修复?
ID_station_in_route
ID_route
ID_station
Number_in_route
1
4
1
1
2
4
2
2
3
4
3
3
4
4
4
4
5
4
5
5
6
4
6
6
7
4
7
7
8
4
8
8
希望添加后的列表会变成这样
ID_station_in_route
ID_route
ID_station
Number_in_route
1
4
1
1
2
4
2
2
25
4
11
3
3
4
3
4
26
4
10
5
4
4
4
6
5
4
5
7
6
4
6
8
7
4
7
9
8
4
8
10
这不是全部table,因为还有其他路线
根据要求,当您向路线添加新停靠点时,您需要将它们正确插入到所需的顺序中,并将所有现有停靠点从该点向前推,以便保持连续的顺序。当你插入一行时,这不是很难(只是 number_in_route + 1 where number_in_route > new_number_in_route
),但是当你插入更多行时,你基本上需要将整个后续停止点集推 1 for each 新行。为了说明这一点,假设您从这个开始:
如果我们插入两个新行,比如:
INSERT dbo.Stations_in_route
(
ID_station_in_route,
ID_route,
ID_station,
Number_in_route
)
VALUES (25, 4, 11, 3),(26, 4, 10, 5);
-- add a stop at 3 ^ ^
----------------- add a stop at 5 ^
我们可以通过将其放慢为单独的步骤来说明这一点。首先,我们需要在位置 #3 添加这一行:
我们通过将所有行 > 3 向下推 1 来做到这一点:
但是现在当我们在位置 #5 添加这一行时:
那是 新 位置 #5,在 之前的班次之后,所以它看起来像这样:
我们可以使用以下触发器来执行此操作,这可能比必须的要复杂一些,但恕我直言,这比可能需要的繁琐循环要好。
CREATE TRIGGER dbo.tr_ins_Stations_in_route ON dbo.Stations_in_route
FOR INSERT AS
BEGIN
;WITH x AS
(
SELECT priority = 1, *, offset = ROW_NUMBER() OVER
(PARTITION BY ID_route ORDER BY Number_in_route)
FROM inserted AS i
UNION ALL
SELECT priority = 2, s.*, offset = NULL FROM dbo.Stations_in_route AS s
WHERE s.ID_route IN (SELECT ID_route FROM inserted)
),
y AS
(
SELECT *, rough_rank = Number_in_route
+ COALESCE(MAX(offset) OVER (PARTITION BY ID_Route
ORDER BY Number_in_route ROWS UNBOUNDED PRECEDING),0)
- COALESCE(offset, 0),
tie_break = ROW_NUMBER() OVER
(PARTITION BY ID_route, ID_station_in_route ORDER BY priority)
FROM x
),
z AS
(
SELECT *, new_Number_in_route = ROW_NUMBER() OVER
(PARTITION BY ID_Route ORDER BY rough_rank, priority)
FROM y WHERE tie_break = 1
)
UPDATE s SET s.Number_in_route = z.new_Number_in_route
FROM dbo.Stations_in_route AS s
INNER JOIN z ON s.ID_route = z.ID_route
AND s.ID_station_in_route = z.ID_station_in_route;
END
- 工作示例db<>fiddle
我已经多次提到您可能想要处理新行的联系,例如如果插入恰好是:
Insert into Stations_in_route values(25, 4, 11, 3),(26, 4, 10, 3)
为此,您可以在此子句中添加额外的打破平局的标准:
new_Number_in_route = ROW_NUMBER() OVER
(PARTITION BY ID_Route ORDER BY rough_rank, priority)
例如:
new_Number_in_route = ROW_NUMBER() OVER
(PARTITION BY ID_Route ORDER BY rough_rank, priority,
ID_station_in_route DESC)
我无法通过问题中的测试 code/data 重现异常,但是我猜问题出在触发器中的这段代码上:
AND Stations_in_route.Number_in_route >=
(
SELECT Number_in_route
FROM inserted
WHERE Stations_in_route.ID_route = inserted.ID_route
)
那里的引擎将隐含地期望 >= 运算符右侧的子查询到 return 标量结果(单行,单列结果),但是插入的 table 是实际上,一个 table... 可能包含多个记录(如示例中概述的多行 insert/update/etc.type 语句中的情况)。鉴于该子查询中的过滤器(即 WHERE 子句)不能保证是唯一的(ID_route 似乎不是唯一的,并且在您的示例中,您有一个 insert 语句实际上插入了具有相同的多行ID_route 值),那么查询肯定有可能 return 一个非标量结果。
要解决此问题,您需要调整该子查询以保证标量值(单行和单列)的结果。您已经使用选择器保证单列...现在您需要添加逻辑来保证单个 result/record 。这可能包括以下一项或多项(也可能包括其他内容):
- 将选定的 Number_in_route 列包装在聚合函数中(即 MAX() 或许?)
- 添加带有 ORDER BY 的 TOP 1 以获得您要与之比较的记录
- 向 WHERE 子句添加额外的过滤器以确保return编辑
单个结果
一个table喜欢
create table Stations_in_route
(
ID_station_in_route int primary key,
ID_route int,
ID_station int,
Number_in_route int not null
)
有如下触发器在路由添加新行后改变Number_in_route列中的值。路由中的号码列表必须保持一致。
create trigger stations_in_route_after_insert on Stations_in_route
after insert
as
if exists
(select *from Stations_in_route
where Stations_in_route.ID_station_in_route not in (select ID_station_in_route from inserted)
and Stations_in_route.ID_route in (select ID_route from inserted)
and Stations_in_route.Number_in_route in (select Number_in_route from inserted))
begin
update Stations_in_route
set Number_in_route = Number_in_route + 1
where Stations_in_route.ID_station_in_route not in (select ID_station_in_route from inserted)
and Stations_in_route.ID_route in (select ID_route from inserted)
and Stations_in_route.Number_in_route >= (select Number_in_route from inserted where Stations_in_route.ID_route = inserted.ID_route)
end
如果执行插入一个 ID_route,此触发器将抛出错误:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
例如,
Insert into Stations_in_route values(25, 4, 11, 3),(26, 4, 10, 5)
如何修复?
ID_station_in_route | ID_route | ID_station | Number_in_route |
---|---|---|---|
1 | 4 | 1 | 1 |
2 | 4 | 2 | 2 |
3 | 4 | 3 | 3 |
4 | 4 | 4 | 4 |
5 | 4 | 5 | 5 |
6 | 4 | 6 | 6 |
7 | 4 | 7 | 7 |
8 | 4 | 8 | 8 |
希望添加后的列表会变成这样
ID_station_in_route | ID_route | ID_station | Number_in_route |
---|---|---|---|
1 | 4 | 1 | 1 |
2 | 4 | 2 | 2 |
25 | 4 | 11 | 3 |
3 | 4 | 3 | 4 |
26 | 4 | 10 | 5 |
4 | 4 | 4 | 6 |
5 | 4 | 5 | 7 |
6 | 4 | 6 | 8 |
7 | 4 | 7 | 9 |
8 | 4 | 8 | 10 |
这不是全部table,因为还有其他路线
根据要求,当您向路线添加新停靠点时,您需要将它们正确插入到所需的顺序中,并将所有现有停靠点从该点向前推,以便保持连续的顺序。当你插入一行时,这不是很难(只是 number_in_route + 1 where number_in_route > new_number_in_route
),但是当你插入更多行时,你基本上需要将整个后续停止点集推 1 for each 新行。为了说明这一点,假设您从这个开始:
如果我们插入两个新行,比如:
INSERT dbo.Stations_in_route
(
ID_station_in_route,
ID_route,
ID_station,
Number_in_route
)
VALUES (25, 4, 11, 3),(26, 4, 10, 5);
-- add a stop at 3 ^ ^
----------------- add a stop at 5 ^
我们可以通过将其放慢为单独的步骤来说明这一点。首先,我们需要在位置 #3 添加这一行:
我们通过将所有行 > 3 向下推 1 来做到这一点:
但是现在当我们在位置 #5 添加这一行时:
那是 新 位置 #5,在 之前的班次之后,所以它看起来像这样:
我们可以使用以下触发器来执行此操作,这可能比必须的要复杂一些,但恕我直言,这比可能需要的繁琐循环要好。
CREATE TRIGGER dbo.tr_ins_Stations_in_route ON dbo.Stations_in_route
FOR INSERT AS
BEGIN
;WITH x AS
(
SELECT priority = 1, *, offset = ROW_NUMBER() OVER
(PARTITION BY ID_route ORDER BY Number_in_route)
FROM inserted AS i
UNION ALL
SELECT priority = 2, s.*, offset = NULL FROM dbo.Stations_in_route AS s
WHERE s.ID_route IN (SELECT ID_route FROM inserted)
),
y AS
(
SELECT *, rough_rank = Number_in_route
+ COALESCE(MAX(offset) OVER (PARTITION BY ID_Route
ORDER BY Number_in_route ROWS UNBOUNDED PRECEDING),0)
- COALESCE(offset, 0),
tie_break = ROW_NUMBER() OVER
(PARTITION BY ID_route, ID_station_in_route ORDER BY priority)
FROM x
),
z AS
(
SELECT *, new_Number_in_route = ROW_NUMBER() OVER
(PARTITION BY ID_Route ORDER BY rough_rank, priority)
FROM y WHERE tie_break = 1
)
UPDATE s SET s.Number_in_route = z.new_Number_in_route
FROM dbo.Stations_in_route AS s
INNER JOIN z ON s.ID_route = z.ID_route
AND s.ID_station_in_route = z.ID_station_in_route;
END
- 工作示例db<>fiddle
我已经多次提到您可能想要处理新行的联系,例如如果插入恰好是:
Insert into Stations_in_route values(25, 4, 11, 3),(26, 4, 10, 3)
为此,您可以在此子句中添加额外的打破平局的标准:
new_Number_in_route = ROW_NUMBER() OVER
(PARTITION BY ID_Route ORDER BY rough_rank, priority)
例如:
new_Number_in_route = ROW_NUMBER() OVER
(PARTITION BY ID_Route ORDER BY rough_rank, priority,
ID_station_in_route DESC)
我无法通过问题中的测试 code/data 重现异常,但是我猜问题出在触发器中的这段代码上:
AND Stations_in_route.Number_in_route >=
(
SELECT Number_in_route
FROM inserted
WHERE Stations_in_route.ID_route = inserted.ID_route
)
那里的引擎将隐含地期望 >= 运算符右侧的子查询到 return 标量结果(单行,单列结果),但是插入的 table 是实际上,一个 table... 可能包含多个记录(如示例中概述的多行 insert/update/etc.type 语句中的情况)。鉴于该子查询中的过滤器(即 WHERE 子句)不能保证是唯一的(ID_route 似乎不是唯一的,并且在您的示例中,您有一个 insert 语句实际上插入了具有相同的多行ID_route 值),那么查询肯定有可能 return 一个非标量结果。
要解决此问题,您需要调整该子查询以保证标量值(单行和单列)的结果。您已经使用选择器保证单列...现在您需要添加逻辑来保证单个 result/record 。这可能包括以下一项或多项(也可能包括其他内容):
- 将选定的 Number_in_route 列包装在聚合函数中(即 MAX() 或许?)
- 添加带有 ORDER BY 的 TOP 1 以获得您要与之比较的记录
- 向 WHERE 子句添加额外的过滤器以确保return编辑 单个结果