SQL: понимание оператора OR в предложении WHERE - PullRequest
3 голосов
/ 19 апреля 2019

У меня есть таблицы под названием Movie, Genre and Keyword, из которых я создал представление под названием «genkeyword». Представление 'genkeyword' имеет множество кортежей, поэтому к нему можно обратиться по адресу DB Fiddle .

У меня есть следующий запрос:

SELECT title, 
       year, 
       Count(DISTINCT genre)   AS genre_freq, 
       Count(DISTINCT keyword) AS keyword_freq 
FROM   genkeyword 
WHERE  ( genre IN (SELECT genre 
                   FROM   genkeyword 
                   WHERE  title = 'Harry Potter and the  Deathly Hallows') 
          OR keyword IN (SELECT keyword 
                         FROM   genkeyword 
                         WHERE  title = 'Harry Potter and the  Deathly Hallows') ) 
       AND title <> 'Harry Potter and the Deathly Hallows' 
GROUP  BY title, 
          year 
ORDER  BY genre_freq DESC, 
          keyword_freq DESC; 

Что я собираюсь сделать с этим запросом, так это получить жанр и частоту ключевых слов для каждого фильма, в котором есть жанры и ключевые слова, общие с Гарри Поттером: Выход должен быть:

title                      |      genre_freq    |    keyword_freq
Cinderella                        2                        2
The Shape of Water                2                        1
How to Train Your Dragon          2                        0
Enchanted                         1                        3

Я знаю, что запрос неверен, поскольку вместо этого я получаю следующий вывод:

    title                      |      genre_freq    |    keyword_freq
    The Shape of Water                4                  3       
    Enchanted                         3                  4
    Cinderella                        2                  5
    How to Train Your Dragon          2                  3              

Однако я хотел бы уточнить мое понимание того, как работает запрос.

В предложении 'where' моего запроса:

where (genre in (select genre from genkeyword where title='Harry Potter') or 
keyword in (select keyword from genkeyword where title='Harry Potter')) 

Прав ли я, говоря, что сгенерированы два набора результатов, один из которых содержит все кортежи жанра Гарри Поттера (пусть это будет R1), а другой - все кортежи с ключевым словом в Гарри Поттер (пусть это будет R2)?

Если рассматриваемый кортеж содержит жанр из результирующего набора жанров R1 или ключевое слово из результирующего набора ключевых слов R2, то учитывается жанр / ключевое слово. Я не уверен, как count (отличный жанр) и count (другое ключевое слово) работает в этом случае. Если кортеж содержит жанр из R1, учитывается ли только жанр или ключевое слово? То же самое для случая, когда кортеж содержит ключевое слово в R2, учитывается ли жанр и ключевое слово?

Я не понимаю, почему я неправильно получаю значения genre_freq и keyword_freq из моего запроса. Это потому, что я не до конца понимаю, как жанр и частоты ключевых слов учитываются за кадром. Любые идеи приветствуются.

Ответы [ 4 ]

0 голосов
/ 19 апреля 2019

Попробуйте этот запрос.
Я не использовал ни одно из созданных вами представлений, но вы можете использовать их, если хотите.

MySQL

SET @tmpMovieid = (SELECT DISTINCT id 
                   FROM Movie 
                   WHERE title = 'Harry Potter and the Deathly Hallows');

SELECT id,
       title,
       IFNULL(Max(CASE WHEN coltype = 'genre' THEN col end),   0) AS genre_freq,
       IFNULL(Max(CASE WHEN coltype = 'Keyword' THEN col end), 0) AS keyword_freq

FROM   (SELECT id,
               title,
               Count(g.genre) AS col,
               'genre'        AS colType
        FROM   Movie m
               INNER JOIN Genre g ON m.id = g.Movie_id
        WHERE  g.genre IN (SELECT DISTINCT genre
                           FROM   Genre
                           WHERE  Movie_id = @tmpMovieid)
        GROUP  BY id, title

        UNION ALL

        SELECT id,
               title,
               Count(k.keyword) AS col,
               'Keyword'        AS colType
        FROM   Movie m
               INNER JOIN Keyword k ON m.id = k.Movie_id
        WHERE  k.keyword IN (SELECT DISTINCT keyword
                             FROM   Keyword
                             WHERE  Movie_id = @tmpMovieid)
        GROUP  BY id, title) tmp

WHERE  id <> @tmpMovieid
GROUP  BY id, title
ORDER  BY genre_freq DESC, keyword_freq DESC;

Онлайн-демонстрация:https://www.db -fiddle.com / f / s1xLQ6r4Zwi5hVjCsdcwV8 / 0


SQL Server
Примечание. Поскольку вы использовали «текст» в качестве некоторых типов данных столбца,это нужно было конвертировать для некоторых операций.Но опять же, поскольку вы используете MySQL, вам это не нужно.Я написал это в любом случае, чтобы показать вам разницу и для удовольствия.

DECLARE @tmpMovieID INT;
SET @tmpMovieID = (SELECT DISTINCT id
                   FROM   movie
                   WHERE  Cast(title AS NVARCHAR(MAX)) = 'Harry Potter and the Deathly Hallows');

SELECT tmpGenre.id                  AS id,
       tmpGenre.title               AS title,
       ISNULL(tmpGenre.genre, 0)    AS genre,
       ISNULL(tmpKeyword.keyword,0) AS keyword

