想象一下,我有下表:
ID || Order
-----------
1 || 1
2 || 2
3 || 5
4 || 20
5 || 100
6 || 4000
(没有具体规则适用于订单价值)。
我希望通过交换订单值来“向上移动”“向下移动”项目。
例如:调用MoveItemUp(4)
会产生新的表值:
ID || Order
-----------
1 || 1
2 || 2
3 || 20 <-- swapped order value
4 || 5 <-- swapped order value
5 || 100
6 || 4000
我想在一个查询中执行此操作,但我还没有成功。
如果项目顺序是顺序的,没有“漏洞”(步骤1:)
,则以下查询有效UPDATE dbo.ITEMS
set ORDER = case when c.ORDER = c2.ORDER then c.ORDER +1 else c.ORDER -1 end
from dbo.ITEMS c
inner join dbo.ITEMS c2 on c.ORDER = c2.ORDER or c.ORDER = c2.ORDER + 1
where c2.ID=4
但是,我无法更改此查询以支持漏洞。我正在尝试:
UPDATE dbo.ITEMS
set case when c.ORDER = c2.ORDER then min(c2.ORDER ) else c2.ORDER end
FROM dbo.ITEMS c
inner join ITEMS c2 on c2.ORDER >= c.ORDER
where c2.ID=4
group by c.CAT_ID, c.ORDER
having c.ORDER = min(c2.ORDER ) or c.ORDER = c2.ORDER
但是,这不能按预期工作(查询会更新所有具有更高订单的项目,而不是交换两个订单)。
PS:我在Sybase ASE 4.5上使用C#2.0,但我认为这个问题不是针对这个平台的。如果您有MSSQL,MySql或Oracle等价物,我会努力转换它;)
答案 0 :(得分:0)
注意以下所有解决方案均假设ItemOrder是唯一的
编辑添加一个更像OP正在尝试的解决方案,并且可能更容易移植到Sybase,这次是在Microsoft SQL Server 2008上。(请参阅下面的使用Oracle分析功能的解决方案,可能效率更高。)
首先选择以使我们的行选择标准正确:
declare @MoveUpId int
set @MoveUpId = 4
select current_row.Id
, current_row.ItemOrder
, prior_row.id as PriorRowId
, prior_row.ItemOrder as PriorItemOrder
, next_row.id as NextRowId
, next_row.ItemOrder as NextItemOrder
from #Items current_row
left outer join #Items prior_row
on prior_row.ItemOrder = (select max(ItemOrder)
from #Items
where ItemOrder < current_row.ItemOrder)
left outer join #Items next_row
on next_row.ItemOrder = (select min(ItemOrder)
from #Items
where ItemOrder > current_row.ItemOrder)
where @MoveUpId in (current_row.id, next_row.id)
然后根据以上内容进行更新:
update current_row
set ItemOrder = case
when current_row.Id = @MoveUpId then prior_row.ItemOrder
else next_row.ItemOrder end
from #Items current_row
left outer join #Items prior_row
on prior_row.ItemOrder = (select max(ItemOrder)
from #Items
where ItemOrder < current_row.ItemOrder)
left outer join #Items next_row
on next_row.ItemOrder = (select min(ItemOrder)
from #Items
where ItemOrder > current_row.ItemOrder)
where @MoveUpId in (current_row.id, next_row.id)
Id ItemOrder
1 1
2 2
3 20
4 5
5 100
6 4000
10 -1
20 -2
将@MoveUpId
设置为20,然后在以下位置重新运行查询结果:
Id ItemOrder
1 1
2 2
3 20
4 5
5 100
6 4000
10 -2
20 -1
但我认为此问题并非针对此平台。问题可能不具体,但答案可能是。例如,使用Oracle,首先是表和一些测试数据:
create table Items (Id number(38) not null
, ItemOrder number);
insert into items values (1, 1);
insert into items values (2, 2);
insert into items values (3, 5);
insert into items values (4, 20);
insert into items values (5, 100);
insert into items values (6, 4000);
insert into items values (10, -1);
insert into items values (20, -2);
commit;
接下来创建一个查询,该查询仅返回我们要更新的行及其Order
的新值。 (我将其命名为ItemOrder,Order是一个保留字和all。)在Oracle中,使用分析函数lag
和lead
最简单:
select *
from (select Id
, ItemOrder
, lead(Id) over (order by Id) as LeadId
, lead(ItemOrder) over (order by Id) as LeadItemOrder
, lag(ItemOrder) over (order by Id) as LagItemOrder
from Items)
where 4 in (Id, LeadId)
order by Id;
ID ITEMORDER LEADID LEADITEMORDER LAGITEMORDER
---------- ---------- ---------- ------------- ------------
3 5 4 20 2
4 20 5 100 5
将其转换为更新语句。但是,上述查询不会创建可更新的视图(在Oracle中),因此请改用merge:
merge into Items TRGT
using (select Id
, ItemOrder
, lead(Id) over (order by Id) as LeadId
, lead(ItemOrder) over (order by Id) as LeadItemOrder
, lag(ItemOrder) over (order by Id) as LagItemOrder
from Items) SRC
on (SRC.Id = TRGT.Id)
when matched then update
set ItemOrder = case TRGT.Id
when 4 then SRC.LagItemOrder
else SRC.LeadItemOrder end
where 4 in (SRC.Id, SRC.LeadId);
select * from Items order by Id;
ID ITEMORDER
---------- ----------
1 1
2 2
3 20
4 5
5 100
6 4000
10 -1
20 -2
不幸的是,我不相信滞后和领导被广泛实施。据我所知,Microsoft SQL Server尚未实现它们。没有ASE的经验,他们有很好的。
Row_number()的实施范围更广。 Row_number()可以用来获得无间隙的东西。 (Row_number()被称为Oracle上的分析函数和SQL Server上的窗口函数。)首先查询:
with t as (select Id
, ItemOrder
, row_number() over (order by Id) as RN
from Items)
select current_row.id
, current_row.ItemOrder
, next_row.Id as NextId
, next_row.ItemOrder NextItemOrder
, prior_row.ItemOrder PriorItemOrder
from t current_row
left outer join t next_row on next_row.RN = current_row.RN + 1
left outer join t prior_row on prior_row.RN = current_row.RN - 1
where 4 in (current_row.id, next_row.id);
ID ITEMORDER NEXTID NEXTITEMORDER PRIORITEMORDER
---------- ---------- ---------- ------------- --------------
3 5 4 20 2
4 20 5 100 5
使用merge而不是update进行更新。 (Oracle确实允许使用update ... from ... join ...
语法,可以使用更新而不是在其他平台上进行合并。)
merge into Items TRGT
using (with t as (select Id
, ItemOrder
, row_number() over (order by Id) as RN
from Items)
select current_row.id
, current_row.ItemOrder
, next_row.Id as NextId
, next_row.ItemOrder as NextItemOrder
, prior_row.ItemOrder as PriorItemOrder
from t current_row
left outer join t next_row on next_row.RN = current_row.RN + 1
left outer join t prior_row on prior_row.RN = current_row.RN - 1
where 4 in (current_row.id, next_row.id)) SRC
on (TRGT.Id = SRC.Id)
when matched then update
set ItemOrder = case
when TRGT.Id = 4 then SRC.PriorItemOrder
else SRC.NextItemOrder end;
select *
from Items
order by Id;
ID ITEMORDER
---------- ----------
1 1
2 2
3 20
4 5
5 100
6 4000
10 -1
20 -2
注意请注意,如果匹配第一行的Id,上面的解决方案将在OrderItems上写入null。