我有一个音乐流应用程序,其中有播放列表。每个播放列表最多可包含100首歌曲。我有一个名为PlaylistSongMapping
的映射表,该表具有以下架构
+------+------------+--------+
|SongId| PlaylistId |Position|
+------+------------+--------+
| 1 | 10 | 2 |
| 2 | 10 | 1 |
| 3 | 10 | 3 |
| 5 | 10 | 4 |
| 6 | 11 | 1 |
+------+------------+--------+
使用Position
列管理播放列表中每首歌曲的位置。在播放列表中,我需要订单更改功能。当前的逻辑是更新Position
列。
问题是如果我需要将带有SongId: 3
的歌曲移到第一位置,我需要更新SongId 1,2,3
的行。当播放列表中有更多歌曲时,此数字将是一个很大的数目。是否有更好的逻辑,以便使更新查询的数量非常少。
答案 0 :(得分:1)
理论上最佳(但难度更大)
如果您将该列视为排序顺序而不是确切位置,则可以采取一些策略来避免出现间隔,因此编号应为10、20、30。然后,如果要将第三首歌曲移至第一首位置,您可以给它5号,甚至可以给它一个负数。
问题在于您仍然需要读取所有数据,并且编号的实现变得更加复杂,因为现在您需要检查是否不需要更新任何内容,或者是否需要更新所有内容,或者只是更新列表的一部分。
非常简单(基本上就是您现在拥有的)
因此,如果我是您,我会保持简单,只更新需要更新的所有内容。毕竟,每个播放列表没有多少行(即使每个播放列表允许播放1000首或更多歌曲),并且如果将其进行批量更新,则数据库应该能够很好地处理这一行。您可以一次移动所有行,然后将一行更新到新位置。这样,您只需要两个语句:1即可插入,更新或删除特定歌曲,1可以移动整个或部分列表以保持后续编号。
非常简单(到目前为止可能还不够快)
过去,我以一种更加懒惰的方式实现了这一点,在这种情况下,我保持有2、4、6、8 ...的间隙的排序顺序……然后,当我想在某个位置更新或插入时位置,我可以在行中使用sortorder = position*2-1
进行插入或更新,或直接删除任何行:
update Song
set
SortOrder = :NewPosition*2-1
where
SortOrder = :OldPosition*2;
此后,无论我在哪里修改,我都简单地更新了所有行以再次编号,从而根据排序顺序生成了一个新序列。
这将意味着我对已经正确的行进行了冗余更新,但这确实非常简单,仍然非常快(因为数据库擅长于此类工作),并且它也具有一些自动修复的效果,因为每次都会重新编号整个列表,更正过去的任何错误。
它确实取决于您的数据库如何正确实现。我使用的是Oracle,它非常好,并且生成了这样的序列。在MySQL中,它有点麻烦,但仍然不那么难。
答案 1 :(得分:1)
“问题是,如果我需要将SongId:3的歌曲移到第一 我需要更新SongId 1,2,3“行的位置
为此,您不需要两个以上的语句。
您可以通过适当地使用增量/减量来更新一个语句中的所有位置值(除了要移至第一位置的位置值),然后使用第二条语句来更新要移动的特定歌曲-例如
update PlaylistSongMapping
set Position = Position + 1
where
Playlistid = 10
and position < 3 and position >= 1;
update PlaylistSongMapping
set position = 1
where songId = 3;
这假设您知道要移动的歌曲的ID(因此您可以确定其当前位置,这是第一个查询中的where子句所需的位置)-但在一定情况下,您应该知道,根据您的描述。
请注意,在上面的示例中,position >= 1
并不是严格必需的,因为1是列表中的第一位,但是此代码旨在涵盖更一般的情况-例如如果您想将其移至位置2,则不希望将歌曲的位置增加到位置1,因此在这种情况下,您将改写position >= 2
。
答案 2 :(得分:1)
如果要在移动位置时要更新表中所有相关位置的SP,这应该可以完成工作:
USE Sandbox;
GO
--Small sample set
CREATE TABLE dbo.Playlist (SongID int,
PlaylistID int,
Position int);
INSERT INTO dbo.Playlist (SongID,
PlaylistID,
Position)
VALUES(1,1,1),
(3,1,2),
(5,1,3),
(2,1,4),
(2,2,1),
(10,2,2);
GO
--Check data
SELECT *
FROM dbo.Playlist;
GO
--CREATE the SP
CREATE PROC dbo.MoveSongPosition @PlaylistID int,
@SongID int,
@NewPosition int
AS BEGIN
UPDATE P
SET Position = CASE WHEN SongID = @SongID THEN @NewPosition ELSE Position +1 END
FROM dbo.Playlist P
WHERE P.PlaylistID = @PlaylistID
AND (P.SongID = @SongID
OR P.Position BETWEEN @NewPosition AND (SELECT sq.Position
FROM dbo.Playlist sq
WHERE sq.SongID = @SongID
AND sq.PlaylistID = @PlaylistID));
END
GO
--Run and test the SP
EXEC dbo.MoveSongPosition @PlaylistID = 1,
@SongID = 2,
@NewPosition = 2;
GO
--Check the new data
SELECT *
FROM dbo.Playlist;
GO
--Clean up
DROP PROC dbo.MoveSongPosition;
DROP TABLE dbo.Playlist;
该解决方案假定您正在使用SQL Server;自从我发表评论以来,您还没有更新过代码,因此我们不知道您确实正在使用哪种RDBM。
编辑:逻辑上的改变,相信我已经纠正了。
CREATE PROC dbo.MoveSongPosition @PlaylistID int,
@SongID int,
@NewPosition int
AS BEGIN
UPDATE P
SET Position = CASE WHEN P.SongID = @SongID THEN @NewPosition
WHEN P.Position = @NewPosition THEN P.Position + V.Direction
WHEN P.Position < @NewPosition THEN P.Position - V.Direction
WHEN P.Position > @NewPosition THEN P.Position + V.Direction
END
FROM dbo.Playlist P
CROSS APPLY (SELECT ca.Position
FROM dbo.Playlist ca
WHERE ca.PlaylistID = P.PlaylistID
AND ca.SongID = @SongID) CS
CROSS APPLY (VALUES(CASE WHEN CS.Position < @NewPosition THEN -1
WHEN @NewPosition < CS.Position THEN 1
ELSE 0 END)) V(Direction)
WHERE P.PlaylistID = @PlaylistID
AND ((P.Position >= @NewPosition AND P.Position <= CS.Position
OR (P.Position >= CS.Position AND P.Position <= @NewPosition)));
END