播放列表下歌曲位置的数据库设计

时间:2019-01-08 10:08:37

标签: sql sql-server database-design

我有一个音乐流应用程序,其中有播放列表。每个播放列表最多可包含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的行。当播放列表中有更多歌曲时,此数字将是一个很大的数目。是否有更好的逻辑,以便使更新查询的数量非常少。

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