从歌曲表创建一个随机排序的播放列表并返回当前,下一首和上一首歌曲的名称?

时间:2009-12-15 20:22:51

标签: sql optimization

考虑下表:

Song
----
SongID GUID Primary Key
Name Varchar
dateAdded DateTime

我正在创建一个播放Song表中歌曲的网站。我想为网站的每个访问者生成一个随机排序的播放列表,该访问者可以在不使用任何服务器端存储(没有会话风格或数据库存储)的情况下持续存在请求。播放器具有播放,下一首歌曲和上一首歌曲按钮。跨请求保留此数据的最有效方法是什么?查询此信息的最有效查询是什么?我有一个解决方案(在mySql中)有效,但我希望看看是否有效的方法。另外,我不想将答案偏向使用我的解决方案。请在答案中加入查询。

3 个答案:

答案 0 :(得分:4)

更新:这是我遵循的要求,拼写出来:

  • 无论表格中有多少首歌曲或播放列表中的当前位置,客户端需要存储的信息(例如在Cookie中)必须具有恒定的大小。
  • 使用'next'100次然后'prev'100次应该会导致100首歌曲的一些序列(可能包括重复),然后是相反的精确序列,无论在任何'next'之间可能进行任何插入或“上一步”行动。 (“上一首歌总是指前一首歌。”)
  • 将跳过播放列表中的“当它被生成/初始化”并且此后被删除的歌曲。
    • 在下面更改我的答案并不难以返回一个未找到的指标。

我认为你还需要一条信息:一个整数位置的二级歌曲键,除了你的GUID(或者你可以用它替换它)。然后,结合PRNG,您可以进行最简单快速的查找。伪代码:

def next_song(initial_seed, current_prng_index, high_song_index):
  """Returns the next song and the parameters to be later passed
  to next/prev_song."""
  while True:
    current_prng_index += 1
    current_seed = PRNG_advance(initial_seed, current_prng_index)
    song_index = song_index_from_seed(current_seed, high_song_index)
    song_id = (SELECT SongID FROM Songs WHERE SongIndex=song_index)
    if song_id: # test, somehow, for non-deleted songs
      return song_id, (initial_seed, current_prng_index, high_song_index)
      # include values that must be passed on the next call
# prev is the same, except uses -= 1

def song_index_from_seed(seed, highest):
  return seed % (highest + 1)
  # simple, but better possibilities might exist for your data

def new_playlist():
  """Returns the parameters to be passed to next/prev_song."""
  high_song_index = (SELECT MAX(SongIndex) FROM Songs)
  return get_a_random_number(), 0, high_song_index

这会在每首下一首歌曲上逐渐变慢,并且不允许通过倒退来进行包装(没有一些创造力)。如果您找到合适的可逆PRNG(虽然在PRNG,AFAIK中不常见):

def next_song(current_seed, high_song_index):
  while True:
    current_seed = PRNG_next(current_seed)
    song_index = song_index_from_seed(current_seed, high_song_index)
    song_id = (SELECT SongID FROM Songs WHERE SongIndex=song_index)
    if song_id: # test, somehow, for non-deleted songs
      return song_id, (current_seed, high_song_index)
      # include values that must be passed on the next call
# prev is the same, except uses PRNG_prev

它通过跳过这些歌曲来处理删除(或者如果你永远不删除那些甚至不是问题),但无论删除了多少,都不会改变顺序。插入由song_index_from_seed函数处理,通过约束索引使新歌不会被“看到”。如果已删除所有可用歌曲,这也是一个无限循环,但我认为此代码处理所有其他极端情况。

(将下一个/ prev版本重构为一个常见函数的简单包装器并不难,但是为了提高清晰度,这里省略了。)

我已经用我的位置索引有效地替换了你的dateAdded,但这是一个改进,因为你不需要将删除的行保留为假人(但仍然不能重用他们的索引),就像你计算位置索引一样通过在dateAdded上订购。

使用像这样的PRNG似乎很幼稚,但有效;并且你必须要小心选择的PRNG和song_index_from_seed组合的行为与你期望的一样:一些初始种子可能会生成“非随机”播放列表。 (不是因为它们不是随机的,而是因为听众期望混合歌曲而不仅仅是4, 49, 9,...)

答案 1 :(得分:3)

这是一个想法,如果SQL,将进行伪随机排序。我现在没有数据库来测试它,所以实际的SQL可能需要调整。我还假设你的ID列是整数。

随机选择两个多位数素数,P1和P2,其中P2大于P1。

从歌曲顺序中选择*(mod((ID * P2),P1)),ID

想法是将ID乘以一个素数,然后使用P1进行mod操作。这应该可以很好地改变歌曲,每个素数对都会以不同的方式改变它们。

要重新创建确切的顺序,您只需要做P1和P2,它应该只是几个字符。在请求期间来回发送没问题。您可能需要稍微尝试一下素数范围,但我猜想使用所有4,5,6位数的素数应该会给你相当多的初始化。

答案 2 :(得分:1)

您可以使用Pseudo random sequence。将gseed和播放列表位置存储在cookie中。伪随机种子将保证您在每个请求上重新创建相同的播放列表序列,而无需在服务器上实际存储播放列表