如何在 postgres 中僅儲存美國/加拿大電話號碼?
我只想在 Postgres TEXT 列中儲存美國和加拿大的電話號碼。我不想使用
pg_libphonenumber
.為此,我應該使用什麼 CHECK 約束?
你需要在
REGULAR EXPRESSION
這裡掌握 s 。現在,PostgreSQL 在這個領域提供了一系列可能性,它遠遠超出了這裡的答案範圍來教授關於正則表達式的所有內容——它本身就是電腦科學的整個領域——恕我直言,這是更好的教程網站之一。首先要做的事情——電話號碼是一個字元串(PostgreSQL 方言中的 TEXT——其他人中的 VARCHAR())——一個不會加、減、乘或除電話號碼。此外,其中可能出現0-9 以外的字元(即
(
、)
、-
和.
空格)。正如@LaurenzAlbe 指出的那樣,清楚地了解實際需求是件好事。因此,您需要檢查您的字元串以確保它們對應於北美電話號碼(下面的所有程式碼都可以在此處的小提琴中找到)!
北美電話號碼的長度為 10 位,通常由空格分隔為三組,每組 3 位、3 位和 4 位。
因此,作為第一個近似值,您可以執行以下操作:
CREATE TABLE phnum_1 ( num TEXT NOT NULL CONSTRAINT num_1_ck_1 CHECK (num ~ '^\d{3} \d{3} \d{4}$') CONSTRAINT num_1_ck_2 CHECK (num ~ '^[0-9]{3} [0-9]{3} [0-9]{4}$') CONSTRAINT num_1_ck_3 CHECK (num ~ '^[[:digit:]]{3} [[:digit:]]{3} [[:digit:]]{4}$') );
請注意,所有這三個
CONSTRAINT
s 都做同樣的事情——只是表達方式不同。這些是非常簡單的正則表達式(見下文),您只需確保每個條目
num
必須以 3 位數字開頭,後跟一個空格,後跟 4 位數字,另一個空格和 4 個最終數字。正則表達式的解釋:
^
- 是一個"anchor"
- 它指的是要檢查的字元串的開頭。\d
(或者$$ 0-9 $$或者$$ [:digit: $$]) 是數字字元的簡寫 - (即 0、1、2…、9){n}
是一種說法n
,並且只n
出現前一件事 - 在這種情況下,是一個數字 - 例如,您可以說,{2,4}
這意味著您的匹配出現 2 到 4 次。在上面,您可以根據需要使用冗餘的 {3,3}/{4,4} 嗎?- 然後是一個空格 - 文字空格字元 - 它在正則表達式中沒有特殊含義。
- 然後
\d{4} \d{4}
- 4 位數字,一個空格,然後再增加 4 位數字。- 最後,另一個錨點——
$
作為字元串標記結尾的字元!因此,正如您從小提琴中看到的那樣,按原樣
'123 345 3434'
接受987 654 3210
,但'123-234-5678'
被拒絕。所以,這個正則表達式很簡單,但非常嚴格。現在,這個特殊要求的複雜程度可能很快就會變得平流層……
有左方括號和右方括號(
(
或)
)(國際程式碼),+
或在國際程式碼之前沒有任何內容),本地交換程式碼是否有效,連字元(-
)是否在數字組之間?真的,可能性幾乎是無限的……我會敦促您在這裡查看,看看我們的姊妹網站 StackOverflow 上是如何處理一些/許多這些棘手問題的。
我已將其中一些正則表達式放入小提琴中,如下所示:
CREATE TABLE phnum_2 ( num TEXT NOT NULL );
並用一些樣本潛在數字填充它:
INSERT INTO phnum_2 VALUES ('123-456-7890'), ('987 654 3210'), ('123-234-5678'), ('+1 123 456 7890'), ('+353 123 456 7890');
然後執行這個查詢:
SELECT num ~ '\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}$' AS re1, num ~ '^(\+\d{1,2}\s)?((\(\d{3}\))|(\d{3}))[\s.-]\d{3}[\s.-]\d{4}$' AS re2, num ~ '^\s*(?:\+?(\d{1,3}))?[-. (]*(\d{3})[-. )]*(\d{3})[-. ]*(\d{4})(?: *x(\d+))?\s*$' AS re3, num ~ '^(\+\d{1,2}\s?)?1?\-?\.?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$' AS re4, num ~ '^(\+1\s?)?1?\-?\.?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$' AS re5, num ~ '^\s*(?:\+?(\d{1,3}))?[-. (]*(\d{3})[-. )]*(\d{3})[-. ]*(\d{4})(?: *x(\d+))?\s*$' AS re6, num ~ '(\+\d{1,3}\s?)?((\(\d{3}\)\s?)|(\d{3})(\s|-?))(\d{3}(\s|-?))(\d{4})(\s?(([E|e]xt[:|.|]?)|x|X)(\s?\d+))?' AS re7 FROM phnum_2;
結果:
re1 re2 re3 re4 re5 re6 re7 t t t t t t t t t t t t t t t t t t t t t t t t t t t t t f t f f t t
我會敦促你看看那些失敗的,並試圖找出他們失敗的原因!
這裡有幾個不太複雜的,更複雜的執行緒可以在這裡找到,這個站點提供了很多可能性,包括這個怪物:
^(\+?1(-|\.|\s)?)?((\(((8(00|22|33|44|55|66|77|[8[0-9]))|900)\)|((8(00|22|33|44|55|66|77|[8[0-9]))|900))(-|\.|\s)?\d{3}(-|\.|\s)?\d{4}|(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))(-|\.|\s)?[2-9]([02-9]\d|1[02-9](-|\.|\s)?\d{4}))$
但是,我將把最後一句話留給這位指出:
如果使用者想給你他們的電話號碼,那麼相信他們會做對。如果他們不想給你,那麼強迫他們輸入一個有效的數字會將他們發送到競爭對手的網站,或者讓他們輸入一個適合你的正則表達式的隨機字元串。我什至可能會想查找收費率占星熱線的號碼,然後輸入。
我還將以下任何一項視為網站上的有效條目:
- “123 456 7890 至下午 6 點,然後撥打 098 765 4321”
- “123 456 7890 或試試我的手機 098 765 4321”
- “前目錄 - 管好自己的事”
此外,不要忘記正則表達式在處理能力方面的成本很高- 請參閱 StackExchange 的一位創始人的這篇文章,以及如何(部分)從StackOverflow 上最多產的正則表達式回答者之一那裡減輕這種情況。
因此,您真的應該考慮您的要求 - 以及最終答案將如何儲存 - 作為自由文本或嚴格作為 10 個系列
$$ valid $$數字?表中的數據越乾淨,使用優化索引策略的潛力就越大。