2011-08-02 65 views
2

我寫匹配他們與客戶數據庫輸入電話號碼列表的用戶解決方案的一部分。試圖生成從報表

用戶需要輸入一個逗號分割的電話號碼(整數)的名單,並查詢需要告訴它從他們的名單的電話號碼是不是在數據庫中的用戶。

我能想到的要做到這一點,首先創建一個子集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 (80,80,80,80,...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 ...將不起作用,因爲這將返回哪些數字在數據庫中,但不在我的列表中。

如何使這項工作適用於1000000000-9999999999(或所有美國10位數電話號碼可能性)之間的所有號碼。我可能會在完全錯誤的情況下生成我的初始HUGE列表,然後排除除IN語句之外的所有內容,但我不確定從哪裏開始。

非常感謝您的幫助。我從你們所有人那裏學到了很多東西。

+0

您可以嘗試添加之間的限制內必須通過的範圍。 WHERE INTEGER BETWEEN lowest_number和highest_number –

回答

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) 

第一個查詢將建立與個人的電話號碼清單。

+0

很好地利用正則表達式來簡化我的查詢 - 我應該想到這一點,但我只是從我的舊回答中複製並粘貼,並且懶得試圖進一步簡化它。假設tel_table包含的數字比傳入檢查的數字多得多,使用NOT EXISTS通過索引而不是MINUS操作檢查數字可能會更好,但真正的技巧是將逗號分隔列表拆分爲行加入反對。 –

+0

這就是我正在尋找的東西。非常感謝! – muncherelli

+0

@Stephen:我同意:不是或不存在應該產生比MINUS更好的計劃 –

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 
) 
+0

是的,我想不出一個更聰明的方法來解決這個問題。雖然感覺就像我們把自己塗在了一個角落裏。我唯一的想法是:1)事先把所有這些數字推到一張桌子上實際上是否實用?磁盤便宜,而且速度會更快。 2)根據數字的範圍,是否有辦法限制自己在所提供的最小數量和所提供的最大數量之間生成數字,以便在小於整個列表時加快速度?我猜這個子集會顯着變小。 –

+0

即使對於一個窄(1列)查找表,也有十億條記錄仍然很多。也就是說,這比每次創建10億條記錄要好。至於以更智能的方式生成列表,您需要更多地描述您的環境;如何構建最終查詢?如果它是用另一種語言寫的,那麼什麼能阻止你構建1000條UNION?或者執行一個準備好的查詢多達1000次並將結果匯​​總到客戶端中?等等等等? – MatBailie

+1

是的;這整個問題給我的感覺是需要向後邁出一大步,而不是僅僅沿着目前的方向進行。 「假設我別無選擇,只能切斷我的腿,哪一個最好?」 –

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字符邊界以確保)。