Saya tidak berpikir ada cara yang sederhana; dan melakukan pemeriksaan dinamis relatif mudah (lihat contoh di bawah). Tetapi sebagai pendekatan yang agak berbelit-belit, Anda bisa mengonversi string menjadi angka dan kembali ke string menggunakan model format yang dibuat dari presisi dan skala Anda:
CREATE OR REPLACE FUNCTION IsNumber(pVALUE VARCHAR2, pPRECISION NUMBER,
pSCALE NUMBER) RETURN NUMBER
IS
lFORMAT VARCHAR2(80);
lNUMBER NUMBER;
lSTRING NUMBER;
FUNCTION GetFormat(p NUMBER, s NUMBER) RETURN VARCHAR2 AS
BEGIN
RETURN
CASE WHEN p >= s THEN LPAD('9', p - s, '9') END
|| CASE WHEN s > 0 THEN '.' || CASE WHEN s > p THEN
LPAD('0', s - p, '0') || RPAD('9', p, '9')
ELSE RPAD('9', s, '9') END
END;
END GetFormat;
BEGIN
-- sanity-check values; other checks needed (precision <= 38?)
IF pPRECISION = 0 THEN
RETURN NULL;
END IF;
-- check it's actually a number
lNUMBER := TO_NUMBER(pVALUE);
-- get it into the expected format; this will error if the precision is
-- exceeded, but scale is rounded so doesn't error
lFORMAT := GetFormat(pPRECISION, pSCALE);
lSTRING := to_char(lNUMBER, lFORMAT, 'NLS_NUMERIC_CHARACTERS='',.''');
-- to catch scale rounding, check against a greater scale
-- note: this means we reject numbers that CAST will allow but round
lFORMAT := GetFormat(pPRECISION + 1, pSCALE + 1);
IF lSTRING != to_char(lNUMBER, lFORMAT, 'NLS_NUMERIC_CHARACTERS='',.''') THEN
RETURN NULL; -- scale too large
END IF;
RETURN lNUMBER;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL; -- not a number, precision too large, etc.
END IsNumber;
/
Hanya diuji dengan beberapa nilai tetapi tampaknya berfungsi sejauh ini:
with t as (
select '0.123' as value, 3 as precision, 3 as scale from dual
union all select '.123', 2, 2 from dual
union all select '.123', 1, 3 from dual
union all select '.123', 2, 2 from dual
union all select '1234', 4, 0 from dual
union all select '1234', 3, 1 from dual
union all select '123', 2, 0 from dual
union all select '.123', 0, 3 from dual
union all select '-123.3', 4, 1 from dual
union all select '123456.789', 6, 3 from dual
union all select '123456.789', 7, 3 from dual
union all select '101.23253232', 3, 8 from dual
union all select '101.23253232', 11, 8 from dual
)
select value, precision, scale,
isNumber(value, precision, scale) isNum,
isNumber2(value, precision, scale) isNum2
from t;
VALUE PRECISION SCALE ISNUM ISNUM2
------------ ---------- ---------- ---------- ----------
0.123 3 3 .123 .123
.123 2 2 .12
.123 1 3 .123
.123 2 2 .12
1234 4 0 1234 1234
1234 3 1
123 2 0
.123 0 3
-123.3 4 1 -123.3 -123.3
123456.789 6 3
123456.789 7 3
101.23253232 3 8
101.23253232 11 8 101.232532 101.232532
Menggunakan WHEN OTHERS
tidak ideal dan Anda bisa menggantinya dengan penangan pengecualian khusus. Saya berasumsi Anda ingin ini mengembalikan nol jika nomornya tidak valid, tetapi tentu saja Anda dapat mengembalikan apa pun, atau melemparkan pengecualian Anda sendiri.
isNum2
kolom berasal dari fungsi kedua yang jauh lebih sederhana, yang hanya melakukan pemeran secara dinamis - yang saya tahu Anda tidak ingin melakukannya, ini hanya untuk perbandingan:
CREATE OR REPLACE FUNCTION IsNumber2(pVALUE VARCHAR2, pPRECISION NUMBER,
pSCALE NUMBER) RETURN NUMBER
IS
str VARCHAR2(80);
num NUMBER;
BEGIN
str := 'SELECT CAST(:v AS NUMBER(' || pPRECISION ||','|| pSCALE ||')) FROM DUAL';
EXECUTE IMMEDIATE str INTO num USING pVALUE;
RETURN num;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END IsNumber2;
/
Namun perhatikan bahwa cast
putaran jika skala yang ditentukan terlalu kecil untuk nilainya; Saya mungkin telah menafsirkan "sesuai dengan" terlalu kuat dalam pertanyaan karena saya salah dalam kasus itu. Jika Anda menginginkan sesuatu seperti '.123', 2, 2
diizinkan (memberi .12
) lalu GetFormat
kedua panggilan dan centang 'skala terlalu besar' dapat dihapus dari IsNumber
saya . Mungkin ada nuansa lain yang saya lewatkan atau salah tafsirkan.
Juga perlu diperhatikan bahwa to_number()
initial awal bergantung pada pengaturan NLS untuk data dan pencocokan sesi - khususnya pemisah desimal; dan itu tidak akan mengizinkan pemisah grup.
Mungkin lebih mudah untuk mendekonstruksi nilai numerik yang diteruskan ke dalam representasi internalnya dan melihat apakah itu sebanding dengan presisi dan skalanya... meskipun rute dinamis menghemat banyak waktu dan tenaga.