我有一个数据库,我需要在其中找到一些缺失的条目并将其填入。 我有一个名为" menu"的桌子,每个餐厅都有多个菜肴,每个菜有4种不同的语言条目(主要数据库实际上是8个,但为了简单起见,我只需要4个),我需要找出哪些菜肴特定餐厅缺少任何语言条目。
select * from menu where restaurantid = 1
我被卡在那里,某些东西沿着语言1或2或3或4不存在的地方,这是一个复杂的位,因为我需要看到存在的语言才能看到语言&#因为我无法展示那些不存在的东西而失踪了。我希望这有道理吗?
在下面的示例表中,2号餐馆的2号餐具缺少语言3,这就是我需要找到的内容。
+--------------+--------+----------+-----------+
| RestaurantID | DishID | DishName | Language |
+--------------+--------+----------+-----------+
| 1 | 1 | Soup | 1 |
| 1 | 1 | Soúp | 2 |
| 1 | 1 | Soupe | 3 |
| 1 | 1 | Soupa | 4 |
| 1 | 2 | Bread | 1 |
| 1 | 2 | Bréad | 2 |
| 1 | 2 | Breade | 3 |
| 1 | 1 | Breada | 4 |
| 2 | 1 | Dish1 | 1 |
| 2 | 1 | Dísh1 | 2 |
| 2 | 1 | Disha1 | 3 |
| 2 | 1 | Dishe1 | 4 |
| 2 | 2 | Dish2 | 1 |
| 2 | 2 | Dísh2 | 2 |
| 2 | 2 | Dishe2 | 4 |
+--------------+--------+----------+-----------+
答案 0 :(得分:2)
就性能而言,反连接模式通常是最有效的。
你的特殊情况有点棘手,因为你需要"生成"缺少的行。如果每个(ResturantID,DishID)应该有4行,语言值为1,2,3和4,我们可以使用CROSS JOIN操作生成所有行的集合。
下一步是将反连接... LEFT OUTER JOIN应用于菜单表中存在的行,这样我们就可以获得CROSS JOIN集中的所有行以及匹配的行。
"技巧"是在WHERE子句中使用谓词来过滤掉我们找到匹配的行,所以我们留下的行没有匹配。
(起初看起来有点奇怪,但是一旦你的大脑被反连接模式缠绕,它就会变得熟悉。)
因此,对此表单的查询应返回指定的结果集。
SELECT d.RestaurantID
, d.DishID
, lang.id AS missing_language
FROM (SELECT 1 AS id UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
) lang
CROSS
JOIN (SELECT e.RestaurantID, e.DishID
FROM menu e
GROUP BY e.RestaurantID, e.DishID
) d
LEFT
JOIN menu m
ON m.RestaurantID = d.RestaurantID
AND m.DishID = d.DishID
AND m.Language = lang.id
WHERE m.RestaurantID IS NULL
ORDER BY 1,2,3
让我们解开那一点。
首先我们得到一个包含数字1到4的集合。
接下来,我们得到一个包含(RestaurantID, DishID)
个不同元组的集合。 (对于每个不同的餐厅,一个不同的DishID列表,只要该组合的任何语言至少有一行。)
我们执行CROSS JOIN,将第一行( lang
)中的每一行与集合中的每一行( d
)相匹配,生成一个完整的"我们希望拥有的每一套(RestaurantID,DishID,语言)。
下一部分是反连接...左外连接到菜单,以找到"完成"中的哪些行。 set在菜单中有一个匹配的行,并过滤掉所有匹配的行。
这可能有点令人困惑。如果我们想到CROSS JOIN操作生成一个看起来像菜单表的临时表,但包含所有可能的行...我们可以用伪代码来考虑它:
create temporary table all_menu_rows (RestaurantID, MenuID, Language) ;
insert into all_menu_rows ... all possible rows, combinations ;
然后反连接模式更容易看到:
SELECT r.RestaurantID
, r.DishID
, r.Language
FROM all_menu_rows r
LEFT
JOIN menu m
ON m.RestaurantID = r.RestaurantID
AND m.DishID = r.DishID
AND m.Language = r.Language
WHERE m.RestaurantID IS NULL
ORDER BY 1,2,3
(但我们不必承担创建和填充临时表的额外开销,我们可以在查询中做到这一点。)
当然,这不是唯一的方法。我们可以使用NOT EXISTS谓词而不是反连接,尽管这通常不那么有效。查询的第一部分是相同的,以生成"完成"我们期望拥有的一组行;不同之处在于我们如何识别菜单表中是否存在匹配的行:
SELECT d.RestaurantID
, d.DishID
, lang.id AS missing_language
FROM (SELECT 1 AS id UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
) lang
CROSS
JOIN (SELECT e.RestaurantID, e.DishID
FROM menu e
GROUP BY e.RestaurantID, e.DishID
) d
WHERE NOT EXISTS ( SELECT 1
FROM menu m
WHERE m.RestaurantID = d.RestaurantID
AND m.DishID = d.DishID
AND m.Language = lang.id
)
ORDER BY 1,2,3
对于"中的每一行" set(由CROSS JOIN操作生成),我们将运行一个相关子查询,检查是否找到匹配的行。如果未找到匹配的行,则NOT EXISTS谓词返回TRUE。 (这有点容易理解,但它通常不会像反连接模式那样表现。)
答案 1 :(得分:0)
如果每个菜单项都应包含每种语言的记录(例如现实生活中的8个),则可以使用以下语句。如果要查看每个餐厅的所有菜单项,并且没有全部8个条目,则可以将数字4更改为8。
SELECT RestaurantID,DishID,COUNT(*) 来自菜单 GROUP BY RestaurantID,DishID 有COUNT(*)< 4