简介:PLSQL Developer 是一款面向 Oracle 数据库管理员与开发者的集成开发环境,适合从入门到进阶的数据库开发、管理与性能调优场景。它支持本地及远程连接 Oracle 数据库,兼容 SQL*Net 与 Oracle Wallet 等身份验证方式,可完成表、视图、存储过程、函数、触发器、索引等对象的创建与维护,并提供语法高亮、代码提示、自动完成与代码片段功能,帮助高效编写 PL/SQL 脚本。在数据操作上,它支持复杂 SQL 查询、结果集导出为 CSV 或 Excel、数据导入导出与备份迁移;调试方面可设置断点、单步执行、检查变量并进行单元测试,还集成 SVN、Git 版本控制与工作区管理,配合 SQL 分析器和 Explain Plan 辅助优化查询性能。资源以 rar 压缩包形式提供,大小约 19.05MB,文件总数与类型明细上游暂未提供。目前已有 191 人学习关注,适合需要一款中文界面、功能全面的 Oracle 开发工具的用户参考使用。
1. PLSQL Developer:为什么老手还在用它调存储过程
如果你维护过一套跑了七八年的 Oracle 业务系统,大概率会遇到这种场面:一个两千行的存储过程,里面嵌套了三层游标,还夹着动态 SQL 拼字符串。用通用数据库工具打开,要么卡死,要么断点根本停不下来。这时候很多老手会默默打开 PLSQL Developer,连上库,找到那个过程,按 F9 开始单步调试。它不是一个新工具,界面甚至有点朴素,但在 Oracle 存储过程开发与调试这个细分场景里,它依然是很多人绕不开的选择。
这篇笔记面向三类人:刚接触 Oracle 存储过程、需要一套能落地的调试环境的新手;从别的数据库转过来、想知道 PLSQL Developer 到底解决什么问题的开发者;以及已经在用、但只用到连接查询、没碰过调试器和会话监控的老用户。我会按「它是什么、怎么装怎么连、怎么写怎么调、坑在哪、怎么用得更深」的顺序讲,每一步都落到可复现的操作和参数上。核心不是吹工具,而是让你在遇到一个报错的存储过程时,知道从哪里下手。
2. 装好并连上:从零到能跑第一条查询
2.1 客户端与服务端的边界先分清
PLSQL Developer 是运行在 Windows 上的 Oracle 客户端工具,它本身不包含数据库,也不自带 Oracle 的网络通信能力。它要连上数据库,依赖的是 Oracle 提供的客户端连接组件。常见做法是安装 Oracle Instant Client,或者装一个完整版 Oracle Client,然后把 PLSQL Developer 的偏好设置指向这个客户端目录。
这里有个新手最容易翻车的点:机器上装了多个 Oracle 客户端,或者之前装过别的数据库工具带了自己的客户端,导致 PLSQL Developer 加载了错误的组件版本,报出「Initialization error」「Could not load OCI DLL」这类错误。解决思路是只保留一个客户端目录,并在工具里显式指定。
安装顺序我一般这样走:
- 下载 Oracle Instant Client 的 Basic 包和 SQL*Plus 包,解压到同一个目录,比如
C:\oracle\instantclient_21。 - 在该目录下新建
network\admin子目录,放入tnsnames.ora。 - 安装 PLSQL Developer,首次启动时在登录窗口点「Cancel」进入主界面,再到 Preferences 里设置 Oracle Home 和 OCI library。
tnsnames.ora是最关键的配置文件,它决定了你能用别名连哪些库。一个典型条目长这样:
# tnsnames.ora ORCL_DEV = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 10.0.0.21)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = orclpdb) ) )逻辑说明:ORCL_DEV是你自己起的连接别名,登录时直接选它就行。HOST和PORT指向数据库监听地址,SERVICE_NAME是数据库服务名,不是 SID。很多连接失败是因为把SERVICE_NAME和SID搞混了,两者在容器数据库里通常不一样。
参数说明:SERVER = DEDICATED表示专用服务器模式,开发环境一般用这个;如果是共享服务器模式才写SHARED。PORT默认 1521,改了监听端口就要同步改这里。
2.2 登录方式与首选项的四个必调项
PLSQL Developer 的登录窗口有四个字段:用户名、密码、数据库、连接为。数据库下拉框里会列出tnsnames.ora里的所有别名。连接为一般选Normal,除非你要用SYSDBA身份做管理操作。
登录进去后,先别急着写 SQL,把 Preferences 里这几项调好,能省掉后面大量麻烦:
| 配置项 | 路径 | 建议值 | 作用 |
|---|---|---|---|
| Oracle Home | Preferences - Connection | 指向 Instant Client 目录 | 决定加载哪个客户端 |
| OCI library | Preferences - Connection | 同目录下的 oci.dll | 避免多版本冲突 |
| 自动提交 | Preferences - SQL Window | 关闭 | 防止误操作直接落库 |
| 查询结果集上限 | Preferences - SQL Window | 500 到 1000 | 防止大表查询把内存吃满 |
自动提交这一项特别值得说。默认开启时,你在 SQL 窗口敲一条UPDATE,回车就提交了,没有后悔药。开发环境我建议关掉,手动按 F10 提交,改错了还能回滚。
2.3 第一条查询与执行计划入口
连接成功后,新建一个 SQL Window,输入:
-- 查询当前用户下的表数量与最近分析时间 SELECT COUNT(*) AS table_count, MAX(last_analyzed) AS last_analyzed FROM user_tables;按 F8 执行。结果区会显示一行数据。如果这里报ORA-00942: table or view does not exist,说明当前登录用户没有这张视图的权限,或者你连错了库。
想看执行计划,选中 SQL 后按 F5,工具会调用EXPLAIN PLAN并把计划树展示出来。这里有个细节:F5 看到的是估算计划,不是真实运行统计。要看真实行数和耗时,得用「自动跟踪」或者查V$SQL_PLAN_STATISTICS_ALL。新手常把估算计划当成实际执行,看到TABLE ACCESS FULL就以为一定慢,其实小表全扫完全没问题。
3. 写存储过程与调试:断点、单步、看变量
3.1 用模板建一个可调试的过程
在 Object Browser 里找到 Procedures 节点,右键新建,工具会给你一个骨架。我把它改成一个带输入输出参数的简单过程,方便后面演示调试:
CREATE OR REPLACE PROCEDURE proc_calc_bonus ( p_emp_id IN NUMBER, p_bonus OUT NUMBER ) IS v_salary NUMBER; v_years NUMBER; BEGIN -- 取员工薪资和入职年限 SELECT salary, TRUNC(MONTHS_BETWEEN(SYSDATE, hire_date) / 12) INTO v_salary, v_years FROM employees WHERE employee_id = p_emp_id; -- 年限越长系数越高 IF v_years >= 5 THEN p_bonus := v_salary * 0.20; ELSIF v_years >= 2 THEN p_bonus := v_salary * 0.10; ELSE p_bonus := v_salary * 0.05; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN p_bonus := 0; END proc_calc_bonus;逻辑说明:这个过程先按员工号查薪资和年限,再按年限分档算奖金,最后用异常处理兜住查不到员工的情况。SELECT ... INTO在 PL/SQL 里必须保证只返回一行,否则会抛TOO_MANY_ROWS。
参数说明:p_emp_id是输入,p_bonus是输出。输出参数在调试时可以在变量窗口里直接看到值。MONTHS_BETWEEN返回的是带小数的月数,除以 12 再取整得到年限。
3.2 断点怎么下、单步怎么看变量
编译通过后,在 Object Browser 里右键这个过程,选「Test」。工具会打开一个测试窗口,上半部分是参数输入,下半部分是调试控制。在BEGIN之后的那行SELECT左侧灰色边栏点一下,会出现一个红点,这就是断点。
按 F9 开始调试,程序会停在断点处。此时可以:
- 按 F7 单步进入(Step Into),遇到子程序会跟进去。
- 按 F8 单步跳过(Step Over),把当前行整体执行完。
- 在下方变量窗口观察
V_SALARY、V_YEARS的实时值。 - 把鼠标悬停在代码里的变量上,也能弹出当前值。
这里有个血泪经验:如果断点打在了没有可执行语句的行上,比如IS或BEGIN那一行,调试器会直接跳过,看起来像断点没生效。断点必须打在真正的可执行语句上。另外,如果过程被别的会话锁住,或者编译状态是INVALID,F9 会报错,先在 Object Browser 里确认状态是VALID。
3.3 调试权限与编译选项
调试不是默认就能用的。当前登录用户需要有DEBUG CONNECT SESSION和DEBUG ANY PROCEDURE权限,否则 F9 会提示没有调试权限。授权语句:
-- 由 DBA 执行,授予开发用户调试权限 GRANT DEBUG CONNECT SESSION TO dev_user; GRANT DEBUG ANY PROCEDURE TO dev_user;逻辑说明:第一条允许该用户建立调试会话,第二条允许调试任意存储过程。生产环境一般不给普通开发这两个权限,只在开发库或测试库开。
参数说明:如果只想让用户调试自己的过程,可以只授DEBUG CONNECT SESSION,再配合对象级权限。编译时还要注意,过程必须以DEBUG模式编译,工具在测试窗口会自动处理,但如果你手动用脚本编译,要加PLSQL_OPTIMIZE_LEVEL相关设置,否则断点信息可能不完整。
4. 避坑与排查:连接、编译、调试的常见翻车
4.1 连接报 ORA-12154 或 ORA-12514
现象:登录时提示ORA-12154: TNS:could not resolve the connect identifier specified,或者ORA-12514: TNS:listener does not currently know of service requested。
原因:前者通常是tnsnames.ora路径不对,或者别名拼写和文件里不一致;后者是监听器不认识你写的服务名,多半是SERVICE_NAME写成了SID,或者数据库服务没注册到监听。
解决:先确认 PLSQL Developer 的 Preferences 里 Oracle Home 指向的目录下确实有network\admin\tnsnames.ora。再用命令行tnsping 别名测试解析。如果tnsping通但登录报 12514,就去数据库服务器上用lsnrctl status看服务名列表,把tnsnames.ora里的SERVICE_NAME改成实际注册的名字。
4.2 编译报 PLS-00103 却找不到语法错
现象:过程编译报PLS-00103: Encountered the symbol ...,但你反复看那一行语法没问题。
原因:PL/SQL 编译器报错位置经常滞后。真正的问题可能在上一行少了分号,或者上一句的括号没闭合。还有一种情况是用了保留字做变量名,比如把变量叫DATE或NUMBER。
解决:从报错行往上找三到五行,重点看分号和括号。变量命名加前缀,比如v_date、v_number,避开保留字。如果还找不到,把过程拆成几段分别编译,缩小范围。
4.3 调试时断点变灰或直接跑完
现象:断点明明打了,F9 一按,程序瞬间跑完,根本没停。
原因:常见有三种。一是过程编译时没带调试信息,比如用了PLSQL_OPTIMIZE_LEVEL较高的优化级别;二是断点打在了注释行或空行;三是当前连接的用户没有调试权限,工具静默降级为普通执行。
解决:在测试窗口重新编译一次,让工具以调试模式生成代码。确认断点落在可执行语句上。用SELECT * FROM user_sys_privs WHERE privilege LIKE '%DEBUG%'检查权限。三者逐一排除,基本能定位。
4.4 查询大表把客户端卡死
现象:在 SQL Window 里SELECT * FROM一张千万级大表,回车后工具长时间无响应,甚至内存溢出。
原因:默认配置下工具会把结果集全部取回本地再显示,数据量大时内存扛不住。
解决:在 Preferences 的 SQL Window 里把「查询结果集上限」设成 500 到 1000,并勾选「只取前 N 行」。写查询时养成加WHERE ROWNUM <= 100的习惯先看数据结构,确认无误再去掉限制。需要导出全量数据时,用工具的导出功能而不是直接查。
4.5 中文乱码
现象:查询结果里的中文显示成问号或方块。
原因:客户端字符集和数据库字符集不一致。常见于数据库是AL32UTF8,而客户端环境变量NLS_LANG没设或设成了ZHS16GBK。
解决:在 Windows 环境变量里设置NLS_LANG = SIMPLIFIED CHINESE_CHINA.AL32UTF8,重启 PLSQL Developer。如果数据库是ZHS16GBK,就设成SIMPLIFIED CHINESE_CHINA.ZHS16GBK。关键是客户端这一侧要和数据库字符集对齐,不是随便选一个中文就行。
5. 会话监控与批量对象操作:把工具用出效率
5.1 用会话窗口定位锁等待
PLSQL Developer 的 Session 窗口能看到当前数据库的所有会话,包括状态、执行的 SQL、等待事件。遇到「某个更新一直不返回」的情况,我一般这样查:
-- 查找被阻塞的会话及其阻塞源 SELECT s.sid, s.serial#, s.blocking_session, s.event, s.seconds_in_wait, q.sql_text FROM v$session s LEFT JOIN v$sql q ON s.sql_id = q.sql_id WHERE s.blocking_session IS NOT NULL OR s.event LIKE 'enq%';逻辑说明:blocking_session不为空,说明这个会话正被另一个会话阻塞。event以enq开头通常是锁等待。seconds_in_wait越大,等得越久。
参数说明:查到阻塞源后,可以用ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;终止阻塞会话,但这属于急救手段,生产环境要谨慎,先确认对方在做什么。更稳妥的做法是联系持有锁的会话,让其提交或回滚。
5.2 批量编译失效对象
数据库做过结构变更后,一批存储过程可能变成INVALID状态。一个个手动编译太慢,可以用工具自带的功能,也可以跑脚本:
-- 生成批量编译语句 SELECT 'ALTER ' || object_type || ' ' || owner || '.' || object_name || ' COMPILE;' AS compile_stmt FROM dba_objects WHERE status = 'INVALID' AND object_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE', 'TRIGGER', 'VIEW') AND owner = 'DEV_USER';逻辑说明:这条查询把需要编译的对象拼成ALTER ... COMPILE语句。把结果复制出来,在命令窗口整体执行即可。
参数说明:owner换成你的目标用户。object_type列表按需增减。编译视图用ALTER VIEW ... COMPILE,编译包时包规范和包体要分别处理,工具一般会自动带出依赖顺序。
5.3 用模板和代码片段减少重复输入
工具支持自定义代码模板。我习惯把常用的异常处理块、游标循环块、日志记录块存成模板,写新过程时直接插入。路径在 Preferences 的「User Interface - Code Templates」里。比如一个标准的游标循环模板:
-- 模板:游标循环 FOR rec IN (SELECT /* 列 */ FROM /* 表 */ WHERE /* 条件 */) LOOP -- 处理逻辑 NULL; END LOOP;逻辑说明:FOR rec IN (...)是隐式游标,不用手动打开关闭,适合大多数遍历场景。模板里留空的地方用注释标出,插入后按需替换。
参数说明:隐式游标在循环内不能做COMMIT,否则会报fetch out of sequence。需要分批提交时,改用显式游标加BULK COLLECT和LIMIT。
5.4 导出与比对:结构变更的验证手段
改完一批对象后,怎么确认改动符合预期?我一般用工具的对象导出功能,把改动前后的定义各导一份,再用文本比对工具看差异。导出时选「用户对象」或「单个对象」,格式选 SQL 文件。比对重点看参数列表、返回类型、异常分支有没有被误删。
这一步在多人协作时尤其有用。别人改了过程,你拉下来一比对,就知道动了哪里,不用逐行读。养成改前导出、改后导出的习惯,相当于给自己留了一份后悔药。
6. 把调试器用成日常习惯:一个具体技巧
最后说一个我用了很多年的小技巧:给关键过程加一个「调试开关」参数,让同一份代码在调试和正式运行时走不同分支。这样你不需要为了调试专门改代码,也不会因为调试语句留在生产版本里而提心吊胆。
CREATE OR REPLACE PROCEDURE proc_settle ( p_order_id IN NUMBER, p_debug IN VARCHAR2 DEFAULT 'N' ) IS v_amount NUMBER; BEGIN SELECT amount INTO v_amount FROM orders WHERE order_id = p_order_id; -- 调试模式下把中间值打到输出 IF p_debug = 'Y' THEN DBMS_OUTPUT.PUT_LINE('order_id=' || p_order_id || ', amount=' || v_amount); END IF; -- 正式结算逻辑 UPDATE orders SET status = 'SETTLED', settle_time = SYSDATE WHERE order_id = p_order_id; IF p_debug = 'Y' THEN DBMS_OUTPUT.PUT_LINE('rows updated=' || SQL%ROWCOUNT); END IF; END proc_settle;逻辑说明:p_debug默认N,正式调用时不传这个参数,过程走正常逻辑。调试时在测试窗口把p_debug设成Y,配合DBMS_OUTPUT就能看到关键中间值。SQL%ROWCOUNT返回上一条 DML 影响的行数,用来确认更新是否命中。
参数说明:DEFAULT 'N'让这个参数可选,不破坏已有调用。DBMS_OUTPUT.PUT_LINE需要在工具里开启输出开关,在测试窗口或命令窗口按 Ctrl+E 启用。注意DBMS_OUTPUT有缓冲区大小限制,输出太多会报ORA-20000,调试完记得把p_debug传回N。
这个习惯的好处是,调试信息跟着代码走,不用每次临时加DBMS_OUTPUT再删掉。我踩过的坑是早期把调试输出直接写在正式逻辑里,上线后忘了删,结果日志表被撑爆。后来改成开关控制,再没出过这类问题。写存储过程这件事,工具只是放大器,真正省时间的是把调试路径提前设计进代码结构里。希望帮到你。
本文还有配套的精品资源,点击获取