我有三个表用于此问题:歌曲,黑名单和白名单。歌曲表有一个名为“accessType”的列,它存储以下四个值之一:public,private,blacklist,whiteelist。我正在尝试获取用户可以访问的所有歌曲的列表。第一个条件是songs.accessType!= private。这里有一个棘手的部分:如果songs.accessType =黑名单,我需要检查用户的ID是否在黑名单表中。同样,如果songs.accessType =白名单,我需要检查用户的ID是否在白名单表中。在我看来,我需要在某些条件下加入黑名单和/或白名单表,但我不知道这是否可行,甚至是正确的方法。非常感谢任何帮助,谢谢!
以下是我的表架构的解释:
歌曲:id,name,acessType,userID
黑名单:songID,userID
白名单:songID,userID
修改的
这是外键的细分:
songs.id - > blacklist.songID
songs.id - > whitelist.songID
songs.userID - > blacklist.userID
songs.userID - > whitelist.userID
Ravish的回答是有效的,但我很想知道人们为什么说它尽可能不高效。
另外,在userID之前@符号的意义是什么?我用@userID替换了?我是查询的绑定参数。
@Workshop Alex - 根本不应包含私人歌曲。
答案 0 :(得分:4)
SELECT * FROM SONGS s
WHERE
accesstype='public'
OR (accesstype='whitelist'
AND EXISTS (SELECT null FROM WHITELIST wl WHERE
wl.songid = s.id AND wl.userid=s.userid))
OR (accesstype='blacklist'
AND NOT EXISTS (SELECT null FROM BLACKLIST bl WHERE
bl.songid = s.id AND bl.userid= s.userid))
如果accesstype = private,它将不会漏掉,不需要额外的条款。
我选择使用相关子查询,因为在这种情况下,它们比过滤后的左连接更具可读性。也许MySQL对它们的效率稍微低一些,但是不应该根据查询优化器的微小临时限制来做出关于设计的决策,除非性能差异绝对是关键的。也许下一版本的MySQL比左连接更快。
答案 1 :(得分:2)
SELECT
s.*
FROM
songs s
LEFT JOIN
whitelist wl ON s.songid = wl.songid AND wl.userid = @userid
LEFT JOIN
blacklist bl ON s.songid = bl.songid AND bl.userid = @userid
WHERE
s.userid = @userid
AND
s.accessType != 'private'
AND
(
s.accessType = 'public'
OR
(s.accessType = 'whitelist' AND wl.songid IS NOT NULL)
OR
(s.accessType = 'blacklist' AND bl.songid IS NULL)
)
答案 2 :(得分:0)
SELECT *
FROM Songs s
WHERE (s.AccessType='BlackList' AND @UserId
Not in(SELECT UserId From BlackList WHERE SongId=s.Id))
OR (s.AccessType='WhiteList' AND @UserId
in(SELECT UserId From WhiteList WHERE SongId=s.Id))
答案 3 :(得分:0)
这个问题有点混乱!一些答案假设您已在某处选择了一个用户ID,并希望为该特定用户选择所有歌曲。其他人假设你只想要一个所有用户的列表,其中(userID,songID)在白名单中,但不在黑名单中。因此,这个Q有两种可能的答案!
因此,它可以被解读为两个不同的问题:
问题1:鉴于特定用户,他可以访问哪些非私人歌曲是公开的,列入白名单还是未列入黑名单?
问题2:哪些用户被列入白名单或公开或未列入黑名单的非私立歌曲?
两个Q都得到了回答。问题2由richardtallent回答,另一个由我回答。在Q2中,使用EXISTS是最佳选择,因为子选择始终返回单个结果,EXISTS将转换为true或false,这是更优化的。 在Q1中,使用IN更好,因为您需要在同一子集中检查多个值。解析器将执行一次子选择并存储结果集,而不是为每一行执行此操作。因此,在这种情况下,使用IN会更好。
我希望这将清除本Q中出现的一些混淆。从技术上讲,答案的结果可能被认为是主观的!混淆来自于行“我正在尝试获取用户可能访问的所有歌曲的列表。”。这让我和其他几个人认为你已经选择了一个用户并需要收集他的歌曲。
答案 4 :(得分:-1)
您尝试加入BOTH表,每个表有两个条件(userID和accessType)。
答案 5 :(得分:-1)
在您的ON
子句中使用字符串比较,并智能地与您的黑名单和白名单联接
SELECT `s`.*
FROM `songs` `s`
INNER JOIN `whitelist` `w`
ON `s`.`id` = `w`.`songID`
AND `s`.`userID` = `w`.`userID`
AND `s`.`accessType` = 'whitelist';
LEFT JOIN `blacklist` `b`
ON `s`.`id` = `b`.`songID`
AND `s`.`userID` = `b`.`userID`
AND `s`.`accessType` = 'blacklist'
WHERE `b`.`songID` IS NULL
AND `s`.`accessType` <> 'private'
AND `s`.`userID` = $youruserid;
这将选择给定userid
的所有歌曲,这些歌曲是公开的,内部加入whitelist
(因此您只能获得白名单上的歌曲)。
左边连接blacklists
然后只选择没有黑名单条目的行
PS。我不确定你的外键关系是什么,也许你可以详细说明一点?
答案 6 :(得分:-1)
尝试用简单的英语(或任何您的母语)写下您的查询通常会有所帮助。
您需要多种选择:
1)所有公共歌曲
2)所有白名单歌曲,其中用户ID在白名单中
3)所有列入黑名单的歌曲,其中用户ID不在黑名单中
4)用户自己的所有私人歌曲。
因此,有四个条件,转化为:
SELECT
*
FROM
songs
WHERE
(acessType = 'Public')
OR
(acessType = 'Whitelist' AND id IN (
SELECT songID FROM WhiteList WHERE userID = :userID)
)
OR
(acessType = 'Blacklist' AND id NOT IN (
SELECT songID FROM BlackList WHERE userID = :userID)
)
(顺便说一句,我也复制了你的拼写错误!)
简单有效。现在让我解释一下为什么会这样。在Where子句中,我执行了四个子选择。因为我想从单个表中得到结果集,所以我不需要连接。由于条件不如我预期的那么复杂,所以不需要联盟。 (虽然首先在不同的子查询中拆分查询会有所帮助。)
第一个条件检查用户拥有的私人歌曲。您可以跳过“acessType ='Private'AND”部分,这样您就可以选择用户拥有的所有歌曲,无论他是否将自己列入黑名单。如果您不想包含私人歌曲,请跳过此条件!
第二个条件添加了所有公共歌曲。那个没有额外的限制。
对于第三个条件,我需要先选择用户可以访问的歌曲列表。因此,添加了一个子选择,我在此列表中检查ID。
对于第四个条件,我需要所有不在特定用户黑名单中的歌曲ID。再次,一个子选择,但这次,ID不应该在列表中。
合并后,这四个条件将返回符合您意愿的歌曲列表。干净利落。当你所做的只是从一个表中选择数据时,不要试图通过考虑连接来使事情变得更复杂。有时,可能需要UNION,但需要JOIN才能将多个表组合成一个结果。你不希望这样!
顺便说一下,:userID转换为查询的参数部分。这往往有点特定于SQL实现,有时候?或其他东西。只要看到:userID。
,请阅读“用户ID参数”