Oracle
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Oracle

Chức năng Số IsNumber Tùy chỉnh của Oracle với Độ chính xác và Quy mô

Tôi không nghĩ rằng có bất kỳ cách xây dựng đơn giản nào; và thực hiện kiểm tra động tương đối dễ dàng (xem ví dụ bên dưới). Nhưng là một cách tiếp cận khá phức tạp, bạn có thể chuyển đổi chuỗi thành một số và quay lại chuỗi bằng cách sử dụng mô hình định dạng được xây dựng từ độ chính xác và tỷ lệ của bạn:

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;
/

Chỉ được thử nghiệm với một vài giá trị nhưng dường như hoạt động cho đến nay:

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 

Sử dụng WHEN OTHERS không lý tưởng và bạn có thể thay thế nó bằng các trình xử lý ngoại lệ cụ thể. Tôi cho rằng bạn muốn điều này trả về null nếu số không hợp lệ, nhưng tất nhiên bạn có thể trả về bất kỳ thứ gì hoặc đưa ra ngoại lệ của riêng bạn.

isNum2 là từ một chức năng thứ hai, đơn giản hơn nhiều, chỉ thực hiện ép kiểu động - mà tôi biết bạn không muốn làm, điều này chỉ để so sánh:

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;
/

Nhưng lưu ý rằng cast làm tròn nếu tỷ lệ được chỉ định quá nhỏ so với giá trị; Tôi có thể đã giải thích "phù hợp với" quá mạnh trong câu hỏi vì tôi đang sai trong trường hợp đó. Nếu bạn muốn một cái gì đó như '.123', 2, 2 được phép (đưa ra .12 ) rồi đến GetFormat thứ hai cuộc gọi và kiểm tra 'quy mô quá lớn' có thể bị xóa khỏi IsNumber của tôi . Có thể có những sắc thái khác mà tôi cũng đã bỏ qua hoặc hiểu sai.

Cũng cần lưu ý rằng to_number() ban đầu dựa vào cài đặt NLS cho dữ liệu và khớp phiên - đặc biệt là dấu phân tách thập phân; và nó sẽ không cho phép phân tách nhóm.

Có thể đơn giản hơn khi giải cấu trúc giá trị số được truyền vào biểu diễn bên trong của nó và xem liệu giá trị đó có được so sánh với độ chính xác và tỷ lệ hay không ... mặc dù tuyến động tiết kiệm rất nhiều thời gian và công sức.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Làm thế nào để truy cập cơ sở dữ liệu Oracle qua mạng?

  2. Oracle Sequence tiếp theo đang nhảy số qua lại

  3. tràn bộ đệm kích thước dbms_output

  4. Sử dụng GROUP BY với XMLCast và XMLQuery mang lại ORA-22950

  5. Danh sách được phân tách bằng dấu phẩy