V$SESSION SQL_ID 为空,找不到SQL_ID

本文介绍了一种在Oracle数据库中定位导致锁等待的SQL语句的方法。通过查询gv$session和gv$sql视图,并结合PREV_EXEC_START和LAST_ACTIVE_TIME字段,可以找出具体的阻塞SQL及其上下文信息。

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

遇到过这个问题,网上搜到这篇文章,实际遇到的时候可以测试下效果

首先我们来做个实验:

SQL> select sid from v$mystat where rownum=1;
 
       SID
----------
      1150
 


SQL> update test set owner='BIGSB' where object_id<100;
 
98 rows updated

在1150这个SESSION里面执行一个UPDATE,不要提交。

SQL> select sid from v$mystat where rownum=1;
 
       SID
----------
      1338


 
SQL>  update test set owner='SB' where object_id<10;

在1338里面跑另外一个UPDATE,因为1150没提交,1138处于行锁等待。

这个时候通过如下脚本去查询数据库:


SQL> select inst_id,
  2         sid,
  3         sql_id,
  4         event,
  5         blocking_session,
  6         blocking_instance
  7    from gv$session a
  8   where blocking_session is not null;
 
   INST_ID        SID SQL_ID        EVENT                                   BLOCKING_SESSION BLOCKING_INSTANCE
---------- ---------- ------------- --------------------------------------- ---------------- ----------------
         1       1338 852mvmth18w37 enq: TX - row lock contention           1150                 1
         
SQL> select sql_id from gv$session where inst_id=1 and sid=1150;
 
SQL_ID
-------------

确实,SQL_ID是空的,也许有人会说,那我去查询PREV_SQL_ID,恩你去试一试吧,那个SQL_ID是事物的SQL_ID,并不是UPDATE的SQL_ID

SQL>  select prev_sql_id from gv$session where inst_id=1 and sid=1150;
 
PREV_SQL_ID
-------------
9m7787camwh4m
 
SQL> select sql_text from gv$sql where sql_id='9m7787camwh4m';
 
SQL_TEXT
--------------------------------------------------------------------------------
begin :id := sys.dbms_transaction.local_transaction_id; end;

所以很多人这个时候就蛋疼了,不知道咋办。现在教大家另外一种方法

SQL> select PREV_EXEC_START,USERNAME,MODULE,ACTION FROM GV$SESSION WHERE INST_ID=1 AND SID=1150;
 
PREV_EXEC_START     USERNAME    MODULE               ACTION
---------------     ------------------------------ -----------------------
2015-04-10 18:01:44 SCOTT       PL/SQL Developer    Command Window - New
 
SQL> SELECT SQL_ID,SQL_TEXT,LAST_ACTIVE_TIME,MODULE,ACTION FROM GV$SQL WHERE INST_ID=1 AND LAST_ACTIVE_TIME=TO_DATE('2015-04-10 18:01:44','YYYY-MM-DD HH24:MI:SS');
 
SQL_ID        SQL_TEXT                                                                         LAST_ACTIVE_TIME MODULE              ACTION
------------- -------------------------------------------------------------------------------- ---------------- ---------------------------------------------------------------- ----------------------------------------------------------------
2syvqzbxp4k9z select u.name, o.name, a.interface_version#, o.obj#      from association$ a, us 2015/4/10 18:01:                                                                  
6c9wx6z8w9qpu select a.default_selectivity                             from association$ a     2015/4/10 18:01:                    
2xyb5d6xg9srh select a.default_cpu_cost, a.default_io_cost             from association$ a     2015/4/10 18:01:                                                                  
d1s917pgj7650  update test set owner='BIGSB' where object_id<100                               2015/4/10 18:01: PL/SQL Developer    Command Window - New
 

现在就可以把SQL 抓到了

请注意:

1.在高并发的情况下,可能会出现多个可疑SQL

2.UPDATE执行过后,又继续执行新的SQL,就悲催了,这个时候要自己把所有SQL抓出来,按照时间线排序,CHECK

反正,提供了一种思路,具体的时候请自己判断,脑袋不要太笨。

select a.inst_id, a.sid, a.sql_id, b.sql_id, b.sql_text
  from gv$session a, gv$sql b
 where a.inst_id = b.inst_id
   and a.PREV_EXEC_START = b.LAST_ACTIVE_TIME
   and a.USERNAME = b.PARSING_SCHEMA_NAME
   and a.MODULE = b.MODULE
   --and a.ACTION_HASH = b.ACTION_HASH;

