oracle字段默认自增,Oracle列自增实现(3)-DEFAULT Values Using Sequences

Oracle 12c开始支持使用序列的NEXTVAL和CURRVAL作为默认值,实现列自增。创建序列,然后在建表时将序列的NEXTVAL设为默认值,即可在插入数据时不指定该列值时自动自增。同时,可通过DEFAULT ON NULL在列值为空时使用序列的NEXTVAL。此特性在主从表设计中也十分实用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Oracle 12c中,可以使用序列的NEXTVAL and CURRVAL的值作为默认值,来实现列自增!

一、使用序列的NEXTVAL and CURRVAL的值作为默认值

创建序列

CREATE SEQUENCE t1_seq;

建表

CREATE TABLEt1 (

idNUMBER DEFAULTt1_seq.NEXTVAL,

descriptionVARCHAR2(30)

);

插入数据

INSERT INTO t1 (description) VALUES ('DESCRIPTION only');INSERT INTO t1 (id, description) VALUES (999, 'ID=999 and DESCRIPTION');INSERT INTO t1 (id, description) VALUES (NULL, 'ID=NULL and DESCRIPTION');

查询结果

SELECT * FROM t1;

cfa84efd9cf488bd66ac01d9ba05ec49.png

二、默认值明确为非空

创建两个序列

CREATESEQUENCE default_seq;CREATE SEQUENCE default_on_null_seq;

建表,col1和col2分别使用上面两个序列的NEXTVAL作为默认值,其中col2 DEFAULT ON NULL

CREATE TABLEt2 (

col1NUMBER DEFAULTdefault_seq.NEXTVAL,

col2NUMBER DEFAULT ON NULLdefault_on_null_seq.NEXTVAL,

descriptionVARCHAR2(30)

);

插入数据

INSERT INTO t2 (description) VALUES ('DESCRIPTION only');INSERT INTO t2 (col1, col2, description) VALUES (999, 999, '999,999,DESCRIPTION');INSERT INTO t2 (col1, col2, description) VALUES (NULL, NULL, 'NULL,NULL,DESCRIPTION');

查询数据,可以看到col2位NULL时候,被默认转换使用了default_on_null_seq.NEXTVAL的

SELECT * FROM t2;

07d8471b3ec4a6e4bf06022b8f19c33c.png

三、例子:主从表的简单例子

CREATE SEQUENCE master_seq;

CREATE SEQUENCE detail_seq;

CREATE TABLE master (

id NUMBER DEFAULT master_seq.NEXTVAL,

description VARCHAR2(30)

);

CREATE TABLE detail (

id NUMBER DEFAULT detail_seq.NEXTVAL,

master_id NUMBER DEFAULT master_seq.CURRVAL,

description VARCHAR2(30)

);

INSERT INTO master (description) VALUES ('Master 1');

INSERT INTO detail (description) VALUES ('Detail 1');

INSERT INTO detail (description) VALUES ('Detail 2');

INSERT INTO master (description) VALUES ('Master 2');

INSERT INTO detail (description) VALUES ('Detail 3');

INSERT INTO detail (description) VALUES ('Detail 4');

SELECT * FROM master;

81a2fad5b8bf1c67822ee5344d8634bb.png

SELECT * FROM detail;

58e426b95e71cf0656dd3727d9748a40.png

原文:

<think>我们正在处理一个Oracle数据库错误:ORA-01790,该错误表明在SQL语句中,表达式必须具有与对应表达式相同的数据类型。错误发生在使用MERGE语句的批量操作中,其中使用了UNION ALL来组合多个数据源。 根据错误信息,我们可以定位到问题通常发生在以下情况: 1. 在UNION或UNION ALL子句中,每个SELECT语句的对应的数据类型不一致。 2. 在MERGE语句的USING子句中,通过UNION ALL组合的多个SELECT语句的对应的数据类型不匹配。 观察提供的SQL语句,我们发现USING子句中有两个SELECT语句通过UNION ALL连接。每个SELECT语句都选择了相同的,并且我们注意到在第一个SELECT语句中,使用了绑定变量(:1, :2等),而第二个SELECT语句使用了不同的绑定变量(:9, :10等)。这些绑定变量在运行时被赋予具体的值。 可能的问题点: 1. 两个SELECT语句中,相同位置的的数据类型不一致。例如,第一个SELECT语句中的:1(PROJECT_ID)可能是字符串类型,而第二个SELECT语句中的:9(PROJECT_ID)可能是数字类型,但表A01_PROJECT_MEMBER中的PROJECT_ID是字符串类型(反之亦然)。 2. 在MERGE语句的ON条件中,比较的两个字段的数据类型不一致。但是,错误信息指向的是USING子句中的表达式,因此更可能是UNION ALL中的数据类型不匹配。 另外,我们注意到在USING子句的中,有一个固定值0(DEL_FLG)和多个绑定变量。绑定变量的数据类型是由传入的Java对象决定的。如果传入的两个绑定变量在相同的位置上类型不一致,就会导致这个问题。 具体分析: 在UNION ALL中,要求每个SELECT语句的对应必须具有相同的数据类型。例如: - 第一个SELECT的:1(PROJECT_ID)和第二个SELECT的:9(PROJECT_ID)必须同类型。 - 第一个SELECT的:2(EMPLOYEE_ID)和第二个SELECT的:10(EMPLOYEE_ID)必须同类型。 - 以此类推。 如果其中任何一对的数据类型不一致,就会引发ORA-01790错误。 解决方案: 1. 检查传入的每个绑定变量的数据类型,确保在UNION ALL中对应的具有相同的数据类型。 2. 如果无法保证传入的数据类型一致,可以在SELECT语句中使用CAST或TO_CHAR、TO_NUMBER等函数进行显式转换,使两个SELECT语句的对应具有相同的数据类型。 例如,假设PROJECT_ID在表中是VARCHAR2类型,但传入的某个绑定变量是数字类型,则应该将其转换为字符串: SELECT TO_CHAR(:1) AS PROJECT_ID, ... FROM dual UNION ALL SELECT TO_CHAR(:9) AS PROJECT_ID, ... FROM dual 同理,对于其他,也要确保类型一致。 另外,注意在MERGE语句的INSERT部分,我们插入的值中有一个是CURRENT_TIMESTAMP(时间戳类型),而对应的表START_DATE可能是DATE类型。但是,在Oracle中,TIMESTAMP可以隐式转换为DATE,所以这可能不是问题。但是,如果表是字符串类型,则会出现问题。因此,需要确认表结构。 建议步骤: 1. 确认表A01_PROJECT_MEMBER各的数据类型。 2. 检查传入的每个参数的数据类型,确保在UNION ALL中对应的类型一致。 3. 在SQL语句中,对绑定变量使用显式类型转换,以统一类型。 错误日志中提到了错误位置(Position: 213),但具体位置可能因SQL字符串的格式而异。不过,根据错误信息,我们重点关注UNION ALL的两个SELECT语句。 修改后的USING子句示例(假设需要转换的类型): ```sql USING ( SELECT TO_CHAR(:1) AS PROJECT_ID, -- 假设PROJECT_ID是字符串类型 TO_NUMBER(:2) AS EMPLOYEE_ID, -- 假设EMPLOYEE_ID是数字类型 :3 AS EMPLOYEE_NAME, :4 AS ROLE, TO_DATE(:5, &#39;YYYY-MM-DD&#39;) AS START_DATE, -- 如果传入的是字符串,需要转换为日期 TO_DATE(:6, &#39;YYYY-MM-DD&#39;) AS END_DATE, 0 AS DEL_FLG, :7 AS INSERT_USER_ID, :8 AS UPDATE_USER_ID FROM dual UNION ALL SELECT TO_CHAR(:9) AS PROJECT_ID, TO_NUMBER(:10) AS EMPLOYEE_ID, :11 AS EMPLOYEE_NAME, :12 AS ROLE, TO_DATE(:13, &#39;YYYY-MM-DD&#39;) AS START_DATE, TO_DATE(:14, &#39;YYYY-MM-DD&#39;) AS END_DATE, 0 AS DEL_FLG, :15 AS INSERT_USER_ID, :16 AS UPDATE_USER_ID FROM dual ) s ``` 注意:转换函数的具体使用取决于传入参数的类型和表的类型。如果传入的参数已经是正确的类型,则不需要转换。主要解决的是UNION ALL中两个SELECT对应类型不一致的问题。 另外,在MERGE语句的UPDATE部分,我们设置t.END_DATE = CURRENT_TIMESTAMP,而原表中END_DATE可能是DATE类型,CURRENT_TIMESTAMP返回的是TIMESTAMP WITH TIME ZONE类型,这通常可以隐式转换,但为了安全,也可以使用CAST或TRUNC将其转换为DATE类型,或者使用SYSDATE(返回DATE类型)代替。 但根据错误信息,当前错误是ORA-01790,所以主要问题还是在USING子句的UNION ALL中。 最后,请检查传入参数的数量和顺序是否正确,确保没有遗漏或错位。 如果问题仍然存在,请单独运行这个MERGE语句(将绑定变量替换为具体值),在SQL工具中执行,以进一步验证。 总结:ORA-01790错误通常是由于UNION ALL中两个SELECT语句的对应数据类型不一致引起的。通过显式转换确保每个对应的类型一致即可解决。</think>### 原因分析:ORA-00936 "缺失表达式" 错误 根据错误信息和引用内容,根本原因是您的代码在数据插入/更新后执行了获取最后插入ID的操作,但Oracle使用方式不正确导致语法错误: ```sql:no-line-numbers SELECT {$sequence}.currval as id FROM dual ``` #### 具体错误点: 1. **序引用错误**(核心问题) 当`$sequence`值为空或无效时,SQL会变成: ```sql SELECT .currval as id FROM dual -- 缺少序名导致语法错误 ``` 这违反了Oracle语法规则,触发ORA-00936[^2][^3] 2. **序使用条件不符** `currval`只能在`nextval`之后调用: ```sql -- 错误顺序 SELECT seq.currval FROM dual; -- 未先调用nextval -- 正确顺序 SELECT seq.nextval FROM dual; -- 必须先执行nextval SELECT seq.currval FROM dual; -- 然后才能调用currval ``` 3. **TP6框架兼容性问题** 框架的`getLastInsID()`方法原生为MySQL设计,直接套用到Oracle会导致语法错误[^3]: ```php:no-line-numbers // 问题代码(TP6框架) public function getLastInsID(BaseQuery $query, string $sequence = null) { $pdo = $this->linkID->query("select {$sequence}.currval as id from dual"); return $pdo->fetchColumn(); } ``` --- ### 解决方案 #### 1. 检查序有效性(立即修复) 确保表有对应的序且名称正确: ```sql -- 验证序存在性 SELECT sequence_name FROM user_sequences WHERE sequence_name = &#39;YOUR_SEQ_NAME&#39;; ``` #### 2. 修改TP6框架方法(推荐) 重写`getLastInsID`方法兼容Oracle: ```php public function getLastInsID(BaseQuery $query, string $sequence = null) { if (empty($sequence)) { throw new \Exception(&#39;Oracle requires sequence name&#39;); } // 先调用nextval激活序 $this->linkID->exec("SELECT {$sequence}.nextval FROM dual"); // 再获取当前值 $pdo = $this->linkID->query("SELECT {$sequence}.currval as id FROM dual"); return $pdo->fetchColumn(); } ``` #### 3. 插入时显式使用序(替代方案) 直接在INSERT语句中使用序: ```sql INSERT INTO your_table (id, ...) VALUES (your_seq.NEXTVAL, ...) -- 无需额外查询currval ``` #### 4. 使用RETURNING子句(最佳实践) 通过单次查询获取插入ID: ```sql DECLARE new_id NUMBER; BEGIN INSERT INTO your_table (id, ...) VALUES (your_seq.NEXTVAL, ...) RETURNING id INTO new_id; END; ``` --- ### 预防措施 1. **框架配置** 在database.php中显式指定序名: ```php &#39;connections&#39; => [ &#39;oracle&#39; => [ &#39;autoinc_seq&#39; => &#39;YOUR_SEQ_NAME&#39; // TP6专用配置 ] ] ``` 2. **NULL值处理** 避免序名为空: ```php // 添加默认名 $sequence = $sequence ?? &#39;DEFAULT_SEQ&#39;; ``` 3. **错误日志监控** 捕获数据库异常: ```php try { $model->save(); } catch (\PDOException $e) { Log::error(&#39;Oracle Error: &#39;.$e->getMessage()); } ``` > **关键提示**:Oracle与MySQL的ID获取机制完全不同,MySQL的`LAST_INSERT_ID()`在Oracle中必须通过序实现[^1][^3] --- ### 相关问题 1. 如何在TP6中为Oracle表自动创建序? 2. 如何批量插入时获取所有生成的ID? 3. Oracle缓存设置对性能有什么影响? 4. 除了序Oracle还有哪些自ID实现方式? 5. 如何在不同数据库间保持ID生成策略的兼容性? [引用来源] [^1]: ORA-00936错误常见于缺失关键表达式或保留字误用场景 [^2]: TP6框架直接移植MySQL逻辑导致Oracle调用错误 [^3]: PDO_OCI扩展在执行`getLastInsID()`时触发缺失表达式错误
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值