FROM   (SELECT id,
               Cast(title AS NVARCHAR(MAX))          AS title,
               Count(Cast(g.genre AS NVARCHAR(MAX))) AS genre
        FROM   movie m
               INNER JOIN genre g ON m.id = g.movie_id
        WHERE  Cast(g.genre AS NVARCHAR(MAX)) IN (SELECT DISTINCT Cast(genre AS NVARCHAR(MAX))
                                                 FROM   genre
                                                 WHERE  movie_id = @tmpMovieID)
        GROUP  BY id, Cast(title AS NVARCHAR(MAX))) tmpGenre

       FULL OUTER JOIN (SELECT id,
                               Cast(title AS NVARCHAR(MAX))            AS title,
                               Count(Cast(k.keyword AS NVARCHAR(MAX))) AS Keyword
                        FROM   movie m
                               INNER JOIN keyword k ON m.id = k.movie_id
                        WHERE  Cast(k.keyword AS NVARCHAR(MAX)) IN
                               (SELECT DISTINCT Cast(keyword AS NVARCHAR(MAX))
                                FROM   keyword
                                WHERE  movie_id = @tmpMovieID)
                        GROUP  BY id, Cast(title AS NVARCHAR(MAX))) tmpKeyword

                    ON tmpGenre.id = tmpKeyword.id
WHERE  tmpGenre.id <> @tmpMovieID
ORDER  BY tmpGenre.genre DESC, tmpKeyword.keyword DESC;

Онлайн-демонстрация: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=a1ee14e1e08b7e55eff2e8e94f89a287&hide=1


Результат

+------+---------------------------+-------------+--------------+
| id   |          title            | genre_freq  | keyword_freq |
+------+---------------------------+-------------+--------------+
| 407  | Cinderella                |          2  |            2 |
| 826  | The Shape of Water        |          2  |            1 |
| 523  | How to Train Your Dragon  |          2  |            0 |
| 799  | Enchanted                 |          1  |            3 |
+------+---------------------------+-------------+--------------+

Кстати, спасибо, что задали четкий вопрос и предоставили схему таблицы, пример данных и желаемый результат.

0 голосов
/ 19 апреля 2019

Как сказал Imre_G, это хороший вопрос, и его объяснение того, что происходит не так, как надо.Вы выбираете жанры и ключевые слова, которые вам не нужны, а затем подсчитываете их, потому что они имеют общий элемент.

Это один из способов исправить это, возможно, не самый лучший, но самый простой:

SELECT
    COALESCE(a.title, b.title) AS title,
    COALESCE(a.year, b.year) AS year,
    a.genre_freq,
    b.keyword_freq
FROM
(SELECT title, year, count(distinct genre) as genre_freq FROM genkeyword where (genre in 
(select genre from genkeyword where title='Harry Potter and the Deathly Hallows') )
AND title <> 'Harry Potter and the Deathly Hallows'
group by title, year) a
LEFT JOIN
(select title, year, 
count(distinct keyword) as keyword_freq 
from genkeyword
where keyword in (select keyword from genkeyword where title='Harry Potter and the Deathly Hallows')
 and title <> 'Harry Potter and the Deathly Hallows' group by title, year) b
 ON b.title = a.title;

Теперь это решение работает только при условии соответствия ключевого слова для фильма.Правильным решением было бы заменить LEFT JOIN на FULL OUTER JOIN, но MySQL по какой-то причине не поддерживает FULL OUTER JOIN s.Для этого также есть решение, но оно длинное и включает много UNION с; (

Как выполнить FULL OUTER JOIN в MySQL?

0 голосов
/ 19 апреля 2019

Вы можете инвертировать свою логику и диск из жанра и ключевых слов, используя подзапрос до суммы

select title,year,
        sum(case when src = 'g' then 1 else 0 end) as genre,
        sum(case when src = 'k' then 1 else 0 end) as keyword
from
(
select 'g' as src, g1.title ,g1.year, g1.genre
from genkeyword g
join genkeyword g1 on g1.genre = g.genre
where g.title =  'Harry Potter and the Deathly Hallows' and g1.title <> 'Harry Potter and the Deathly Hallows'
union
select 'k' as src, g1.title ,g1.year, g1.genre
from genkeyword g
join genkeyword g1 on g1.keyword = g.keyword
where g.title =  'Harry Potter and the Deathly Hallows' and g1.title <> 'Harry Potter and the Deathly Hallows'
) s
group by title , year;

+--------------------------+------+-------+---------+
| title                    | year | genre | keyword |
+--------------------------+------+-------+---------+
| Cinderella               | 2015 |     2 |       2 |
| Enchanted                | 2007 |     1 |       3 |
| How to Train Your Dragon | 2010 |     2 |       0 |
| The Shape of Water       | 2017 |     2 |       4 |
+--------------------------+------+-------+---------+
4 rows in set (0.10 sec)
0 голосов
/ 19 апреля 2019

Один из самых часто задаваемых вопросов, которые я когда-либо видел на SO.

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

Сначала выбираются все строки, содержащие тот же жанр, что и у Гарри Поттера. Затем он выбирает все строки, содержащие ключевые слова. Затем он выполняет подсчет. Очевидно, что это слишком высоко, потому что вы также получите все записи, которые не имеют тот же жанр, но имеют перекрывающиеся ключевые слова. Вы также получите все строки, которые имеют перекрывающиеся жанры, но не перекрывающиеся ключевые слова.

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

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