本文首先會介紹自動類型轉換的缺點,然后闡述Oracle自動類型轉換的規則,并結合實例分析自動類型轉換可能造成的問題。
SELECT TO_DATE(SYSDATE,YYYYMM) FROM DUAL;
DINGJUN123>ALTER SESSION SET NLS_DATE_FORMAT = YYYYMMDD;
DINGJUN123>SELECT TO_DATE(SYSDATE,YYYYMMDD) FROM DUAL;
TO_DATE(
--------
20210611
--下面的出錯了,因為自動轉換后SYSDATE變為字符串,格式是YYYYMMDD。
DINGJUN123>SELECT TO_DATE(SYSDATE,YYYYMM) FROM DUAL;
SELECT TO_DATE(SYSDATE,YYYYMM) FROM DUAL
*
第 1 行出現錯誤:
ORA-01830: 日期格式圖片在轉換整個輸入字符串之前結束
DINGJUN123>ALTER SESSION SET NLS_DATE_FORMAT = YYYYMM;
會話已更改。
DINGJUN123>SELECT TO_DATE(SYSDATE,YYYYMM) FROM DUAL;
TO_DAT
------
202106
DINGJUN123>SELECT TO_DATE(SYSDATE,YYYYMMDD) FROM DUAL;
SELECT TO_DATE(SYSDATE,YYYYMMDD) FROM DUAL
*
第 1 行出現錯誤:
ORA-01840: 輸入值對于日期格式不夠長
DINGJUN123>ALTER SESSION SET NLS_DATE_FORMAT = YYYYMONDD;
會話已更改。
DINGJUN123>SELECT TO_DATE(SYSDATE,YYYYMONDD) FROM DUAL;
TO_DATE(SYSDAT
--------------
20216月 11
DINGJUN123>ALTER SESSION SET NLS_DATE_LANGUAGE = AMERICAN;
會話已更改。
DINGJUN123>SELECT TO_DATE(SYSDATE,YYYYMONDD) FROM DUAL;
TO_DATE(SYSD
------------
2021JUN11
DINGJUN123>DROP TABLE t;
表已刪除。
DINGJUN123>CREATE TABLE t(name VARCHAR2(10));
表已創建。
DINGJUN123>INSERT INTO t VALUES(1);
已創建 1 行。
DINGJUN123>INSERT INTO t VALUES(abc);
已創建 1 行。
DINGJUN123>COMMIT;
提交完成。
DINGJUN123>CREATE INDEX idx_t ON t (name);
索引已創建。
DINGJUN123>SELECT * FROM t
2 WHERE name = 1;
ERROR:
ORA-01722: 無效數字
未選定行
DINGJUN123>SELECT * FROM t
2 WHERE name = 1;
NAME
------
1
DINGJUN123>EXPLAIN PLAN FOR
2 SELECT * FROM t
3 WHERE name = 1;
已解釋。
DINGJUN123>SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------
Plan hash value: 1601196873
----------------------------------
| Id | Operation | Name |
----------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | TABLE ACCESS FULL| T |
----------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(TO_NUMBER("NAME")=1)
Note
-----
- rule based optimizer used (consider using cbo)
DINGJUN123>EXPLAIN PLAN FOR
2 SELECT * FROM t
3 WHERE name = 1;
已解釋。
DINGJUN123>SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------
Plan hash value: 2296882198
----------------------------------
| Id | Operation | Name |
----------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | INDEX RANGE SCAN| IDX_T |
----------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("NAME"=1)
Note
-----
- rule based optimizer used (consider using cbo)
DINGJUN123>DROP TABLE t;
表已刪除。
DINGJUN123>CREATE TABLE t
2 AS
3 SELECT SYSDATE+LEVEL done_date
4 FROM DUAL
5 CONNECT BY LEVEL < 10;
表已創建。
DINGJUN123>CREATE INDEX idx_t ON t (done_date);
索引已創建。
DINGJUN123>SET AUTOTRACE ON EXPLAIN
DINGJUN123>ALTER SESSION SET NLS_DATE_FORMAT=YYYY-MM-DD;
會話已更改。
DINGJUN123>SELECT * FROM t
2 WHERE TO_CHAR(done_date,YYYYMMDD) = 20210612;
DONE_DATE
----------
2021-06-12
執行計劃
----------------------------------------------------------
Plan hash value: 1601196873
----------------------------------
| Id | Operation | Name |
----------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | TABLE ACCESS FULL| T |
----------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(TO_CHAR(INTERNAL_FUNCTION("DONE_DATE"),YYYYMMDD)=202106
12)
Note
-----
- rule based optimizer used (consider using cbo)
DINGJUN123>SELECT * FROM t
2 WHERE done_date >= TO_DATE(20210612,YYYYMMDD)
3 AND done_date < TO_DATE(20210613,YYYYMMDD);
DONE_DATE
--------------
2021-06-12
執行計劃
----------------------------------------------------------
Plan hash value: 2296882198
----------------------------------
| Id | Operation | Name |
----------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | INDEX RANGE SCAN| IDX_T |
----------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("DONE_DATE">=TO_DATE(2021-06-12 00:00:00, yyyy-mm-dd
hh24:mi:ss) AND "DONE_DATE"2021-06-13 00:00:00, yyyy-mm-dd
hh24:mi:ss))
Note
-----
- rule based optimizer used (consider using cbo)
OK,索引生效,符合預期,其實很少遇到不能對右值進行改寫或不能改寫SQL條件而必須要對左值進行轉換的情況,如果真遇到這種情況,可以考慮函數索引。
DINGJUN123>SELECT 5*10+ab FROM DUAL;
SELECT 5*10+ab FROM DUAL
*
第 1 行出現錯誤:
ORA-01722: 無效數字
DINGJUN123>SELECT 5*10+11 FROM DUAL;
5*10+11
------------
61
自動類型轉換矩陣圖
DINGJUN123>SELECT parameter,value
2 FROM NLS_SESSION_PARAMETERS
3 WHERE parameter in (NLS_DATE_FORMAT,NLS_DATE_LANGUAGE) ;
PARAMETER VALUE
---------------------------------------- ---------------------------------
NLS_DATE_FORMAT YYYY-MM-DD
NLS_DATE_LANGUAGE SIMPLIFIED CHINESE
DINGJUN123>DROP TABLE t;
表已刪除。
DINGJUN123>CREATE TABLE t
2 (x VARCHAR2(100));
表已創建。
DINGJUN123>INSERT INTO t VALUES(SYSDATE);
已創建 1 行。
DINGJUN123>SELECT x FROM t;
X
---------------------
2021-06-11
DINGJUN123>SET SERVEROUTPUT ON
DINGJUN123>DECLARE
2 var CHAR(10);
3 BEGIN
4 SELECT 1
5 INTO var
6 FROM DUAL;
7 DBMS_OUTPUT.PUT_LINE(var is || var || ,the length is || LENGTH(var));
8 END;
9 /
var is 1 ,the length is 10
PL/SQL 過程已成功完成。
DINGJUN123>DROP TABLE t;
表已刪除。
DINGJUN123>CREATE TABLE t AS
2 SELECT CAST(3 AS NUMBER(2)) AS id FROM DUAL;
表已創建。
DINGJUN123>SELECT id/8 FROM t;
ID/8
-----------------
.375
DINGJUN123>DROP TABLE t;
表已刪除。
DINGJUN123>CREATE TABLE t(x VARCHAR2(100));
表已創建。
DINGJUN123>SELECT * FROM t WHERE x = 1;
未選定行
DINGJUN123>SET AUTOTRACE ON EXPLAIN
DINGJUN123>SELECT * FROM t WHERE x = 1;
未選定行
執行計劃
----------------------------------------------------------
Plan hash value: 1601196873
----------------------------------
| Id | Operation | Name |
----------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | TABLE ACCESS FULL| T |
----------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(TO_NUMBER("X")=1)
Note
-----
- rule based optimizer used (consider using cbo)
DINGJUN123>DROP TABLE t;
表已刪除。
DINGJUN123>CREATE TABLE t(x BINARY_FLOAT);
表已創建。
DINGJUN123>INSERT INTO t VALUES(1234567);
已創建 1 行。
DINGJUN123>INSERT INTO t VALUES(123456789);
已創建 1 行。
DINGJUN123>COLUMN x FORMAT 999999999
DINGJUN123>SELECT * FROM t;
X
----------
1234567
123456792
DINGJUN123>DROP TABLE t;
表已刪除。
DINGJUN123>CREATE TABLE t
2 ( x VARCHAR2(10));
表已創建。
DINGJUN123>INSERT INTO t VALUES(TO_CLOB(121212121212));
INSERT INTO t VALUES(TO_CLOB(121212121212))
*
第 1 行出現錯誤:
ORA-12899: 列 "DINGJUN123"."T"."X" 的值太大 (實際值: 12, 最大值: 10)
DINGJUN123>DROP TABLE t;
表已刪除。
DINGJUN123>CREATE TABLE t(x BINARY_DOUBLE);
表已創建。
DINGJUN123>DROP TABLE t1;
表已刪除。
DINGJUN123>CREATE TABLE t1(x BINARY_FLOAT);
表已創建。
DINGJUN123>INSERT INTO t1 VALUES(3.42E+37F);
已創建 1 行。
DINGJUN123>INSERT INTO t
2 SELECT x FROM t1;
已創建 1 行。
DINGJUN123>SELECT x FROM t;
X
----------
3.42E+037
已選擇 1 行。
DINGJUN123>SELECT x FROM t1;
X
----------
3.42E+037
已選擇 1 行。
DINGJUN123>DROP TABLE t;
表已刪除。
DINGJUN123>CREATE TABLE t(x BINARY_DOUBLE);
表已創建。
DINGJUN123>INSERT INTO t VALUES(1.79769313486E+39);
已創建 1 行。
DINGJUN123>DROP TABLE t1;
表已刪除。
DINGJUN123>CREATE TABLE t1(x BINARY_FLOAT);
表已創建。
DINGJUN123>INSERT INTO t1
2 SELECT x FROM t;
已創建 1 行。
DINGJUN123>SELECT * FROM t;
X
----------
1.798E+039
已選擇 1 行。
DINGJUN123>SELECT x FROM t1;
X
----------
Inf
已選擇 1 行。
DINGJUN123>DROP TABLE t;
表已刪除。
DINGJUN123>CREATE TABLE t
2 AS SELECT TO_DATE(2021-06-12,YYYY-MM-DD) x
3 FROM DUAL;
表已創建。
DINGJUN123>SELECT * FROM t WHERE x = 2021-06-12;
SELECT * FROM t WHERE x = 2021-06-12
*
第 1 行出現錯誤:
ORA-01861: 文字與格式字符串不匹配
DINGJUN123>ALTER SESSION SET NLS_DATE_FORMAT=YYYY-MM-DD;
會話已更改。
DINGJUN123>SELECT * FROM t WHERE x = 2021-06-12;
X
----------
2021-06-12
DINGJUN123>SELECT REPLACE(12345,4) x FROM DUAL;
X
--------
1235
DINGJUN123>SELECT 10 + 0 x FROM DUAL;
X
----------------
10
DINGJUN123>SELECT 10 || 0 x FROM DUAL;
X
------
100
DINGJUN123>SELECT ROWID FROM t;
ROWID
------------------
AAAPCiAAEAAAVfUAAA
DINGJUN123>SELECT * FROM t
2 WHERE ROWID = AAAPCiAAEAAAVfUAAA;
X
----------------
2021-06-12
字符類型內部轉換表
DINGJUN123>SET SERVEROUTPUT ON
DINGJUN123>DECLARE
2 a CHAR(4):=ab ;
3 b VARCHAR2(4):=ab;
4 BEGIN
5 IF a = b THEN
6 DBMS_OUTPUT.PUT_LINE(a = b);
7 ELSE
8 DBMS_OUTPUT.PUT_LINE(a <> b);
9 END IF;
10 END;
11 /
a <> b
PL/SQL 過程已成功完成。
--返回4000,LPAD如果第1個參數是字符類型,最大只能是4000個字節
DINGJUN123>SELECT LENGTH(TO_CLOB(LPAD(a,6000,a)))
2 FROM DUAL;
LENGTH(TO_CLOB(LPAD(A,6000,A)))
-----------------------------------
4000
--返回6000,LPAD如果第1個參數是CLOB,那么最大可以達到CLOB最大長度
DINGJUN123>SELECT LENGTH(TO_CLOB(LPAD(TO_CLOB(a),6000,a))) len
2 FROM DUAL;
LEN
----------
6000
--返回6000,SUBSTR也可以接受CLOB列,則返回CLOB
DINGJUN123>SELECT LENGTH(SUBSTR(TO_CLOB(LPAD(TO_CLOB(a),6000,a)),1,6000)) len
2 FROM DUAL;
LEN
----------
6000
--報錯,INITCAP不接受CLOB列,自動類型轉換只允許最多4000個字節
DINGJUN123>SELECT LENGTH(INITCAP(TO_CLOB(LPAD(TO_CLOB(a),6000,a)))) len
2 FROM DUAL;
SELECT LENGTH(INITCAP(TO_CLOB(LPAD(TO_CLOB(a),6000,a)))) len
*
第 1 行出現錯誤:
ORA-22835: 緩沖區對于 CLOB 到 CHAR 轉換或 BLOB 到 RAW 轉換而言太小 (實際: 6000, 最大: 4000)
--正確,取最大4000字節
DINGJUN123>SELECT LENGTH(INITCAP(TO_CLOB(LPAD(TO_CLOB(a),4000,a))))
2 FROM DUAL;
LEN
----------
4000
DINGJUN123>SET SERVEROUTPUT ON
--在PL/SQL中LPAD(a,6000,a)是6000字節,但是在SQL中只能取到4000字節
--在PLSQL中LPAD(a,6000,a) || a是正確的,但是在SQL中就超出了4000字節的范圍,運算出錯
--在PL/SQL中,超出定義的最大字節數32767也出錯
DINGJUN123>DECLARE
2 v_str VARCHAR2(32767);
3 BEGIN
4 v_str := LPAD(a,6000,a);
5 DBMS_OUTPUT.PUT_LINE(LENGTH(v_str));
6 v_str := v_str ||a;
7 DBMS_OUTPUT.PUT_LINE(LENGTH(v_str));
8 v_str := LPAD(a,32768,a);
9 END;
10 /
6000
6001
DECLARE
*
第 1 行出現錯誤:
ORA-06502: PL/SQL: 數字或值錯誤 : 字符串緩沖區太小
ORA-06512: 在 line 8
DINGJUN123>SELECT LENGTH(LPAD(a,6000,a)) FROM DUAL;
LENGTH(LPAD(A,6000,A))
--------------------------
4000
已選擇 1 行。
DINGJUN123>SELECT LENGTH(LPAD(a,6000,a) || a) FROM DUAL;
SELECT LENGTH(LPAD(a,6000,a) || a) FROM DUAL
*
第 1 行出現錯誤:
ORA-01489: 字符串連接的結果過長
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/129289.html
摘要:最近解決了一個生產慢查詢的問題,排查問題之后發現一些比較隱匿且容易忽略的問題。所以實際在數據庫查詢如下可能這里發生一次隱式轉換。這次查詢走的是索引。 showImg(https://segmentfault.com/img/bVbmJNK?w=6000&h=4000); Photo by Iga Palacz on Unsplash 最近解決了一個生產 SQL 慢查詢的問題,排查問題之...
閱讀 1346·2023-01-11 13:20
閱讀 1684·2023-01-11 13:20
閱讀 1132·2023-01-11 13:20
閱讀 1858·2023-01-11 13:20
閱讀 4100·2023-01-11 13:20
閱讀 2704·2023-01-11 13:20
閱讀 1385·2023-01-11 13:20
閱讀 3597·2023-01-11 13:20