MySQL-确定给定列表中存在但表中不存在的数据

时间:2018-08-24 13:15:17

标签: mysql sql

我有一张桌子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

但是,我无权这样做(只读访问权限)。

4 个答案:

答案 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      |

View on DB Fiddle

注意

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;

这是小提琴-http://www.sqlfiddle.com/#!9/4645be/24

答案 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

参见演示http://sqlfiddle.com/#!9/31956e/35

答案 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|
*/