我有一张桌子my_tab
:
+--------+
| tab_id |
+--------+
| 1 |
| 2 |
| ... |
| 50 |
| 56 |
| 100 |
+--------+
*请注意,我没有tab_id = 51, 52, 53, 54 or 55
现在,我有一个ID列表。
1, 2, 3, 4, ..., 50, 51, 52, 53, 54, 55, 56, ..., 100
。
我想知道表my_tab
中不存在ID列表中的哪个ID。
我的意思是,所需的输出应为51, 52, 53, 54 and 55
。
我考虑过使用临时表,将list
存储在其中,然后对Left JOIN
进行my_tab
。
但是,我无权这样做(只读访问权限)。
答案 0 :(得分:1)
如果您的mysql版本支持cte,则可以尝试使用 CTE Recursion 创建一个完整的表,并使用NOT exists
检查my_tab
表中是否不存在值。 / p>
这是您的小样本。
模式(MySQL v8.0)
create table my_tab(
tab_id int
);
insert into my_tab values (1);
insert into my_tab values (2);
insert into my_tab values (3);
insert into my_tab values (4);
insert into my_tab values (5);
insert into my_tab values (9);
insert into my_tab values (10);
insert into my_tab values (20);
查询#1
WITH RECURSIVE cte AS (
SELECT MIN(tab_id) fromVal,MAX(tab_id) toVal
FROM my_tab
UNION ALL
SELECT (fromVal+1),toVal
FROM cte
WHERE fromVal < toVal
)
SELECT fromVal
FROM cte c
WHERE NOT exists
(
SELECT 1
FROM my_tab t1
WHERE t1.tab_id = c.fromVal
);
| fromVal |
| ------- |
| 6 |
| 7 |
| 8 |
| 11 |
| 12 |
| 13 |
| 14 |
| 15 |
| 16 |
| 17 |
| 18 |
| 19 |
注意
mysql CTE RECURSIVE默认深度为1000。
如果您需要使用超过1000个,可以尝试设置@@cte_max_recursion_depth值。
答案 1 :(得分:1)
这是不使用CTE的另一种方法-
GMap.NET.WindowsForms.GMapControl gmap = new GMap.NET.WindowsForms.GMapControl();
gmap.MapProvider = GMap.NET.MapProviders.GoogleMapProvider.Instance;
GMap.NET.GMaps.Instance.Mode = GMap.NET.AccessMode.ServerOnly;
gmap.Position = new GMap.NET.PointLatLng(48.8589507, 2.2775175);
gmap.Width = 500;
gmap.Height = 500;
gmap.Zoom = 13;
gmap.ShowCenter = false;
答案 2 :(得分:1)
想知道表中不存在ID列表的哪个ID my_tab。我的意思是,期望的输出应该是51、52、53、54和55。
我假设使用当前数据示例和数字列表,期望的输出将是
| number |
|--------|
| 3 |
| 4 |
| 51 |
| 52 |
| 53 |
| 54 |
| 55 |
如您所见,此查询允许在数字列表和表数据中留出空白。
查询
SELECT
number_list.number
FROM (
SELECT
1 AS number
UNION
SELECT
2 AS number
UNION
SELECT
3 AS number
UNION
SELECT
4 AS number
# ...
# ...
UNION
SELECT
50 AS number
UNION
SELECT
51 AS number
UNION
SELECT
52 AS number
UNION
SELECT
53 AS number
UNION
SELECT
54 AS number
UNION
SELECT
55 AS number
UNION
SELECT
56 AS number
UNION
# ...
# ...
SELECT
100 AS number
) AS number_list
LEFT JOIN
Table1
ON
number_list.number = Table1.tab_id
WHERE
Table1.tab_id IS NULL
结果
| number |
|--------|
| 3 |
| 4 |
| 51 |
| 52 |
| 53 |
| 54 |
| 55 |
参见演示http://sqlfiddle.com/#!9/31956e/13
由于未发表评论而进行更新。
您不认为如果号码列表增加到1000个,几乎是不可能的 通过仅使用Select或最多10000生成列表 对此,我很确定。
更动态的查询需要MySQL数字生成器和嵌套的子索引函数才能工作。
数字生成器查询,生成1到100的数字。
因此,该查询仅适用于最多100个号码的号码列表。
如果您需要更多,只需添加一个新的
CROSS JOIN (
SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10
) AS record_<number>
查询
SELECT
(@row_number := @row_number + 1) AS row_number
FROM (
SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10
) AS record_1
CROSS JOIN (
SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10
) AS record_2
CROSS JOIN (SELECT @row_number := 0) AS init_user_param
参见演示http://sqlfiddle.com/#!9/31956e
使用嵌套的SUBSTRING_INDEX
函数在MySQL中从字符串中拆分项目。
查询
SELECT
SUBSTRING_INDEX(
SUBSTRING_INDEX(
'1,2,3,4,50,51,52,53,54,55,56,100'
,',', 1
)
, ','
, -1
) AS number
参见演示http://sqlfiddle.com/#!9/340e01/528
使用两种方法并获得所需的结果。
查询
SELECT
number_list.number
FROM (
SELECT
DISTINCT
SUBSTRING_INDEX(
SUBSTRING_INDEX(
'1,2,3,4,50,51,52,53,54,55,56,100' #this is the number list
,',', number_generator.row_number
)
, ','
, -1
) AS number
FROM (
SELECT
(@row_number := @row_number + 1) AS row_number
FROM (
SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10
) AS record_1
CROSS JOIN (
SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10
) AS record_2
CROSS JOIN (SELECT @row_number := 0) AS init_user_param
) AS number_generator
) AS number_list
LEFT JOIN
Table1
ON
number_list.number = Table1.tab_id
WHERE
Table1.tab_id IS NULL
答案 3 :(得分:0)
使用MySql 8的此示例的关键是not in
:
drop table if exists tableA ;
create table tableA (tabId int) ;
insert into tableA (tabId) values
(1), (2), (3), (4), (22), (19), (50), (51), (52), (53), (54), (55), (56), (88), (76), (100) ;
drop table if exists tableB ;
create table tableB (tabId int) ;
insert into tableB (tabId) values
(1), (5), (3), (4), (22), (19), (50), (56), (88), (76), (100) ;
select *
from tableA
where tableA.tabId not in (select tabId from tableB) ;
产生:
2
51
52
53
54
55
此线程中还有not in ...
的一些类似用法:SQL: find missing IDs in a table
编辑:两个相关示例:
-- ----------------------------------------------------------------
-- Example 2: Which Id's are in which table
--
select *
from
(
select distinct tabId from tableA
union
select distinct tabId from tableB
) as unionAB
left join tableA on unionAB.tabId = tableA.tabId
left join tableB on unionAB.tabId = tableB.tabId
order by unionAB.tabId
;
/*
tabId |tabId |tabId |
------|------|------|
1| 1| 1|
2| 2|[NULL]|
3| 3| 3|
4| 4| 4|
5|[NULL]| 5|
19| 19| 19|
22| 22| 22|
50| 50| 50|
51| 51|[NULL]|
52| 52|[NULL]|
53| 53|[NULL]|
54| 54|[NULL]|
55| 55|[NULL]|
56| 56| 56|
76| 76| 76|
88| 88| 88|
100| 100| 100|
*/
-- ----------------------------------------------------------------
-- Example 3: Which Id's are missing from both tables
--
-- Determine the start/end of the range of Id's
set @startId = least((select min(tabId) from tableA), (select min(tabId) from tableB)) ;
set @endId = greatest((select max(tabId) from tableA), (select max(tabId) from tableB)) ;
select @startId, @endId ;
/*
@startId|@endId|
--------|------|
1| 100|
*/
-- Build the full list of Id's from min to max (no gaps), using a "Common Table Expression"
-- This is based on @Brad's post in Stackoverflow: https://stackoverflow.com/questions/2157282/generate-days-from-date-range
with recursive lstIds as
(
select @startId as 'tabId'
union
select lstIds.tabId + 1 as 'tabId'
from lstIds
where lstIds.tabId < @endId
) -- no trailing ';' needed/allowed
-- Extract the new data so that it can be used
-- Note: this 'select' needs to be imediately after the 'with' block
select *
from lstIds
-- Join the other tables into the full list
left join tableA on lstIds.tabId = tableA.tabId
left join tableB on lstIds.tabId = tableB.tabId
-- where isnull(tableA.tabId) and isnull(tableB.tabId) -- uncomment this line to see only the Id's that don't have a match in both tables
order by lstIds.tabId
;
/*
tabId |tabId |tabId |
------|------|------|
1| 1| 1|
2| 2|[NULL]|
3| 3| 3|
4| 4| 4|
5|[NULL]| 5|
6|[NULL]|[NULL]|
7|[NULL]|[NULL]|
{ snipped repeated nulls }
17|[NULL]|[NULL]|
18|[NULL]|[NULL]|
19| 19| 19|
20|[NULL]|[NULL]|
21|[NULL]|[NULL]|
22| 22| 22|
23|[NULL]|[NULL]|
24|[NULL]|[NULL]|
{ snipped repeated nulls }
48|[NULL]|[NULL]|
49|[NULL]|[NULL]|
50| 50| 50|
51| 51|[NULL]|
52| 52|[NULL]|
53| 53|[NULL]|
54| 54|[NULL]|
55| 55|[NULL]|
56| 56| 56|
57|[NULL]|[NULL]|
58|[NULL]|[NULL]|
{ snipped repeated nulls }
74|[NULL]|[NULL]|
75|[NULL]|[NULL]|
76| 76| 76|
77|[NULL]|[NULL]|
78|[NULL]|[NULL]|
{ snipped repeated nulls }
86|[NULL]|[NULL]|
87|[NULL]|[NULL]|
88| 88| 88|
89|[NULL]|[NULL]|
90|[NULL]|[NULL]|
{ snipped repeated nulls }
98|[NULL]|[NULL]|
99|[NULL]|[NULL]|
100| 100| 100|
*/