【发布时间】:2019-05-26 02:33:23
【问题描述】:
我正在尝试在 SQLite 中为音乐流服务数据库建模,我想创建这个触发器:当从播放列表中删除一首歌曲时,它之后的每首歌曲都需要减少其位置。我不太确定如何编写这个触发器。
这是播放列表表的示例(抱歉,葡萄牙语名称。idMusica = 歌曲 ID;posicao = 歌曲在播放列表中的位置)
为了进一步解释我想要的,这里有一个例子: 如果我从播放列表 1 中删除歌曲 6,歌曲 11 的位置将变为 3。
【问题讨论】:
我正在尝试在 SQLite 中为音乐流服务数据库建模,我想创建这个触发器:当从播放列表中删除一首歌曲时,它之后的每首歌曲都需要减少其位置。我不太确定如何编写这个触发器。
这是播放列表表的示例(抱歉,葡萄牙语名称。idMusica = 歌曲 ID;posicao = 歌曲在播放列表中的位置)
为了进一步解释我想要的,这里有一个例子: 如果我从播放列表 1 中删除歌曲 6,歌曲 11 的位置将变为 3。
【问题讨论】:
我相信以下会如你所愿:-
CREATE TRIGGER IF NOT EXISTS playlistreorder
AFTER DELETE ON playlist
BEGIN
UPDATE playlist SET posicao = posicao -1 WHERE posicao > old.posicao AND idPlayList = old.idPlayList;
END
;
考虑以下测试:-
DROP TABLE IF EXISTS playlist;
CREATE TABLE IF NOT EXISTS playlist (idMusica INTEGER, idPlayList INTEGER, posicao INTEGER);
INSERT INTO playlist (idMusica,idPlayList, posicao) VALUES
(12,1,1),(5,1,2),(6,1,3),(11,1,4),
(1,2,1),(2,2,2),(3,2,2),(4,2,4),(9,2,5)
;
CREATE TRIGGER IF NOT EXISTS playlistreorder
AFTER DELETE ON playlist
BEGIN
UPDATE playlist SET posicao = posicao -1 WHERE posicao > old.posicao AND idPlayList = old.idPlayList;
END
;
SELECT * FROM playlist;
DELETE FROM playlist WHERE posicao = 3 AND idplayList = 1;
SELECT * FROM playlist;
【讨论】:
我认为这种方法根本上是错误的并且过于脆弱。不是跟踪播放列表中的绝对 位置,而是跟踪相对 位置。这样您在插入或删除歌曲时不必更新位置:
如果前三首歌曲的 posicao 值为 1,2,3,并且您想在位置 2 添加一首歌曲,而不是将当前的 2 更新为 3,将当前的 3 更新为 4 等等,请使用当前位置 1 和 2 的 posicao 之间的新值:在这种情况下为 1.5。
删除的时候不用担心填空;已删除歌曲两侧的posicao 的顺序仍然正确。
如果您出于某种原因需要绝对位置,您可以使用现代版本的 sqlite(3.25 或更高版本)即时在 SELECT 中计算它,例如
SELECT idMusica, row_number() OVER (ORDER BY posicao) AS pos
FROM playlist_songs
WHERE idPlaylist = :playlistid
ORDER BY posicao
或者,只要每次从给定的播放列表中获取一行,只要它是由posicao 排序的,就在程序中增加一个计数器。
基于窗口函数的查询,用于插入到播放列表的中间并删除,给定一个绝对位置(虽然对于删除,如果您的联结表不是 WITHOUT ROWID 之一,则更容易跟踪歌曲的 rowid您的程序并使用所选歌曲的程序删除):
INSERT INTO playlist_songs(idMusica, idPlaylist, posicao)
VALUES(:songid, :playlistid
, (SELECT (pos + prev_pos) / 2.0
FROM (SELECT posicao AS pos
, lag(posicao, 1, 0.0) OVER (ORDER BY posicao) AS prev_pos
, row_number() OVER (ORDER BY posicao) AS rn
FROM playlist_songs WHERE idPlaylist = :playlistid)
WHERE rn = :position));
DELETE FROM playlist_songs
WHERE rowid = (SELECT rowid
FROM (SELECT rowid, row_number() OVER (ORDER BY posicao) AS rn
FROM playlist_songs WHERE idPlaylist = :playlistid)
WHERE rn = :position);
【讨论】: