SQL:了解WHERE子句中的OR运算符

时间:2019-04-19 08:29:20

标签: mysql sql

我有一个名为Movie,Genre和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')) 

我的意思是说,生成了两个结果集,一个结果集包含所有具有在Harry Potter中流派的元组(让它为R1),另一个包含所有具有关键字in中的元组。哈利·波特(让这个叫R2)?

如果所考虑的元组包含流派结果集R1中的流派或关键字结果集R2中的关键字,则对流派/关键字进行计数。我不确定在这种情况下count(distinct genre)和count(distinct关键字)的工作方式。如果元组包含R1中的流派,则仅对流派计数还是对关键字进行计数?当元组在R2中包含关键字时,情况也是如此,类型和关键字一样被计算吗?

我不明白为什么我从查询中弄错了genre_freq和keyword_freq值。这是因为我不完全了解幕后如何统计类型和关键字频率。任何见解都会受到赞赏。

4 个答案:

答案 0 :(得分:0)

到目前为止,我对SO提出的最常见的问题之一。

回答您的问题。 OR子句基本上将关键字部分和体裁部分的结果相互粘贴在一起。 SQL在行(或记录)中工作,因此您应该始终在行中思考。

首先,它选择所有包含相同类型的行,例如Harry Potter。然后,它选择包含关键字的所有行。然后它执行计数。显然,这太高了,因为您还将获得所有具有相同流派但具有重叠关键字的记录。您还将获得所有具有重叠流派但没有重叠关键字的行。

要正确计数记录,只需将OR更改为AND。这只会选择具有相同流派以及包含关键字的记录。计数这些将产生正确的结果。

答案 1 :(得分:0)

正如Imre_G所说,这是一个很好的问题,他对问题出在哪里的解释很明确。您基本上是在选择不需要的类型和关键字,然后对它们进行计数,因为它们具有相同的元素。

这是修复它的一种方法,也许不是最好的,而是最简单的:

docker.for.win.localhost

现在,该解决方案仅在电影匹配关键字时才有效。正确的解决方案是将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,但是MySQL由于某些原因不支持FULL OUTER JOIN。也有一个解决方案,但是很长,并且涉及很多FULL OUTER JOIN;((

How to do a FULL OUTER JOIN in MySQL?

答案 2 :(得分:0)

在总计之前,您可以使用子查询来反转逻辑并从类型和关键字中获取动力

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)

答案 3 :(得分:0)

尝试此查询。
我没有使用您创建的任何视图,但是您可以根据需要使用这些视图。

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 |
+------+---------------------------+-------------+--------------+

顺便说一句,谢谢您提出一个明确的问题,并给出了表模式,示例数据和所需的输出。