尝试从IN语句生成子集

时间:2011-08-02 14:11:52

标签: sql oracle oracle10g

我正在为用户编写一个解决方案,该解决方案与他们针对客户数据库输入的电话号码列表相匹配。

用户需要输入逗号分隔的电话号码列表(整数),查询需要告诉用户列表中的哪些电话号码不在数据库中。

我能想到的唯一方法是首先创建一个NUMBER_LIST子集,其中包含我可以加入的所有电话号码,然后从我从客户数据库中带回来的那个列表中排除该列表。

WITH NUMBER_LIST AS (
    SELECT INTEGERS 
    FROM (
        SELECT level - 1 + 8000000000 INTEGERS
           FROM dual
        CONNECT BY level <= 8009999999-8000000000+1
    )
    WHERE INTEGERS IN (8001231001,8001231003,8001231234,8001231235,...up to 1000 phone numbers)
)

这里的问题是上面的代码可以很好地创建我的子集,数字在800-000-0000和800-999-9999之间。我的列表和客户数据库中的电话号码可以是任意范围(不仅仅是800个号码)。我这样做只是为了测试。从该查询生成子集大约需要6秒钟。如果我创建CONNECT BY LEVEL以包含从100-000-0000到999-999-9999的所有数字,这些数字正在运行我的查询内存以创建一个大的子集(我相信创建一个巨大的列表是荒谬的过度杀伤并使用我的IN语句将其分解。)

问题在于创建初始子集。我可以处理剩下的查询,但我需要能够生成数字子集,以便从我的IN语句中查询我的客户数据库。

要记住的事情很少:

  1. 我无法先在临时表中加载数字。用户将自己输入“IN(...,...,...)”语句。
  2. 这需要是单个语句,没有额外的功能或变量声明
  3. 数据库是Oracle 10g,我使用SQL Developer来创建查询。
  4. 用户理解他们只能在IN语句中输入1000个数字。这需要足够强大,以便从整个区域代码范围中选择任何1000个数字。
  5. 最终结果是获取不在数据库中的电话号码列表。一个简单的NOT IN ...将不起作用,因为这将带回数据库中的数字,但不在我的列表中。
  6. 如何在1000000000-9999999999(或所有美国10位数电话号码)之间的所有号码上使用此功能。生成我的初始巨型列表,然后排除除IN语句之外的所有内容,我可能会完全错误,但我不确定从何处开始。

    非常感谢您的帮助。我从大家那里学到了很多东西。

4 个答案:

答案 0 :(得分:3)

您可以使用以下内容:

SELECT *
  FROM (SELECT regexp_substr(&x, '[^,]+', 1, LEVEL) phone_number
           FROM dual
         CONNECT BY LEVEL <= length(&x) - length(REPLACE(&x, ',', '')) + 1)
 WHERE phone_number NOT IN (SELECT phone_table.phone_number 
                              FROM phone_table)

第一个查询将构建一个包含各个电话号码的列表。

答案 1 :(得分:2)

这个问题与“我如何绑定列表”问题密切相关,这个问题已在这里出现过几次。我过去发过了一个答案Dynamic query with HibernateCritera API & Oracle - performance

这样的事情应该做你想做的事情:

create table phone_nums (phone varchar2(10));

insert into phone_nums values ('12345');

insert into phone_nums values ('23456');

with bound_inlist
  as
  (
  select
    substr(txt,
           instr (txt, ',', 1, level  ) + 1,
           instr (txt, ',', 1, level+1) - instr (txt, ',', 1, level) -1 )
           as token
    from (select ','||:txt||',' txt from dual)
  connect by level <= length(:txt)-length(replace(:txt,',',''))+1
  )
  select *
from bound_inlist a
where  not exists (select null from phone_nums where phone = token); 

此处以逗号分隔的电话号码列表绑定到查询中,因此您正确使用绑定变量,并且您可以输入可能无限数量的电话号码以便一次性检查(尽管我会检查两者可以肯定的是4000和32767字符边界。

答案 2 :(得分:1)

如果你被约束到必须用IN (n1,n2,n3,...,n1000)来解决,那么你的方法似乎是唯一的解决方案。

正如你所提到的,这是你预先创建的一个大清单。

您是否能够稍微调整您的方法?

WITH NUMBER_LIST (number) AS (
            SELECT n1    FROM DUAL
  UNION ALL SELECT n2    FROM DUAL
  UNION ALL SELECT n3    FROM DUAL
  ...
  UNION ALL SELECT n1000 FROM DUAL
)

答案 3 :(得分:1)

你说你不能使用临时表或过程或自定义函数 - 如果可以的话,这将是一项简单的任务。

用于提交此查询的客户端工具是什么?有没有理由不能查询数据库中的所有电话号码并在客户端进行比较?