我有一个数字列表,比如 {2,4,5,6,7} 我有一张表,foos,带有 foos.ID,包括 {1,2,3,4,8,9}
我想获取我的数字列表,并在我的表的 ID 字段中找到那些没有对应项的数字。
实现此目的的一种方法是创建一个表栏,在 ID 字段中加载 {2,4,5,6,7}。 然后,我会做
SELECT bars.* FROM bars LEFT JOIN foos ON bars.ID = foos.ID WHERE foos.ID IS NULL
但是,我想完成这个没有临时表的工作。
有人对它如何发生有任何意见吗?
这是一个很常见的问题:在不创建表的情况下动态生成关系。这个问题的 SQL 解决方案非常笨拙。使用派生表的一个示例:
SELECT n.id
FROM
(SELECT 2 AS id
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7) AS n
LEFT OUTER JOIN foos USING (id)
WHERE foos.id IS NULL;
但这并不能很好地扩展,因为您可能有很多值,而不仅仅是六个。构建一个每个值都需要一个
UNION
的长列表可能会变得很烦人。
另一种解决方案是手头保留一张十位数的通用表格,并在多种用途下重复使用它。
CREATE TABLE num (i int);
INSERT INTO num (i) VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9);
SELECT n.id
FROM
(SELECT n1.i + n10.i*10 AS id
FROM num AS n1 CROSS JOIN num AS n10
WHERE n1.i + n10.i*10 IN (2, 3, 4, 5, 6, 7)) AS n
LEFT OUTER JOIN foos USING (id)
WHERE foos.id IS NULL;
我展示了内部查询从 0..99 生成的值,尽管这对于这种情况不是必需的。但是您的列表中的值可能大于 10。关键是,使用一张表
num
,您可以生成大量数字,而不必求助于每个值一个 UNION
的非常长的链。此外,您可以在一个地方指定所需值的列表,这样更方便和可读。
我找不到不使用临时表的精确问题的解决方案,但是使用子选择而不是连接进行查询的另一种方法是:
SELECT bars.* FROM bars WHERE bars.ID NOT IN (SELECT ID FROM foos)
就像我最初写的其他海报一样:
SELECT * FROM foos WHERE foos.ID NOT IN (2, 4, 5, 6, 7)
但后来我意识到这与你想要的相反。
如果你使用 PHP,你可以在不创建任何临时表的情况下完成这项工作。
SELECT ID FROM foos WHERE foos.ID IN (2, 4, 5, 6, 7)
您可以使用 PHP 的 array_diff() 函数将其转换为所需的结果。如果您的列表 (2,4,5,6,7) 在名为 $list 的数组中,并且上面查询的结果在数组 $result 中,那么
$no_counterparts = array_diff($list, $result);
...将返回列表中的所有数字,而数据库表中没有对应的数字。虽然此解决方案不会在查询中执行整个操作,但您需要在 PHP 中执行的后处理最少以获得您想要的结果,并且避免必须创建临时表可能是值得的。
我遇到了类似的问题。我有一个范围,其中自动递增的主键有一些缺失值,所以首先我发现有多少:
select count(*) from node where nid > 1962
。
将此数字与最高值进行比较,我发现数字丢失了。然后我运行了这个查询:
select n2.nid from node n1 right join node n2 on n1.nid = (n2.nid - 1) where n1.nid is null and n2.nid > 1962
这将找到非连续缺失记录的数量。它不会显示连续的,而且我不完全确定该怎么做,除了更改 ON 子句以允许更大的自由度(这会使 JOIN 表大得多)。
无论如何,这给了我七个缺失中的五个结果,另外两个保证至少与五个中的一个相邻。如果你有更多的缺失,你可能需要一些其他的方法来找到剩余的缺失。
Alnitak 的(和你的)解决方案应该有效,我不能考虑其他任何只能在 SQL 语言中工作的方法。
但是问题来了——你如何传递值列表?在调用代码中处理这个不是更好吗 - 即请求 ID 并在 colling 代码中比较它,这可能是一种更适合这种操作的语言。
碰巧到达这里寻找答案。较早的帖子是 MySQL 8 之前的帖子。由于 MySQL 从 8.0.19 版本开始就有
value
语句,因此可以使用 value
语句和 CTE
非常优雅地解决这个问题,它也可以从 MySQL 8.0 开始使用
第一步: 结合 CTE 和 value 语句创建一组行,其中包含需要与表进行比较的值(这里的表是
foo
)。
with MyValues(val) as
(
values row(2),row(4),row(5),row(6),row(7)
)
步骤 2: 将 CTE 与表
foo
进行外部连接,并在使用 foo
进行外部连接后从 CTE 中过滤具有空值的行
WITH myvalues(val)
AS (VALUES ROW(2), ROW(4), ROW(5), ROW(6), ROW(7))
SELECT f.id
FROM foo f
LEFT OUTER JOIN myvalues m
ON f.id = m.val
WHERE m.val IS NULL;
足迹:
mysql> WITH myvalues(val)
-> AS (VALUES ROW(2), ROW(4), ROW(5), ROW(6), ROW(7))
-> SELECT f.id
-> FROM foo f
-> LEFT OUTER JOIN myvalues m
-> ON f.id = m.val
-> WHERE m.val IS NULL;
+------+
| id |
+------+
| 1 |
| 3 |
| 8 |
| 9 |
+------+
4 rows in set (0.00 sec)