Могу ли я присоединиться к таблице на основе SQL-оператора if? - PullRequest
1 голос
/ 14 сентября 2009

У меня есть три таблицы для этой проблемы: песни, черный список и белый список. В таблице песен есть столбец с именем «accessType», в котором хранится одно из следующих четырех значений: общедоступное, личное, черный список, белый список. Я пытаюсь получить список всех песен, к которым пользователь имеет доступ. Первое условие состоит в том, что songs.accessType! = Private. Здесь возникает сложная часть: если songs.accessType = blacklist, мне нужно убедиться, что идентификатор пользователя не находится в таблице черного списка. Точно так же, если songs.accessType = белый список, мне нужно проверить, что идентификатор пользователя находится в таблице белого списка. Мне кажется, что мне нужно при определенных условиях присоединиться к черному списку и / или таблице белого списка, но я понятия не имею, возможно ли это, или даже правильный подход. Любая помощь очень ценится, спасибо!

Вот объяснение моей схемы таблицы:

песни : идентификатор, имя, acessType, идентификатор пользователя

черный список : идентификатор песни, идентификатор пользователя

белый список : идентификатор песни, идентификатор пользователя

Редактировать
Вот разбивка внешних ключей:
songs.id -> blacklist.songID
songs.id -> whitelist.songID
songs.userID -> blacklist.userID
songs.userID -> whitelist.userID

Ответ Равиша работает, но мне интересно узнать, почему люди говорят, что это неэффективно, насколько это возможно.

Кроме того, каково значение символа @ перед userID? Я заменил @userID на? и я привязываю параметры к запросу.

@ Workshop Alex - частные песни вообще не должны быть включены.

Ответы [ 7 ]

4 голосов
/ 14 сентября 2009
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 будет еще быстрее с левым соединением.

2 голосов
/ 14 сентября 2009
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)      
    )
0 голосов
/ 15 сентября 2009

В этом вопросе есть некоторая путаница! В нескольких ответах предполагается, что вы выбрали где-то идентификатор пользователя и хотите выбрать все песни для этого конкретного пользователя. Другие предположили, что вы просто хотели получить список всех пользователей, где (userID, songID) находится в белом списке, но не в черном списке. В результате есть два возможных ответа на этот вопрос!

Итак, это можно прочитать как два разных вопроса:
Вопрос 1: Для конкретного пользователя, к каким частным песням он может получить доступ, которые являются общедоступными, находятся в белом списке или не в черном списке?
Вопрос 2: Какие не частные песни, из которых пользователи занесены в белый или публичный список или не занесены в черный список?

На оба вопроса ответили. На вопрос 2 ответил richardtallent , а другой - я. В Q2 использование EXISTS является лучшим вариантом просто потому, что дополнительный выбор всегда возвращает один результат, а EXISTS просто переводит в true или false, что более оптимизировано.
В Q1 лучше использовать IN, потому что вам нужно проверить несколько значений в одном и том же подмножестве. Парсер выполнит подвыбор один раз и сохранит набор результатов, вместо того, чтобы выполнять это для каждой строки. Таким образом, в этом случае использование IN лучше.

Я надеюсь, что это прояснит некоторую путаницу, возникшую в этом вопросе. Технически говоря, результат ответа можно считать субъективным! Путаница возникает из-за строки «Я пытаюсь получить список всех песен, которые пользователь может получить.» . Это заставило меня и нескольких других подумать, что вы уже выбрали одного пользователя и вам нужно собрать его песни.

0 голосов
/ 14 сентября 2009
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))
0 голосов
/ 14 сентября 2009

Обычно это помогает попытаться записать ваш запрос на простом английском языке (или на любом другом языке, на котором вы говорите).

Вам нужно несколько вариантов:
1) Все публичные песни
2) Все песни из белого списка, где идентификатор пользователя находится в белом списке.
3) Все песни из черного списка, в которых идентификатор пользователя отсутствует в черном списке.
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) 
    ) 

(Кстати, я тоже скопировал вашу орфографическую ошибку!)

Просто и эффективно. И позвольте мне теперь объяснить, почему это работает. В предложении «Где» я делаю четыре подпункта. Поскольку я хочу получить набор результатов из одной таблицы, мне не нужно объединение. Также не нужен союз, так как условия менее сложные, чем я ожидал. (Хотя это помогает сначала разбить запрос на несколько подзапросов.)

Первое условие проверяет личные песни, которые принадлежат пользователю. Вы можете просто пропустить часть "acessType = 'Private' AND", чтобы выбрать все песни, принадлежащие пользователю, независимо от того, входит он в черный список или нет. Если вы не хотите, чтобы частные песни были включены, пропустите это условие!

Второе условие добавляет все публичные песни. У этого нет никаких дополнительных ограничений.

Для третьего условия мне нужно сначала выбрать список песен, к которым пользователь может получить доступ. Таким образом, добавляется дополнительный выбор, и я проверяю идентификаторы в этом списке.

Для четвертого условия мне нужны все идентификаторы песен, которые НЕ находятся в черном списке для конкретного пользователя. Опять же, это дополнительный выбор, но на этот раз идентификатор не должен быть в списке.

В совокупности четыре условия вернут список записей песен, соответствующих вашим пожеланиям. Легко и просто. Не пытайтесь усложнить ситуацию, думая о соединениях, когда все, что вы делаете, это выбираете данные из одной таблицы. Иногда может потребоваться UNION, но требуется JOIN, чтобы объединить несколько таблиц в один результат. Вы этого не хотите!

Кстати,: userID преобразуется в часть параметров запроса. Это имеет тенденцию быть немного специфичным для реализации SQL, а иногда? или что-то еще используется. Просто прочитайте «Параметр идентификатора пользователя», когда увидите: userID.

0 голосов
/ 14 сентября 2009

используйте сравнение строк в своем предложении 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 затем выбирает только те строки, которые не имеют записи в черном списке

пс. Я не совсем уверен, каковы ваши отношения с внешними ключевыми, может быть, вы могли бы немного это уточнить?

0 голосов
/ 14 сентября 2009

Вы пытаетесь объединить обе таблицы с двумя условиями для каждого (userID и accessType).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...