【发布时间】:2013-01-18 01:15:57
【问题描述】:
我的任务是创建自定义存储过程:
exec UPDATE_PROJECT_ORDER @PROJECTID=12, @UPDATEMODE=0
这将在表中的一行被更新后执行。
不幸的是,这让我很困惑(以至于我什至不确定这是否可能)我一直在努力简化我正在做的事情,并想知道我是否可以就代码本身向董事会寻求帮助...
因此,存储过程/代码的目的是通常按顺序重新排序项目列表,重新排序会根据传入的参数更改,该参数将保留在表并围绕它排序列表的其余部分或将其重新编号为序列中的下一个数字。我想我们可以假设大部分时间 UPDATEMODE=0 将是默认值。
我有一张如下所示的表格:
---------------------------------------
| ID | POSITION | OLD_POS |
---------------------------------------
| 10 | 1 | |
| 11 | 2 | |
| 12 | 3 | |
| 13 | 4 | |
---------------------------------------
如果用户决定给 ID 为 12 的记录(ID 与存储过程一起传入)更高的优先级,例如 1,那么应该发生的是其他记录 (10 / 11 / 13) 应该是围绕它按顺序重新排序(2 / 3 / 4),这将影响它们在前端的显示方式,例如
---------------------------------------
| ID | POSITION | OLD_POS |
---------------------------------------
| 10 | 2 | 1 |
| 11 | 3 | 2 |
| 12 | 1 | 3 |
| 13 | 4 | 4 |
---------------------------------------
另一个例子是记录 ID 12 的位置从 1 更改为 7,因此这个数据集:
---------------------------------------
| ID | POSITION | OLD_POS |
---------------------------------------
| 10 | 2 | 1 |
| 11 | 3 | 2 |
| 12 | 1/7 | 3 |
| 13 | 4 | 4 |
---------------------------------------
位置数据重新排序为:
---------------------------------------
| ID | POSITION | OLD_POS |
---------------------------------------
| 10 | 1 | 2 |
| 11 | 2 | 3 |
| 12 | 7 | 1 |
| 13 | 3 | 4 |
---------------------------------------
如上所述 - 在存储过程中,需要传入另一个参数(@UPDATEMODE,可以是 0 或 1),这会改变函数的行为,允许用户指定他们想要的位置并重新- 对列表进行排序而不是使其成为序列中的下一个数字,例如,它们将第 3 行中的优先级从值 1 更新为值 7
此数据集第 12 行位置值 = 1,但更改为 7,其中 UPDATEMODE 指定为 1:
---------------------------------------
| ID | POSITION | OLD_POS |
---------------------------------------
| 10 | 2 | 1 |
| 11 | 3 | 2 |
| 12 | 1/7 | 3 |
| 13 | 4 | 4 |
---------------------------------------
这会将列表重新排序如下:
---------------------------------------
| ID | POSITION | OLD_POS |
---------------------------------------
| 10 | 1 | 2 |
| 11 | 2 | 3 |
| 12 | 4 | 1 |
| 13 | 3 | 4 |
---------------------------------------
在本例中,存储过程将被称为:
exec UPDATE_PROJECT_ORDER @PROJECTID=12, @UPDATEMODE=1
这是我一直在使用的 SQL 代码:
-- Declare variables
DECLARE @PROJECTID INTEGER
DECLARE @CURRENTPOSITION INTEGER
DECLARE @ROLLBACKPOSITION INTEGER
DECLARE @STARTPOSITION INTEGER
DECLARE @ENDPOSITION INTEGER
-- For testing hardcode a REQUEST ID
SET @PROJECTID = 12
-- Start Position value
SET @STARTPOSITION = 1
-- End Position value
SELECT @ENDPOSITION = COUNT(ID) FROM PROJECT WHERE PROJECT_ORDER IS NOT NULL
-- Update Rollback column with current value
UPDATE PROJECT SET OLD_POS = POSITION WHERE POSITION IS NOT NULL
DECLARE cursorProjectPositionUpdate CURSOR fast_forward
FOR
SELECT ID, POSITION, OLD_POS
FROM PROJECT
WHERE ID = @PROJECTID
AND POSITION IS NOT NULL
OPEN cursorProjectPositionUpdate
FETCH NEXT FROM cursorProjectPositionUpdate INTO @PROJECTID, @CURRENTPOSITION, @ROLLBACKPOSITION
WHILE @@FETCH_STATUS = 0
BEGIN
WHILE (@STARTPOSITION <= @ENDPOSITION)
IF @STARTPOSITION = 1
UPDATE PROJECT
SET POSITION = @STARTPOSITION
WHERE ID = @PROJECTID
AND OLD_POSITION = @ROLLBACKPOSITION
ELSE
UPDATE PROJECT
SET POSITION = @STARTPOSITION
WHERE OLD_POS = @ROLLBACKPOSITION
AND ID <> @PROJECTID
SET @STARTPOSITION = @STARTPOSITION + 1
FETCH NEXT FROM cursorProjectPositionUpdate INTO @PROJECTID, @CURRENTPOSITION, @ROLLBACKPOSITION
END
CLOSE cursorProjectPositionUpdate
DEALLOCATE cursorProjectPositionUpdate
我使用了游标,因为最多可以重新排序 25 条记录的硬性限制,所以我不太担心性能。虽然表中可能有超过 25 条记录,这就是为什么我尝试使用 AND POSITION IS NOT NULL 子句排除记录的原因,所以我希望这是可以接受的。
我脑海中的想法是计算我总共有多少条记录有一个位置,然后循环将第一个设置为位置 1,然后将其余的设置为下一个顺序。
我发现的最大问题是记录匹配,即我的 WHERE 子句,因为存储过程传入的唯一内容是我想要设置为位置 1 的 ID,所以我怎么知道哪个接下来是一个...逻辑是它应该是列表中的下一个最低 ID 号。
这是在 MS SQL Server 中完成的。
我试图避免创建任何临时表,因此需要看看这是否可以一次性完成。
希望这对某人有某种意义,我对解决方案持开放态度,并将尽可能多地分享信息。
非常感谢!
附加
根据以下答案,我一直在考虑这个问题,我再次尝试尽可能简单地开始,所以我有存储过程,它传入了一个 PROJECT ID 值,那么如果我循环遍历我拥有的与我的项目 ID 不匹配的所有值项目到 1。我知道这不适合我的更新模式选项,但我担心这只会增加很多复杂性。
就代码而言,您对以下内容有何看法:
-- Declare variables
DECLARE @PROJECTID INTEGER
DECLARE @STARTPOSITION INTEGER
DECLARE @ENDPOSITION INTEGER
-- Hardcoded for testing
SET @PROJECTID = 25061
-- Start Position value
SET @STARTPOSITION = 2
-- End Position value
SELECT @ENDPOSITION = COUNT(ID) FROM PROJECT WHERE PROJECT_ORDER IS NOT NULL AND ID <> @PROJECTID
-- Update Rollback column with current value
UPDATE PROJECT SET PROJECT_ORDER_RB = PROJECT_ORDER WHERE PROJECT_ORDER IS NOT NULL
-- Loop other records
WHILE (@STARTPOSITION <= @ENDPOSITION)
UPDATE PROJECT SET PROJECT_ORDER = @STARTPOSITION WHERE ID <> @PROJECTID
AND PROJECT_ORDER IS NOT NULL AND (PROJECT_ORDER = 1 OR PROJECT_ORDER => @STARTPOSITION OR PROJECT_ORDER <= @ENDPOSITION)
SET @STARTPOSITION = @STARTPOSITION + 1
-- Finally set passed in Project to Position 1
UPDATE PROJECT SET PROJECT_ORDER = 1 WHERE ID = @PROJECTID
【问题讨论】:
-
如果需要,您可以将变量添加到存储过程吗?为了完成第二部分,我认为您需要另一个变量来指定新位置,因为默认情况下它不会是 1。因此,对于您的示例,它将是 "exec UPDATE_PROJECT_ORDER @ID=12, @POSITION=7, @UPDATEMODE=0"
-
所以基于我得到的答案是没有额外的变量不能添加到存储过程中。对于它的第二部分,我相信我需要排除使用 SQL 更新传入的 ID,即 WHERE ID @REQUEST,所以它会跳过它,但这对我来说没有多大意义,因为你不希望重复,所以代码必须忽略传入的变量,但不能在列表中重复使用它。
-
也许这个答案会在某种程度上有所帮助:SQL Server custom record sort in table, allowing to delete records。基本上,查询会根据参数旋转排序列中的值(并且它的参数化方式与您的要求略有不同)。
-
... 拥有基于布尔变量做两件事的过程是邪恶的。你真的应该编写两个不同的例程,名称不同。此外,如果您想避免必须更新表中的每一行,请使用“插入的项目在现有项目之间的中间”的策略(即第一个项目位于 0,下一个位于 2 ^ 16 等)。这可能有时需要重新平衡 - 但对于 20 亿个值,这将是一段时间。
-
所以我正在查看下面提供的答案(顺便感谢大家),我看到了一些让我对它们感到担忧的事情。在这一点上,我不能假设我有任何额外的变量可以使用,所以例如没有 startpos / endpos 加上我必须传入 UPDATEMODE ,默认情况下将为 1 这意味着提供的 ID 变为位置 1 并且一切都重新-围绕它排序,如果它为 0,那么为该 ID 设置的值将被单独保留,并且所有内容都将围绕它重新排序。我知道这并不理想,但这是我给定的范围。
标签: sql sql-server stored-procedures