我的任务是创建自定义存储过程:
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但在UPDATEMODE指定为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 | 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值的存储过程,那么如果我怎么样循环遍历我所拥有的与我的PROJECT ID不匹配的所有值,重置它们从2开始到我的终点,该终点基于我拥有的记录数量,并且一旦按顺序设置传入的位置,我知道这不适合我的更新模式选项,但我担心这只会增加很多复杂功能。
就代码而言,你对以下内容的看法是什么:
-- 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
答案 0 :(得分:2)
您似乎可以将其实现为两个步骤:
您可以使用两个更新语句执行此操作,如果您在其中添加一些CASE
逻辑,则可以执行此操作。
答案 1 :(得分:2)
你不需要一个模式,你可以设置默认的新位置= 1所以如果你想做第一个模式你只能输入ID,如果你想做第二个模式你要指定附加变量。
Create Proc update_project_order (@ID Int, @NewPosition Int = 1)
As
Declare @OldPosition Int,
@Direction Int
Select @OldPosition = Position
From tableName
Where ID = @ID
Set @Direction = Case
When @OldPosition < @NewPosition Then 0
When @OldPosition > @NewPosition Then 1
Else -1
End
Update t
Set Old_Pos = Position,
Position = Case
When ID = @ID Then @NewPosition
When @Direction = 0 And
Position Between @OldPosition And @NewPosition Then Position - 1
When @Direction = 1 And
Position Between @NewPosition And @OldPosition Then Position + 1
Else Position
End
From tableName t
答案 2 :(得分:2)
这可以使用单个更新语句完成,无需OLD_POS列或类似的任何内容,也不需要任何循环,游标或其他非设置的工件:
CREATE Proc UPDATE_PROJECT_ORDER
(
@ProjectID As INT,
@StartPosition As INT,
@EndPosition As INT
) As
;With
cteProjectOld As
(
SELECT ID,
POSITION,
Case WHEN POSITION = @StartPosition
THEN @EndPosition
ELSE POSITION END As TempPosition,
Case WHEN POSITION = @StartPosition
THEN 1
ELSE 0 END As Moved
FROM PROJECT
)
, cteProjectNew As
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY ID ORDER BY TempPosition, Moved) As NewPosition
FROM cteProjectOld
)
UPDATE cteProjectNew
SET POSITION = NewPosition
WHERE ID = @ProjectID
答案 3 :(得分:0)
我认为这是两种不同的可能性,无论是向上移动列表还是向下移动。其余的只是对位置的算术,可以在单个更新语句中完成。
create procedure update_project_order (@ProjectId int, @UpdateMode int default 1)
begin
declare @ProjectIdPosition int;
select @ProjectIdPostion = Position
from t
where ProjectId = @ProjectId;
if (@UpdateMode < @ProjectIdPosition) -- Moving up the list
update t
set position = (case when position < @UpdateMode
then position
when position < @ProjectIdPosition
then position + 1
when ProjectId = @ProjectId
then @UpdateMode
when position
end);
else if @UpdateMode > @ProjectIdPosition -- Moving down the list
update t
set position = (case when position < @ProjectIdPostion
then position
when position < @UpdateMode
then position - 1
when ProjectId = @ProjectId
then @UpdateMode
else position
end);
end;
注意:我没有测试过这个。这是一个如何实现它的想法。
答案 4 :(得分:0)
Create Procedure UPDATE_PROJECT_ORDER (@ID int,@Posi int) as
begin
Select Cast(ID as int) as ID,POSITION,Identity(int ,1,1) as NewPos
into #tmp
from Project
where POSITION<=@Posi
order by POSITION
Select Cast(ID as int) as ID,POSITION,Identity(int ,1,1) as NewPos
into #tmp2
from Project
where POSITION>=@Posi
order by POSITION
Update Project set Project.POSITION=#tmp.NewPos
from #tmp
where #tmp.ID=Project.ID
Update Project set Project.POSITION=#tmp2.NewPos + @Posi
from #tmp2
where #tmp2.ID=Project.ID
Update Project set POSITION=@Posi where ID=@ID
Drop table #tmp
Drop table #tmp2
end