ORA-00904
ORA-00904表示您在語句中使用了不匹配的欄位名或不正確的語法。通常,錯誤不僅發生在SELECT或INSERT中,還發生在UPDATE,DELETE,CREATE TABLE,ALTER TABLE和PL/SQL塊中。
大多數使用者不會相信錯誤,但事實上SQL解析器從未犯過任何錯誤。在這篇文章中,您可能會看到一些關於ORA-00904的錯誤模式,其中一個可能與您的情況相符。由於大多數使用者錯誤是由區分大小寫的問題引起的,因此我們必須首先解釋鬆散形式和精確形式之間的差異。
通常,Oracle會將大多數識別字排除在密碼之外,並將其視為不區分大小寫的識別字。但是在ORA-00904無效識別字的情況下應該注意一些特殊用法。
不僅ORA-00904,ORA-00903和ORA-00911也與物件識別碼的無效使用有關。更具體地說,ORA-00903警告用戶無效的表名,並引發ORA-00911來定位非法字元
鬆散形式與精確形式
根據Oracle資料庫物件名稱和限定詞,有兩種有效的命名形式來創建資料庫物件。一個是非引用識別字,另一個是帶引號的識別字。為了避免ORA-00904,您必須知道兩種命名形式之間的明顯差異。
非引用識別字(鬆散形式)
它們沒有標點符號或任何其他特殊處理。只要它們基本上是等效的字串,我們就可以在SQL語句中不區分大小地靈活使用它們。所以我通常在這篇文章中將其稱為鬆散形式。實際上,非引用識別字都被認為是上層識別字。這個概念將幫助您更好地瞭解ORA-00904。
帶引號的識別字(精確形式)
它們以雙引號("")開頭和結尾。您可以將幾乎所有字元放在雙引號中,包括空格和保留字。這種識別字應該與最初定義的完全一致。所以我通常在這篇文章中將其稱為精確形式。據我所知,這是ORA-00904的主要來源。
稍後,我們還將討論有關ORA-00904的更多問題,以區分引用的和未引用的識別字。
ORA-00904的錯誤模式
在大多數情況下,濫用欄位名是ORA-00904的主要來源,其餘的是語法錯誤。在這篇文章中,我們將在以下部分討論ORA-00904的幾種錯誤模式。
- SELECT或INSERT中的ORA-00904
- WHERE,ORDER BY或GROUP BY中的ORA-00904
- CREATE TABLE中的ORA-00904
- ALTER TABLE中的ORA-00904
- PL/SQL中的ORA-00904
- Toad Error ORA-00904: "REF": invalid identifier
A. SELECT或INSERT中的ORA-00904
SELECT或INSERT中列出的任何欄位都有機會生成此錯誤。
在本節中,ORA-00904會警告用戶出現問題,這可能是由以下原因之一引起的:
1. 由於不存在的欄位而導致的ORA-00904
通常,我們創建一個表而不使用雙引號:
SQL> create table all_names_1 (First_Name varchar2(25), Last_Name varchar2(25));
Table created.
然後我們插入一些資料。
SQL> insert into all_names_1 select distinct first_name, last_name from employees;
107 rows created.
要避免使用ORA-00904,您應該在沒有任何引號的情況下查詢此表。也就是說,低位或大寫的欄位名都是有效且可接受的。這是因為SQL解析器會將所有識別字視為大寫識別字。
SQL> select first_name, last_name from all_names_1 where first_name = 'Scott';
FIRST_NAME LAST_NAME
------------------------- -------------------------
Scott Rowe
使用這種通常創建的表格可以保證它不再具有ORA-00904了嗎?讓我們看看兩種常見類型的無效識別字。
第一種情況是選擇表中不存在的欄位。
SQL> select first_name, last_name, num from all_names_1 where first_name = 'Scott';
select first_name, last_name, num from all_names_1 where first_name = 'Scott'
*
ERROR at line 1:
ORA-00904: "NUM": invalid identifier
SQL> insert into all_names_1 (first_name, last_name, num) values ('Scott', 'Rowe', 100);
insert into all_names_1 (first_name, last_name, num) values ('Scott', 'Rowe', 100)
*
ERROR at line 1:
ORA-00904: "NUM": invalid identifier
我們知道,欄位NUM是無效欄位,因為它不存在於表中。我們最好通過描述表來檢查定義。
SQL> desc all_names_1;
Name Null? Type
----------------------------------------- -------- ----------------------------
FIRST_NAME VARCHAR2(25)
LAST_NAME VARCHAR2(25)
如果您對表中缺少該欄位感到驚訝,則有可能已被其他人刪除。
2. 由於不存在的函數而導致的ORA-00904
這種模式很少見,我們應該討論不存在的函式呼叫。讓我們看看我們如何複製ORA-00904。
SQL> select first_name, month(hire_date) hire_month from employees where last_name = 'Rowe';
select first_name, month(hire_date) hire_month from employees where last_name = 'Rowe'
*
ERROR at line 1:
ORA-00904: "MONTH": invalid identifier
SQL解析器首先嘗試匹配名為MONTH的函數,但沒有找到任何內容,因此它轉向匹配欄位,也沒有匹配,SQL解析器別無選擇,只能拋出ORA-00904。這讓我想起MySQL確實有MONTH功能,但Oracle沒有。不存在功能的原因相同:ORA-00904:"DATEDIFF":識別字無效。
ORA-00904的解決方案是使用正確Oracle的函數名為EXTRACT來獲取欄位的月份值。
SQL> select first_name, extract(month from hire_date) hire_month from employees where last_name = 'Rowe';
FIRST_NAME HIRE_MONTH
-------------------- ----------
Scott 9
函數 DATEDIFF在Oracle中不起作用
請注意,您在MySQL中使用的某些日期時間函數,如DATEDIFF,YEAR,MONTH,NOW,SUBSTRING,在Oracle中不是有效的函數或識別字。您可以查看Oracle SQL語言參考:函數,以防止ORA-00904。
3. 由於拼寫錯誤的欄位而導致的ORA-00904
第三種情況是選擇拼寫錯誤的欄位名稱,這是ORA-00904最常見的模式。
SQL> select first name, last_name from all_names_1 where first_name = 'Scott';
select first name, last_name from all_names_1 where first_name = 'Scott'
*
ERROR at line 1:
ORA-00904: "FIRST": invalid identifier
在SQL語句中,我故意通過錯過FIRST和NAME之間的底線使欄位名無效。因此,SQL解析器將語句翻譯為:
從表ALL_NAMES_1中查詢名為FIRST的欄位並顯示為NAME,和欄位LAST_NAME以及其他條件。當然,沒有名為FIRST的有效欄位,甚至沒有First Name。有效且真實的識別字是FIRST_NAME。我們應該再次檢查拼寫,然後更正識別字。
為避免欄位名稱出現拼寫錯誤,您可以使用SQL Developer,Toad for Oracle或PL/SQL Developer等GUI工具來方便您自動填充欄位名。有關SQL Developer的示例: 正如您所看到的,我們提供了一些字母,SQL開發人員的編輯將負責其餘的工作。
4. 區分大小寫的欄位
在某些情況下,SQL解析器通過拋出ORA-00904來抱怨缺少的欄位,但表中存在欄位。怎麼會發生這種情況?您需要知道的是如何使用精確的表單來表達SQL語句中的欄位。
要使用確切形式的識別字,我們必須使用雙引號來包裝欄位名,這會通知資料庫創建我們提供的確切名稱。究竟怎麼樣?至少,我們應該將它們視為區分大小寫的識別字。
SQL> create table all_names_2 ("First_Name" varchar2(25), "Last_Name" varchar2(25));
Table created.
然後我們插入一些資料。
SQL> insert into all_names_2 select distinct first_name, last_name from employees;
107 rows created.
從現在開始,我們再也不能像過去那樣鬆散地使用表格了。否則,我們很有可能在語句中使用無效識別字,然後看到ORA-00904。
如果不在欄位上添加雙引號,我們就得到了ORA-00904。
SQL> select First_Name, Last_Name from all_names_2 where First_Name = 'Scott';
select First_Name, Last_Name from all_names_2 where First_Name = 'Scott'
*
ERROR at line 1:
ORA-00904: "FIRST_NAME": invalid identifier
SQL> insert into all_names_2 (First_Name, Last_Name) values ('Scott', 'Rowe');
insert into all_names_2 (First_Name, Last_Name) values ('Scott', 'Rowe')
*
ERROR at line 1:
ORA-00904: "LAST_NAME": invalid identifier
儘管欄位名與其定義完全相同,但由於欄位上缺少雙引號,我們仍然得到ORA-00904。這是因為未引用的識別字First_Name被鬆散地識別為FIRST_NAME,這是一個大寫的識別字。因此,SQL解析器無法在表中找到匹配的識別字。最終,它提出了ORA-00904以提醒這個錯誤的欄位。
解決方法
解決方法很簡單,我們應該按照我們在創建表格時提供的方式引用它們,而不是非引用形式。
SQL> select "First_Name", "Last_Name" from all_names_2 where "First_Name" = 'Scott';
First_Name Last_Name
------------------------- -------------------------
Scott Rowe
SQL> insert into all_names_2 ("First_Name", "Last_Name") values ('Scott', 'Rowe');
1 row created.
我們使用確切的表單來使語句有效。我們可以看到,使用精確的表單非常不方便,我們應該關注識別字的每一個細節,以防止它們被無效。
5. 欄位名稱中的空白
值得注意的是,確切的形式允許我們創建填充空白的欄位,這些欄位在沒有ORA-00904失效問題的情況下有效。讓我們先看一下普通表。
SQL> create table all_names_3 (First_Name varchar2(25), Last_Name varchar2(25), Num int, Create_Date date default sysdate);
Table created.
SQL> insert into all_names_3 (first_name, last_name, num) select first_name, last_name, count(*) from employees group by first_name, last_name;
107 rows created.
SQL> select first_name, last_name, num, create_date from all_names_3 where first_name = 'Scott';
FIRST_NAME LAST_NAME NUM CREATE_DA
------------------------- ------------------------- ---------- ---------
Scott Rowe 1 12-MAR-19
接下來,讓我們看一個奇怪但有效的案例,欄位名中有空格。
SQL> create table all_names_4 ("First_Name" varchar2(25), "Last_Name" varchar2(25), " " int, " " date default sysdate);
Table created.
SQL> insert into all_names_4 ("First_Name", "Last_Name", " ") select first_name, last_name, count(*) from employees group by first_name, last_name;
107 rows created.
SQL> select "First_Name", "Last_Name", " ", " " from all_names_4 where "First_Name" = 'Scott';
First_Name Last_Name
------------------------- ------------------------- ---------- ---------
Scott Rowe 1 12-MAR-19
我們可以看到,我為第三欄位使用了一個空格,第四欄位使用了兩個空格,只要您按照規則查詢表,它們就可以在沒有ORA-00904的情況下正常工作。
有任何名稱衝突或失效?沒有,這是因為一個空白與同一個表的命名空間中的兩個空格不同,特別是當我們使用精確形式來定義欄位時。
即使我們沒有得到任何ORA-00904和無效問題,因為我們仔細而恰當地使用了表格,但確切風格的命名確實令人困惑。嘗試描述奇怪但有效的表格:
SQL> desc all_names_4;
Name Null? Type
----------------------------------------- -------- ----------------------------
First_Name VARCHAR2(25)
Last_Name VARCHAR2(25)
NUMBER(38)
DATE
因此,它在第三和第四欄位上顯示的資訊很少。對於一個新成員,他完全不知道這裡發生了什麼。此外,一旦查詢這樣一個奇數表時拋出ORA-00904,沒有人能夠對其進行故障排除。
這提醒我,我曾嘗試在Unix作業系統中刪除空名或空白名稱的檔。我花了很長時間才解決它。
B. WHERE,ORDER BY或GROUP BY中的ORA-00904
我們上面討論的不匹配的欄位問題(如不存在的欄位,不存在的函數,拼寫錯誤的欄位和區分大小寫的欄位)也可能發生在WHERE,ORDER BY或GROUP BY子句中。
在本節中,我們將特別討論如何在WHERE或GROUP BY子句中使用欄位別名。
1. 由於濫用欄位別名而導致的ORA-00904
您可以在ORDER BYORDER BY子句中使用欄位別名,如下所示。
SQL> column did format 9999;
SQL> column eid format 9999;
SQL> select department_id as did, employee_id as eid from employees order by did;
DID EID
----- -----
10 200
20 201
...
但是,您既不能直接在WHERE子句中使用欄位別名:
SQL> select department_id as did, employee_id as eid from employees where did > 50;
select department_id as did, employee_id as eid from employees where did > 50
*
ERROR at line 1:
ORA-00904: "DID": invalid identifier
也不能在GROUP BY子句中使用:
SQL> select department_id as did, count(employee_id) as cnt from employees group by did;
select department_id as did, count(employee_id) as cnt from employees group by did
*
ERROR at line 1:
ORA-00904: "DID": invalid identifier
我們都知道欄位DID是什麼,但SQL解析器並不知道。它拋出ORA-00904來提醒問題。
解決方案
當然,您可以使用實際欄位名來隨時抑制ORA-00904。這種方法會更加穩定。
為了能夠在WHERE子句中使用欄位別名,您應該通過外部SELECT包裝查詢。
SQL> select * from (select department_id as did, employee_id as eid from employees) where did > 50;
DID EID
----- -----
90 100
90 101
...
對於想要使用欄位別名的GROUP BY子句,這有點棘手。
SQL> select did, count(eid) cnt from (select department_id as did, employee_id eid from employees) group by did;
DID CNT
----- ----------
100 6
30 6
...
也就是說,如果您堅持使用欄位別名,則外部SELECT是此錯誤模式中ORA-00904的解決方案,它可以將所有欄位別名視為實際欄位名。
在某些SELECT子條款中,欄位別名c_alias有更多微妙的用法,例如search_clause,cycle_clause和order_by_clause。.
C. CREATE TABLE中的ORA-00904
在CREATE TABLE語句中有幾種可能的ORA-00904模式。
1. 由於濫用保留字而導致的ORA-00904
像ORA-00904這樣的錯誤可能發生在各種物件創建中。讓我們看一些錯誤類型的識別字來闡明命名規則。
使用保留字是瘋狂的,涉及它們的一切可能最終變得不可預測和令人討厭。您永遠不應該使用它們來命名資料庫物件。
SQL> create table t1 (audit int);
create table t1 (audit int)
*
ERROR at line 1:
ORA-00904: : invalid identifier
我們再次看到了ORA-00904,但這次SQL解析器無法告訴我們欄位名,並將無效字串留空。
SQL> create table t1 ("audit" int);
Table created.
有可能,您的用戶可能會在完全無意識的情況下創建此類資料庫物件。這是因為他們的工具可以處理剩下的工作。
2. 由於錯誤地添加了額外的逗號而導致的ORA-00904
在語句中錯誤地添加了一個額外的逗號使得SQL解析器不知道該怎麼做,讓我們看看一些例子:
SQL> create table t1 (c1 number, c2 date,);
create table t1 (c1 number, c2 date,)
*
ERROR at line 1:
ORA-00904: : invalid identifier
SQL> create table t1 (c1 number,, c2 date);
create table t1 (c1 number,, c2 date)
*
ERROR at line 1:
ORA-00904: : invalid identifier
如您所見,欄位的列表中有一個額外的逗號。SQL解析器知道額外逗號後面並沒有任何內容或空,但它最終留下ORA-00904讓您去修復這些無效的使用。
正如我們所說的那樣,只要您使用確切的表單來創建表,使用純空格定義欄位是可接受且有意義的。但是,沒有任何內容或空則是另一個想法,這完全是無稽之談。難怪ORA-00904被拋出。
要解決此問題,我們應刪除額外的逗號以使該語句有效。
SQL> create table t1 (c1 number, c2 date);
Table created.
3. 由於以數位開始而導致的ORA-00904
一個以數字開頭的表名會怎麼樣?
SQL> create table t1 (12345678 int);
create table t1 (12345678 int)
*
ERROR at line 1:
ORA-00904: : invalid identifier
ORA-00904警告你,這在這裡是非法的。所以我在識別字之前添加了一個字母“c”來修復ORA-00904。
SQL> create table t1 (c12345678 int);
Table created.
對於正在使用的任何無效字元,會引發ORA-00911以提示此問題,如下所示。
SQL> create table $t1 (c12345678 int);
create table $t1 (c12345678 int)
*
ERROR at line 1:
ORA-00911: invalid character
實際上,美元符號“$”對於識別字是有效的,只是不要將它放在物件名稱的開頭。有關特殊字元限制的更多資訊,您應該選擇下一個規則。
儘管ORA-00911是對象命名的非法用法,但它並不像ORA-00904那樣明顯和具體。現在讓我們來看看如何在物件命名上使用特殊字元。
特殊字元
只允許$,_和#。對於欄位名,使用不可接受的特殊字元“?”引發ORA-00911無效字元。
SQL> create table t1 (c12345678?production int);
create table t1 (c12345678?production int)
*
ERROR at line 1:
ORA-00911: invalid character
取代ORA-00904,我們看到ORA-00911通知我們問號是無效字元。所以我將“?”改為“$”,“#”或“_”來糾正問題。
美元符號
SQL> create table t1 (c12345678$production int);
Table created.
數位標誌
SQL> create table t2 (c12345678#production int);
Table created.
底線
SQL> create table t3 (c12345678_production int);
Table created.
實際上,使用字串作為識別字是很正常的。而不是空格,經常使用底線並建議用於分隔字串中有意義的單詞。
D. ALTER TABLE中的ORA-00904
類似的問題不存在的欄位,不存在的功能,拼寫錯誤的欄位和區分大小寫欄位,在這裡也適用。因此,我寧願在本節討論一些罕見的情況。
在ALTER TABLE語句中有幾種ORA-00904模式。
1. ORA-00904在ALTER TABLE ADD 欄位中
ALTER DATABASE ADD欄位語句中的典型ORA-00904 如下所示:
SQL> alter table t1 add column c2 date;
alter table t1 add column c2 date
*
ERROR at line 1:
ORA-00904: : invalid identifier
ORA-00904專門定位在保留字COLUMN並告訴您它不是有效的識別字。實際上,這是一個語法錯誤。您不必在語句中添加保留字COLUMN。所以我們通過刪除關鍵字來糾正它。
SQL> alter table t1 add c2 date;
Table altered.
此外,在ADD之後,您不應該在該位置使用任何保留字。
2. ORA-00904在ALTER TABLE MODIFY 欄位中
讓我們在ALTER DATABASE MODIFY欄位語句中看一種ORA-00904的情況。
SQL> alter table all_names_1 modify (column fname varchar2(30));
alter table all_names_1 modify (column fname varchar2(30))
*
ERROR at line 1:
ORA-00904: : invalid identifier
同樣的原因,您不必在語句中添加保留字COLUMN。請刪除它。
刪除保留字後,我們仍然得到了ORA-00904,這是因為我故意使用了無效的欄位名。
SQL> alter table all_names_1 modify (fname varchar2(30));
alter table all_names_1 modify (fname varchar2(30))
*
ERROR at line 1:
ORA-00904: "FNAME": invalid identifier
表格中未找到欄位FNAME。有效的欄位名稱應為FIRST_NAME。
SQL> alter table all_names_1 modify (first_name varchar2(30));
Table altered.
3. ORA-00904在ALTER TABLE ADD CONSTRAINT NOT NULL中
SQL> create table t1 (c1 number);
Table created.
您可以在ALTER TABLE ADD CONSTRAINT語句中的欄位上添加UNIQUE約束,如下所示:
SQL> alter table t1 add constraint c1_unique UNIQUE (c1);
Table altered.
但是您不能以相同的方式在欄位上添加NOT NULL約束。
SQL> alter table t1 add constraint c1_nn NOT NULL (c1);
alter table t1 add constraint c1_nn NOT NULL (c1)
*
ERROR at line 1:
ORA-00904: : invalid identifier
如您所見,SQL解析器拋出ORA-00904以警告語句中使用的識別字名稱是非法的。實際上,這是一個語法錯誤。
為什麼?NOT NULL是一個約束,不是嗎?當然,NOT NULL是某種約束,但它更像是一個欄位屬性,我們以錯誤的方式使用它。我們先把它當作約束。
解決方案1:將其視為約束
基本上,Oracle中約束子句有4種子條款:
- inline_constraint
- out_of_line_constraint
- inline_ref_constraint
- out_of_line_ref_constraint
在上面生成ORA-00904的語句中,它是一個out_of_line_constraint子句,它不允許使用NOT NULL。相反,您必須將inline_constraint用於NOT NULL。這就是你在添加約束NOT NULL時看到ORA-00904的原因。
SQL> alter table t1 modify (c1 constraint c1_nn NOT NULL);
Table altered.
我們來檢查約束名稱。
SQL> select constraint_name from user_constraints where table_name = 'T1';
CONSTRAINT_NAME
------------------------------
C1_UNIQUE
C1_NN
SQL> alter table t1 drop constraint C1_NN;
Table altered.
與其他約束不同,NOT NULL不能是複合(多欄位)約束,它僅用於其欄位。
解決方案2:將其視為欄位屬性
嚴格地說,NOT NULL是一個欄位屬性,因此您不必將其視為約束,即便它可以是約束。
例如,我們可以將欄位修改為NOT NULL,如下所示:
SQL> alter table t1 modify (c1 NOT NULL);
Table altered.
我們可以看到,我將它視為欄位屬性,而不涉及任何約束子句。我們來檢查約束名稱。
SQL> select constraint_name from user_constraints where table_name = 'T1';
CONSTRAINT_NAME
------------------------------
C1_UNIQUE
SYS_C0011427
因此,資料庫為約束提供了系統生成的名稱。讓我們進一步看看我們如何放棄約束。
SQL> alter table t1 modify (c1 NULL);
Table altered.
SQL> select constraint_name from user_constraints where table_name = 'T1';
CONSTRAINT_NAME
------------------------------
C1_UNIQUE
也就是說,我們不必知道約束名稱就可以刪除它,只需恢復屬性即可。
E. PL/SQL中的ORA-00904
到目前為止,我們在PL/SQL中有兩個錯誤模式ORA-00904。
1. 由於PL/SQL中的變數聲明順序不正確而導致的ORA-00904
假設我們想使用一個涉及這樣的變數的游標:
SQL> declare
2 cursor c1 is select employee_id from hr.employees where department_id = v_num;
3 v_num number;
4 begin
5 v_num := 110;
6 open c1;
7 end;
8 /
cursor c1 is select employee_id from hr.employees where department_id = v_num;
*
ERROR at line 2:
ORA-06550: line 2, column 75:
PLS-00320: the declaration of the type of this expression is incomplete or
malformed
ORA-06550: line 2, column 75:
PL/SQL: ORA-00904: "V_NUM": invalid identifier
ORA-06550: line 2, column 16:
PL/SQL: SQL Statement ignored
我們看到了PL/SQL引擎拋出的ORA-00904。其中,ORA-06550表示第2列第75行的識別字在匿名PL/SQL塊中被非法使用。
這個錯誤非常明顯,我們在聲明它們之前不能使用變數。換句話說,我們必須先聲明變數。所以我交換第2和第3行:
SQL> declare
2 v_num number;
3 cursor c1 is select employee_id from hr.employees where department_id = v_num;
4 begin
5 v_num := 110;
6 open c1;
7 end;
8 /
PL/SQL procedure successfully completed.
2. 由於不帶引號的字串而導致的ORA-00904
在PL/SQL中使用EXECUTE IMMEDIATE時,您可能想知道如何在語句中引用字串。
SQL> begin
2 execute immediate 'select nvl(first_name, NO_VALUE) from employees';
3 end;
4 /
begin
*
ERROR at line 1:
ORA-00904: "NO_VALUE": invalid identifier
ORA-06512: at line 2
在上面的語句中,我們想用函數NVL用NO_VALUE字串替換所有NULL值,但我們猶豫是在語句中引用字串,該語句現在也是一個字串。結果,我們得到了ORA-00904。現在問題是,如何引用外部字串中的字串以使其正確?
解決方案
答案是,只需將每個單引號複製為兩個單引號,以便在字串中轉義單個引號,無論您希望在另一個字串中顯示字串,尤其是在EXECUTE IMMEDIATE中。
SQL> begin
2 execute immediate 'select nvl(first_name, ''NO_VALUE'') from employees';
3 end;
4 /
PL/SQL procedure successfully completed.
現在代碼塊很好。