我有一张表,实质上就是这种结构:
key value ------ ------ 2 val1 3 val2 5 val3
键是从1到(当前)100万的连续整数,每天增加数千。删除记录时会出现密钥错误。
我正在寻找一个返回此内容的SQL查询:
key value ------ ------ 1 2 val1 3 val2 4 5 val3
我可以看到如何通过连接到具有完整键列表的第二个表来执行此操作。但是我更喜欢使用标准SQL(没有存储过程或第二个键表)的解决方案,无论密钥的上限是什么,它都可以工作。
答案 0 :(得分:3)
SQL查询没有循环机制。过程语言有循环,但查询本身只能“循环”它们在表(或派生表)中找到的数据。
我在动态生成数字列表时所做的是在数字0到9的小表上进行交叉连接:
CREATE TABLE n (d NUMERIC);
INSERT INTO n VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9);
然后生成00..99:
SELECT n1.d + n2.d*10 AS d
FROM n AS n1 CROSS JOIN n AS n10;
如果你只想要00..57:
SELECT n1.d + n2.d*10 AS d
FROM n AS n1 CROSS JOIN n AS n2
WHERE n1.d + n2.d*10 <= 57;
你当然可以加入100个地方的表,1000个地方等。请注意,你不能在WHERE子句中使用列别名,所以你必须重复完整的表达式。
现在,您可以将其用作FROM
子句中的派生表,并将其连接到数据表。
SELECT n0.d, mytable.value
FROM
(SELECT n1.d + n2.d*10 + n2.d*100 + n3.d*1000
+ n4.d*10000 + n5.d*100000 AS d
FROM n AS n1 CROSS JOIN n AS n2 CROSS JOIN n AS n3
CROSS JOIN n AS n4 CROSS JOIN n AS n5) AS n0
LEFT OUTER JOIN mytable ON (n0.d = mytable.key)
WHERE n0.d <= (SELECT MAX(key) FROM mytable);
每次表格超过一个数量级时,您需要添加另一个CROSS JOIN
。例如。当它增长超过100万时,为n6
添加联接。
另请注意,我们现在可以在外部查询的WHERE子句中使用列别名。
不可否认,仅在SQL中执行此操作可能是一个非常昂贵的查询。您可能会发现通过编写一些应用程序代码来“填补空白”既简单又快捷。
答案 1 :(得分:1)
另一种方法是创建百万个数字的结果集,并将其用作连接的基础。那可能对你有用。 (从ASKTOMs Blog)
被盗select level
from dual
connect by level <= 1000000
产生类似这样的东西
WITH
upper_limit AS
(
select 1000000 limit from dual
),
fake_table AS
(
select level key
from dual
connect by level <= (select limit from upper_limit)
)
select key, value
from table, fake_table
where fake_table.key = table.key(+)
我不上班,所以我无法测试。你的旅费可能会改变。我在工作中使用Oracle。
答案 2 :(得分:0)
在MySQL中,您可以通过对正负偏移执行左连接来找到间隙的边缘。
例如:
create table seq ( i int primary key, v varchar(10) );
insert into seq values( 2, 'val1' ), (3, 'val2' ), (5, 'val3' );
select s.i-1 from seq s left join seq m on m.i = (s.i -1) where m.i is null;
+-------+
| s.i-1 |
+-------+
| 1 |
| 4 |
+-------+
select s.i+1 from seq s left join seq m on m.i = (s.i +1) where m.i is null;
+-------+
| s.i+1 |
+-------+
| 4 |
| 6 |
+-------+
这并不能完全满足您的需求,但提供了足够的信息来确定缺失的行。
答案 3 :(得分:0)
WITH range (num) AS (
SELECT 1 -- use your own lowerbound
UNION ALL
SELECT 1 + num FROM range
WHERE num < 10 -- use your own upper bound
)
SELECT r.num, y.* FROM range r left join yourtable y
on r.num = y.id