在列表中找到数据库列中不存在的数字

时间:2017-01-12 00:56:47

标签: java oracle

我有一个要求,我需要在ArrayList中找到第一个缺少的数字,而数据库(Oracle)列中没有这个数字。

情景是这样的:

表1:

enter image description here

从上表中我想制作3个列表

List<Integer> lst1 = new ArrayList<Integer>();
List<Integer> lst2 = new ArrayList<Integer>();
List<Integer> lst3 = new ArrayList<Integer>();

lst1 - &gt; [0,1,2,3,4,5,6 .... 1000]

lst2 - &gt; [A0,A1,A2,A3,A4,A5,A6 .... A1000]

lst3 - &gt; [B0,B1,B2,B3,B4,B5,B6 .... B1000]

截至目前,列表中包含大约1000个序列顺序值。

现在我有一个数据库表,如下所示

enter image description here

如何将lsts与Range列匹配。 我需要找到这个表中没有来自lsts的值?

就像在这种情况下,如果我们从“lst1”看到表中没有的第一个可用值是“1”,那么下一个可用值是“3”然后是5.6 ......等等。 类似地,对于“lst2”,第一个缺失值是“a3”

有什么办法吗?

1 个答案:

答案 0 :(得分:1)

以下查询获取range_start和range_end的前缀和数值 为了简化示例,我将范围限制为0-5

SELECT  lstname,
        regexp_substr( rangestart, '[^0-9]') AS Prefix,
        regexp_substr( rangestart, '[0-9]') AS r_start,
        regexp_substr( rangeend, '[0-9]') AS r_end
FROM table_1

LSTNAME |PREFIX |R_START |R_END |
--------|-------|--------|------|
Lst1    |       |0       |5     |
Lst2    |a      |0       |5     |
Lst3    |b      |0       |5     |

以下查询将使用上述查询作为子查询生成范围的所有值 请注意CROSS JOIN LATERAL适用于Oracle 12c及更高版本,如果您使用的是早期版本,则必须重写此查询。

SELECT lstname, prefix || val AS val
FROM (
        SELECT  lstname,
                regexp_substr( rangestart, '[^0-9]') AS Prefix,
                regexp_substr( rangestart, '[0-9]') AS r_start,
                regexp_substr( rangeend, '[0-9]') AS r_end
        FROM table_1
) x
CROSS JOIN LATERAL (
   SELECT LEVEL - 1 + x.r_start AS val
   FROM dual
   CONNECT BY LEVEL <= x.r_end - x.r_start + 1
)

LSTNAME |VAL |
--------|----|
Lst1    |0   |
Lst1    |1   |
Lst1    |2   |
Lst1    |3   |
Lst1    |4   |
Lst1    |5   |
Lst2    |a0  |
Lst2    |a1  |
Lst2    |a2  |
Lst2    |a3  |
Lst2    |a4  |
Lst2    |a5  |
Lst3    |b0  |
Lst3    |b1  |
Lst3    |b2  |
Lst3    |b3  |
Lst3    |b4  |
Lst3    |b5  |

现在说table_2包含以下值:

SELECT * FROM table_2

RANGE |
------|
3     |
a0    |
a1    |
a5    |
b3    |
b4    |
b5    |

要查找缺失值,只需将上述查询LEFT JOIN到此表并过滤掉非空值 请注意,我在引号中使用"RANGE"作为此示例中的列名,因为RANGE是Oracle中的保留字

SELECT lstname, val
FROM (
        SELECT lstname, prefix || val AS val
        FROM (
                SELECT  lstname,
                        regexp_substr( rangestart, '[^0-9]') AS Prefix,
                        regexp_substr( rangestart, '[0-9]') AS r_start,
                        regexp_substr( rangeend, '[0-9]') AS r_end
                FROM table_1
        ) x
        CROSS JOIN LATERAL (
           SELECT LEVEL - 1 + x.r_start AS val
           FROM dual
           CONNECT BY LEVEL <= x.r_end - x.r_start + 1
        )
) XX
LEFT JOIN table_2 t2
ON t2."RANGE" = xx.val
WHERE t2."RANGE" IS NULL
ORDER BY 1, 2;

LSTNAME |VAL |
--------|----|
Lst1    |0   |
Lst1    |1   |
Lst1    |2   |
Lst1    |4   |
Lst1    |5   |
Lst2    |a2  |
Lst2    |a3  |
Lst2    |a4  |
Lst3    |b0  |
Lst3    |b1  |
Lst3    |b2  |

此版本的子查询模拟了横向连接,应该可以在Oracle 10上运行,但我还没有测试过它

SELECT lstname, 
   prefix || column_value AS val
FROM (
        SELECT  lstname,
                regexp_substr( rangestart, '[^0-9]') AS Prefix,
                regexp_substr( rangestart, '[0-9]') AS r_start,
                regexp_substr( rangeend, '[0-9]') AS r_end
        FROM table_1
) x
CROSS JOIN  table(cast(multiset(
   SELECT LEVEL - 1 + x.r_start AS val
   FROM dual
   CONNECT BY LEVEL <= x.r_end - x.r_start + 1
) as sys.OdciNumberList)) q
;