select a.inst_id,
       a.sid,
       a.event,
       a.sql_id,
       b.sql_text          running_sql,
       c.sql_in_session,
       c.sql_id_in_v$sql,
       c.sql_text          blocking_sql,
       a.blocking_session,
       a.blocking_instance
  from gv$session a,
       (select sql_id, sql_text
          from (select sql_id,
                       sql_text,
                       row_number() over(partition by sql_id order by sql_id) as rn
                  from gv$sql)
         where rn = 1) b,
       (select a.inst_id,
               a.sid,
               a.sql_id   sql_in_session,
               b.sql_id   sql_id_in_v$sql,
               b.sql_text
          from gv$session a, gv$sql b
         where a.inst_id = b.inst_id
           and a.PREV_EXEC_START =b.LAST_ACTIVE_TIME
           and a.USERNAME = b.PARSING_SCHEMA_NAME
           and a.MODULE = b.MODULE
        ) c
 where a.sql_id = b.sql_id
   and a.blocking_session is not null
   and a.BLOCKING_SESSION = c.sid
   and a.BLOCKING_INSTANCE = c.inst_id;

--------------------- 

原文:https://blog.youkuaiyun.com/robinson1988/article/details/44982861

<think>我们有两个视图:v$session 和 v$active_session_history。用户想要在查询这些视图时同时显示SQL的文本内容。 在Oracle中,SQL文本存储在v$sql或v$sqlarea等视图中。我们可以通过SQL_ID(在v$session和v$active_session_history中都有)来关联这些视图,从而获取SQL文本。 注意:v$active_session_history中存储的是历史活动会话信息,而v$session是当前会话信息。 对于v$session,我们可以这样写: SELECT s.sid, s.serial#, s.username, s.machine, s.program, s.module, s.client_info, q.sql_text FROM v$session s LEFT JOIN v$sql q ON s.sql_id = q.sql_id WHERE s.status = 'ACTIVE'; 但是注意,一个SQL_ID可能对应多个子游标(child cursor),但通常我们取其中一个即可(比如通过sql_id和child_number),或者使用v$sqlarea(它按sql_id聚合,但只显示一个子游标的sql_text)。不过,v$sqlarea中sql_text可能是截断的(只显示前1000字符),而v$sql中sql_fulltext是完整的(CLOB类型,但显示时可能受限于工具)。 另外,v$active_session_history中,每个活动会话每秒采样一次,可能同一个会话在多次采样中执行不同的SQL,所以我们需要根据SQL_ID来关联。 对于v$active_session_history的查询: SELECT ash.sample_id, ash.sample_time, ash.session_id, ash.session_serial#, ash.sql_id, ash.sql_exec_id, ash.event, ash.wait_class, q.sql_text FROM v$active_session_history ash LEFT JOIN v$sql q ON ash.sql_id = q.sql_id WHERE ash.sample_time BETWEEN ... AND ...; 但是,v$active_session_history中可能包含历史数据,而v$sql中可能已经没有对应的SQL(因为SQL游标被刷出共享池)。为了获取更久远的SQL文本,我们可以关联dba_hist_sqltext(需要AWR许可,并且已经做过快照)。 因此,如果我们想要查询v$active_session_history并且显示SQL文本,有两种情况: 1. 如果SQL还在共享池中(v$sql中还存在),则从v$sql中获取。 2. 如果SQL已经不在共享池中,则从dba_hist_sqltext中获取(前提是AWR快照已经捕获)。 但是,由于用户没有特别说明历史数据,我们先提供当前共享池中能关联到的SQL文本。 另外,注意v$sql中的sql_text是前1000个字符,而sql_fulltext是完整的SQL文本(CLOB)。我们可以使用DBMS_LOB.SUBSTR来截取,或者根据工具支持直接显示。 以下提供两个查询示例: 1. 查询v$session并显示SQL文本(当前正在执行的SQL): SELECT s.sid, s.serial#, s.username, s.machine, s.program, s.module, s.client_info, s.sql_id, q.sql_text, -- 前1000个字符 DBMS_LOB.SUBSTR(q.sql_fulltext, 4000, 1) AS sql_fulltext -- 显示最多4000字符 FROM v$session s LEFT JOIN v$sql q ON s.sql_id = q.sql_id WHERE s.status = 'ACTIVE'; 2. 查询v$active_session_history并显示SQL文本(注意:v$active_session_history中的SQL_ID可能为null,表示当时没有执行SQL): SELECT ash.sample_id, ash.sample_time, ash.session_id, ash.session_serial#, ash.sql_id, ash.sql_exec_id, ash.event, ash.wait_class, q.sql_text, DBMS_LOB.SUBSTR(q.sql_fulltext, 4000, 1) AS sql_fulltext FROM v$active_session_history ash LEFT JOIN v$sql q ON ash.sql_id = q.sql_id WHERE ash.sample_time > SYSDATE - INTERVAL '1' HOUR -- 最近一小时 ORDER BY ash.sample_time DESC; 但是,如果v$sql中已经没有了,我们可以尝试关联dba_hist_sqltext(需要AWR许可): SELECT ash.sample_id, ash.sample_time, ash.session_id, ash.session_serial#, ash.sql_id, ash.sql_exec_id, ash.event, ash.wait_class, COALESCE(q.sql_text, h.sql_text) AS sql_text FROM v$active_session_history ash LEFT JOIN v$sql q ON ash.sql_id = q.sql_id LEFT JOIN dba_hist_sqltext h ON ash.sql_id = h.sql_id WHERE ash.sample_time > SYSDATE - INTERVAL '1' HOUR ORDER BY ash.sample_time DESC; 注意:dba_hist_sqltext中的sql_text是完整的SQL文本(VARCHAR2类型,最多4000字符),如果SQL文本超过4000字符,则会被截断。而v$sql中的sql_fulltext是CLOB,可以显示更长的文本(但我们在查询时通过DBMS_LOB.SUBSTR转换为4000字符)。 另外,如果用户没有AWR许可,则不能使用dba_hist_sqltext。 因此,我们给出一个通用的,只使用v$sql的查询。 最后,注意权限:用户需要被授予SELECT_CATALOG_ROLE,或者直接授予对v$session, v$sql, v$active_session_history, dba_hist_sqltext的查询权限。 由于用户的问题中提到了“现有的Oracle性能视图查询”,我们假设用户已经有查询这些视图的权限。 现在,根据用户提供的引用,我们知道v$active_session_history是内存中的,重启会消失,而dba_hist_active_sess_history是持久化的。但是,我们这里只需要关联SQL文本,所以用dba_hist_sqltext就可以。 总结:在查询v$session和v$active_session_history时,通过关联v$sql或dba_hist_sqltext来获取SQL文本。</think>### Oracle 查询添加 SQL 文本内容的方法 在 Oracle 中,可以通过关联 `v$sql` 或 `dba_hist_sqltext` 视图来获取 SQL 文本内容。以下是具体实现方法: #### 1. 查询 `v$active_session_history` + SQL 文本 ```sql SELECT ash.sample_id, ash.sample_time, ash.session_id, ash.sql_id, ash.event, ash.wait_class, ash.machine AS "客户端主机", ash.program AS "客户端程序", s.sql_text AS "SQL文本", DBMS_LOB.SUBSTR(s.sql_fulltext, 4000, 1) AS "完整SQL文本" FROM v$active_session_history ash LEFT JOIN v$sql s ON ash.sql_id = s.sql_id WHERE ash.sample_time > SYSDATE - INTERVAL '1' HOUR AND ash.session_state = 'ON CPU' -- 过滤活动会话 ORDER BY ash.sample_time DESC; ``` #### 2. 查询 `v$session` + SQL 文本(实时会话) ```sql SELECT s.sid, s.serial#, s.username, s.machine AS "客户端主机", s.program AS "客户端程序", s.module AS "应用模块", q.sql_text AS "当前SQL文本", DBMS_LOB.SUBSTR(q.sql_fulltext, 4000, 1) AS "完整SQL文本" FROM v$session s LEFT JOIN v$sql q ON s.sql_id = q.sql_id WHERE s.status = 'ACTIVE'; ``` #### 3. 历史 SQL 文本查询(需 AWR 许可) ```sql SELECT ash.sample_time, ash.session_id, ash.sql_id, ash.machine, t.sql_text AS "历史SQL文本" FROM dba_hist_active_sess_history ash JOIN dba_hist_sqltext t ON ash.sql_id = t.sql_id WHERE ash.sample_time BETWEEN TO_DATE('2023-01-01','YYYY-MM-DD') AND TO_DATE('2023-01-02','YYYY-MM-DD'); ``` ### 关键说明: 1. **SQL文本获取原理** - 通过 `sql_id` 关联 `v$sql` 或 `dba_hist_sqltext` 视图[^1][^3] - `v$sql.sql_text`:显示前 1000 字符 - `DBMS_LOB.SUBSTR(sql_fulltext)`:显示完整 SQL(CLOB 转 VARCHAR2) 2. **性能优化建议** ```sql -- 创建 SQL_ID 索引加速关联查询 CREATE INDEX idx_sql_id ON v$sql(sql_id); ``` 3. **权限要求**: ```sql GRANT SELECT ON v_$sql TO your_user; GRANT SELECT ON dba_hist_sqltext TO your_user; ``` ### 注意事项: 1. `v$active_session_history` 数据保留约 1 小时(内存中),重启后丢失[^1][^2] 2. `dba_hist_sqltext` 需要 AWR 许可且已启用快照[^2][^3] 3. 超过 1000 字符的 SQL 需使用 `sql_fulltext` 字段 4. 闲会话(IDLE)不会出现在 ASH 采样中[^4][^5] > **示例输出**: > ![查询结果示例](https://example.com/oracle-ash-sqltext.png) > *显示客户端主机、程序和对应 SQL 文本的查询结果* ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值