mySQL: 所有可能值至少丢失一次的查询

mySQL: query in which all possible values are missing at least once

我有一份电视节目清单。每个电视节目可能会在 0 个或多个时区被中断。说一个节目在某个时区是 "blacked out" 意味着该网络无权在该时区播放该节目。此数据如下所示:

|----|---------------------|
| ID |         Show        |
|----|---------------------|
|  1 | Nightly News        |
|  2 | Primetime Sitcom    |
|  3 | Daytime Talkshow    |
|  4 | Nightly News II     |
|  5 | Daytime Talkshow II |
|  6 | Nightly News III    |
|----|---------------------|
   |
   |-----join
   |
   v
|----|----------------------|
| ID | Timezone Restriction |
|----|----------------------|
|  1 | EST                  |
|  1 | CST                  |
|  1 | PST                  |
|  2 | EST                  |
|  2 | CST                  |
|  3 | PST                  |
|  5 | CST                  |
|  5 | PST                  |
|  6 | HST                  |
|----|----------------------|

并非所有节目都受时区限制(大多数没有)。鉴于这些数据,我需要获取一个包含尽可能多的结果的列表,以便在每个时区提供 2 个未停播的节目。结果应按 ID 排序,每个时区看到尽可能少的不受限制的 ID。

例如,在上面的数据集中,这个假设的查询将 return 行 1-4,例如:

|----|------------------|--------------|
| ID |       Show       | Restrictions |
|----|------------------|--------------|
|  1 | Nightly News     | EST,CST,PST  |
|  2 | Primetime Sitcom | EST,CST      |
|  3 | Daytime Talkshow | PST          |
|  4 | Nightly News II  | None         |
|----|------------------|--------------|

如您所见,在上面的结果集中,所有时区至少有2个不受限制的节目。 EST 或 CST 的观众可以观看节目 3 和 4。PST 的观众可以观看节目 2 和 4。MST 或 HST 的观众可以观看节目 1 和 2。

我一辈子都想不出 SQL 来解决这个问题(旁注,我实际上不需要结果中的 "restrictions" 列,那只是此处用于说明目的)。

创建一个列出所有时区的 table。然后,您可以 CROSS JOIN 将其与节目列表一起使用,以获取可以显示节目的所有潜在区域。然后使用带有限制 table 的 LEFT JOIN 来过滤掉符合任何限制的行,如 Return row only if value doesn't exist.

中所述
SELECT s.show, z.zone
FROM shows AS s
CROSS JOIN timezones AS z
LEFT JOIN restrictions AS r ON r.id = s.id AND r.`Timezone Restriction` = z.zone
WHERE r.id IS NULL
ORDER BY z.zone, s.id

DEMO

这列出了每个时区可以显示的所有节目,而不仅仅是前 2 个。请参阅 Using LIMIT within GROUP BY to get N results per group? 了解如何限制每组的结果数量。

所以再考虑一下,我很确定我想做的事情是 1) 查找每个时区的不受限制的节目列表,以及 2) UNION 将它们全部放在一起。这实际上看起来几乎完全是我现在想到的用例 UNION 创建的。

所以我可以像这样不受限制地观看单个时区的节目:

SELECT `shows`.`ID`
FROM shows 
LEFT JOIN restrictions
ON `shows`.`ID`=`restrictions`.`ID`
AND `shows`.`ID` NOT IN (
  SELECT `restrictions`.`ID`
  FROM restrictions 
  WHERE `Timezone Restriction`='EST'
) 
LIMIT 2

然后像这样将它们链接在一起:

(SELECT `shows`.`ID` FROM shows  LEFT JOIN restrictions  ON `shows`.`ID`=`restrictions`.`ID`  AND `shows`.`ID` NOT IN (select `restrictions`.`ID` from restrictions where `Timezone Restriction`='EST') LIMIT 2)
UNION
(SELECT `shows`.`ID` FROM shows  LEFT JOIN restrictions  ON `shows`.`ID`=`restrictions`.`ID`  AND `shows`.`ID` NOT IN (select `restrictions`.`ID` from restrictions where `Timezone Restriction`='CST') LIMIT 2)
UNION
(SELECT `shows`.`ID` FROM shows  LEFT JOIN restrictions  ON `shows`.`ID`=`restrictions`.`ID`  AND `shows`.`ID` NOT IN (select `restrictions`.`ID` from restrictions where `Timezone Restriction`='MST') LIMIT 2)
UNION
(SELECT `shows`.`ID` FROM shows  LEFT JOIN restrictions  ON `shows`.`ID`=`restrictions`.`ID`  AND `shows`.`ID` NOT IN (select `restrictions`.`ID` from restrictions where `Timezone Restriction`='PST') LIMIT 2)
UNION
(SELECT `shows`.`ID` FROM shows  LEFT JOIN restrictions  ON `shows`.`ID`=`restrictions`.`ID`  AND `shows`.`ID` NOT IN (select `restrictions`.`ID` from restrictions where `Timezone Restriction`='HST') LIMIT 2)
ORDER BY ID;

建立在 @Barmar 提供的 sqlfiddle 之上:http://www.sqlfiddle.com/#!9/25773/1/0