如何在一个查询中获取一项和另一项的值最接近前一项的项?
想象一下我有下表:(
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 同等产品,我会尽力将其转换;)
Imagine I have the following table :
ID || Order
-----------
1 || 1
2 || 2
3 || 5
4 || 20
5 || 100
6 || 4000
(no specific rule applies to the order value).
I want to "move up" ou "move down" items by swapping order values.
Ex: a call to MoveItemUp(4)
will results in this new table values :
ID || Order
-----------
1 || 1
2 || 2
3 || 20 <-- swapped order value
4 || 5 <-- swapped order value
5 || 100
6 || 4000
I want to do this in a single query, but I was not yet successful.
The following query works if items order are sequential, with no "hole" (steps of 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
However, I was not able to change this query to support hole. I'm trying to do :
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
However, this does not works as expected (the query updates all items having a greater order instead of the two orders to swap).
PS: I'm working with C# 2.0 on Sybase ASE 4.5, but I assume this question is not specific to this platform. If you have a MSSQL, MySql or Oracle equivalent, I'll put effort to convert it ;)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
注意以下所有解决方案都假设 ItemOrder 是唯一的
编辑添加一个更像OP尝试的解决方案,并且可能更容易移植到Sybase,这次是在Microsoft SQL Server上2008。(请参阅下面使用 Oracle 分析函数的解决方案,如果可用的话可能会更有效。)
首先选择以使我们的行选择标准正确:
然后根据上述进行更新:
设置@MoveUpId到20 并重新运行上面的查询结果:
但我认为这个问题不是特定于该平台的。问题可能不具体,但答案可能是。例如,首先使用 Oracle,一个表和一些测试数据:
接下来创建一个查询,仅返回我们想要使用
Order
的新值更新的行。 (我将其命名为 ItemOrder,Order 是保留字等。)在 Oracle 中,使用分析函数lag
和lead
是最简单的:将其转换为更新语句。然而,上面的查询不会创建可更新的视图(在 Oracle 中),因此请使用合并:
不幸的是,我不相信滞后和超前被广泛实现。据我所知,Microsoft SQL Server 尚未实现它们。没有使用 ASE 的经验,但他们的产品很棒。
Row_number() 得到了更广泛的实现。 Row_number() 可用于获取无间隙的内容。 (Row_number() 在 Oracle 上被称为分析函数,在 SQL Server 上被称为窗口函数。)首先是查询:
执行更新,再次使用合并而不是更新。 (Oracle 确实允许
update ... from ... join ...
语法,在其他平台上可能可以使用更新而不是合并。)注意 请注意,如果与第一行的 Id 匹配,上面的解决方案将在 OrderItems 上写入 null。
NOTE All below solutions assume that ItemOrder is unique
EDIT Adding a solution that is more like what OP was trying, and may be more portable to Sybase, this time on Microsoft SQL Server 2008. (See below for solutions using Oracle's analytic functions, that may be more efficient if available.)
First the select to get our row selection criteria correct:
Then the update based on the above:
Set
@MoveUpId
to 20 and rerun above query results in:but I assume this question is not specific to this platform. The question may not be specific, but the answer probably is. For example, using Oracle, first, a table and some test data:
Next create a query that returns just the rows we want to update with their new values for
Order
. (Which I named ItemOrder, Order being a reserved word and all.) In Oracle this is simpliest using the analytic functionslag
andlead
:Convert that into an update statement. However the above query will not create an updateable view (in Oracle), so use merge instead:
Unfortunately, I do not believe lag and lead are widely implemented. Microsoft SQL Server, as far as I know, has yet to implement them. No experience with ASE, it they have them great.
Row_number() is more widely implemented. Row_number() can be used to get something that is gap free. (Row_number() is refered to as an analytic function on Oracle and a windowed function on SQL Server.) First the query:
Doing the update, again with merge instead of update. (Oracle does allow the
update ... from ... join ...
syntax, one may be able to get away with update instead of merge on other platforms.)NOTE Note the solutions above will write null over OrderItems if matching against the Id for the first row.