select ' 字段内容' 字段名 , t.*, t.rowid from table_name t

本文详细介绍了Oracle数据库中ROWID的概念及其应用。ROWID是Oracle用于唯一标识表中每一行的特殊数据类型,由DATAOBJECTNUMBER、数据文件号、块号及行号四部分组成。文章还解释了如何通过ROWID快速定位表中的行,并展示了如何在SQL查询中正确使用ROWID。

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

在学习oracle 过程当中,当在看tables时,比如STUDENT,右击——查看——查询,会自动有这样的一条查询语句:

select t.*, t.rowid from STUDENT_TJB t

       其中,作为一个sql小白,我想问的就是rowid是什么用法,还有t.*定位的什么表还是用户呢?

       经过查询资料,获得了几个重要的知识点:

 一、 ROWID(行ID)

      ROWID也是一种数据类型,但是这种数据类型是Oracle 服务器使用并管理的。它具有以下几个特性:

  •  ROWID是数据库每一行的唯一标识符。
  •  ROWID作为列值是隐式存储的。
  •  ROWID不直接给出行的物理地址,但是可以用ROWID来定位行。
  •  ROWID提供了最快速地访问表中行的方法。

查询以下STUDENT表中的ROWID: select rowid from STUDENT; 结果如下:

 

      可以看出, Oracle的物理扩展ROWID有18位。

      ROWID的每位采用64位编码,分别用A~Z、a~z、0~9、+、/共64个字符表示。其中,A表示0,B表示1,……Z表示25,

a表示26,……z表示51,0表示52,……,9表示61,+表示62,/表示63。

       例如,

create table test_rowid (id number, row_id rowid); --1.创建一临时表 
insert into test_rowid values(1,null); --2.插入一行记录 
update test_rowid set row_id = rowid where id = 1; --修改刚插入的记录 
select rowid,row_id from test_rowid; --4.查看rowid 

     返回结果为:
  

                  rowid                                 row_id 
            AAAO0DAAJAAAAMYAAA                    AAAO0DAAJAAAAMYAAA

 

       ROWID具体划分可以分为4部分。

       (1).OOOOOO:前6位表示DATA OBJECT NUMBER,将起转化位数字后匹配DBA_OBJECTS中的DATA_OBJECT_ID,可以确定表信息。

        如上面的例子DATA OBJECT NUMBER是AAAO0D,转化位数字是14×64×64 +52×64 + 3。 
         输入以下查询: 
      

  select owner, object_name from dba_objects 
     where data_object_id = 14*64*64 + 52*64 + 3; 

         返回: 

?

1

2

OWNER    OBJECT_NAME

WG        TEST_ROWID


      (2).FFF:第7到9位表示相对表空间的数据文件号。 
         上面的例子中是AAJ,表示数据文件9。 
      (3).BBBBBB:第10到15位表示这条记录在数据文件中的第几个BLOCK中。

         上面的例子是AAAAMY,转化位数字是12×64+24,表示这条记录在数据文件中的第792个BLOCK。

      (4).RRR:最后3位表示这条记录是BLOCK中的第几条记录。

         上面的例子是AAA,表示第0条记录(总是从0开始计数)。

 二、select t.* ,rowid.t from 表 t

      1.

       select t.*   是选出t表的所有信息
       select *     是选出 关键字from后面的所有表的信息
       如果from 后面只有一个表,那么两者无区别。

      2.       

      当from后面只有一个表名时,select t.*,rowid 能用, select *,rowid为什么不能用?提示“未找到要求的FROM关键字”。

因为select *,rowid的时候,会把rowid当作表的一个字段来用,而在select t.*,rowid的时候没有作为所查表的字段。

    3. 

     比如select t.* from table t,那么t就是你查的那张表table的别名,后面写的时候,table可以用t代替,简单点,这里的t.*就是查询table表的所有列名,有多少,查出多少,*就是全部的意思、

--数据追溯查询 CREATE PROCEDURE [dbo].[proc_RecordFive] --查询条件 @productid varchar(50),--产品ID @mindate datetime,--最小时间 @maxdate datetime,--最大时间 @IsOk int, --是否合格 为空不查询 @startIndex int, --当前分页起始行 @EndIndex int, --当前分页结束行 --临时变量 @ProcessID varchar(50), @ItemSql nvarchar(max) AS BEGIN if @EndIndex>5000 RAISERROR('要查询的生产记录超出了5000条!',16,1) else begin --1.查找所有工序 对应字段 select c.F_Name as TTechniqueName,b.F_ID ProcessID,d.F_SortCode,b.F_Name ProcessName,a.F_Name AS ColText,f_ColName into #table1 from TProcessIOSet a right join TProcess b on a.F_VersionID=b.F_EffectiveVersionId right join TTechnique c on b.F_TechniqueID=c.f_id right join TTechniqueLine d on d.F_CurProcessID=b.F_id and d.F_TVersionID=c.F_EffectiveVersionId where c.F_ProductID=F_ProductID and a.F_Type=0 order by d.F_SortCode,a.f_ColName --2.查找所有生产记录 select * into #table2 from (select ROW_NUMBER() OVER (ORDER BY F_CreatorTime ASC) rowid, F_ProductCode,F_CreatorTime,lastIsok from( SELECT a.F_ProductCode,min(a.F_CreatorTime) F_CreatorTime,max(case d.F_NProcessID when '-1' then a.F_ISOK else 0 end) as lastIsok FROM TAllIDCode a left join TProcess b on a.F_ProcessID=b.F_Id left join TTechnique c on b.F_TechniqueID=c.f_id left join TTechniqueLine d on d.F_CurProcessID=b.F_id and d.F_TVersionID=c.F_EffectiveVersionId where c.F_ProductID=F_ProductID and a.F_CreatorTime BETWEEN @mindate AND @maxdate group by a.F_ProductCode ) _T where @IsOk is null or lastIsok=@IsOk) t where rowid BETWEEN @startIndex AND @EndIndex --3.打印所有列 select * from #table1 --4.打印所有生产 select * from #table2 --5.打印所有工序生产记录 declare Process_cur cursor for select ProcessID from #table1 group by ProcessID,F_SortCode order by F_SortCode open Process_cur fetch next from Process_cur into @ProcessID while (@@fetch_status=0) begin if exists (select * from sysobjects where id = object_id(N'TProductionDetail_'+replace(@ProcessID,'-','_')) and OBJECTPROPERTY(id, N'IsUserTable') = 1) begin set @ItemSql='select a.rowid,c.F_ISOK,c.F_ProductionTime,' select @ItemSql+='c.'+f_ColName+',' from #table1 where ProcessID=@ProcessID order by F_SortCode,f_ColName set @ItemSql +=' 0 as fakecol from #table2 a left join TAllIDCode b on b.F_ProductCode=a.F_ProductCode left join TProductionDetail_'+replace(@ProcessID,'-','_')+' c on c.f_id=b.F_KeyId where b.F_ProcessID='''+@ProcessID+''' ;' print @ItemSql exec sp_executesql @ItemSql end else select null fetch next from Process_cur into @ProcessID end close Process_cur deallocate Process_cur end end
03-17
<think>我们分析一下这个SQL语句。报错信息是"f.d4_enclose invalid identifier",这意味着在查询中引用的"f.d4_enclose"这个字段名无效。可能的原因有: 1. 表formtable_main_1233中没有名为d4_enclose的列。 2. 表别名f没有正确使用,或者表名错误导致无法识别。 3. 在子查询中,外部查询的别名f的作用域问题。 观察整个SQL结构,主查询是从formtable_main_1233表(别名为f)中选取一个子查询的结果。在子查询中,我们使用了f.d4_enclose,这个字段应该来自formtable_main_1233表。但是,在子查询内部,我们引用外部查询的字段时,需要确保外部查询的表别名在子查询中是可见的。然而,在嵌套子查询中,内层子查询可以访问外层查询的别名,所以这里的作用域应该是没问题的。 因此,最可能的原因是表formtable_main_1233中不存在名为d4_enclose的列,或者可能是拼写错误(大小写问题?)。在Oracle中,默认情况下,列名是不区分大小写的,除非创建时使用了双引号引起来并指定了大小写。如果创建表时列名使用了双引号(例如:"d4_enclose"),那么在查询时也必须使用双引号和相同的大小写。 但是,我们也可以看到,这个子查询的结构比较复杂,它使用了两个子查询嵌套。我们尝试简化并检查问题。 另外,注意在子查询中,我们使用了: ```sql select trim(REGEXP_SUBSTR(f.d4_enclose, '[^,]+', 1, LEVEL)) AS split_value from DUAL CONNECT BY LEVEL <= REGEXP_COUNT(f.d4_enclose, ',') + 1 ``` 这里,我们引用了两次f.d4_enclose,如果这个字段不存在,就会报错。 解决步骤: 1. 确认表formtable_main_1233是否存在d4_enclose列。可以通过查询数据字典视图,例如: ```sql SELECT column_name FROM all_tab_columns WHERE table_name = 'FORMTABLE_MAIN_1233' AND column_name = 'D4_ENCLOSE'; ``` 注意:表名和列名在数据字典中默认都是大写的,除非创建时用了双引号。 2. 如果确认列存在,那么检查SQL语句中是否有拼写错误。 3. 如果列名确实存在,那么可能是作用域问题?但在这个例子中,子查询是关联子查询,应该可以访问外部查询的列。 然而,我们注意到,在子查询中,我们引用了f.d4_enclose,而这个子查询是在主查询的SELECT子句中,它并不是一个关联子查询(即没有将外部查询的列作为条件传递给内层查询)。实际上,这个子查询是独立于外部查询的每一行的,但是这里却引用了外部查询的列,这会导致错误,因为子查询在解析时无法确定外部查询的列。 实际上,在Oracle中,SELECT列表中的标量子查询(即返回单个值的子查询)可以引用外部查询的列,所以这里的作用域是允许的。但是,在子查询中,我们使用了CONNECT BY层级查询,而CONNECT BY子句通常用于生成序列,这里用于拆分字符串。但是,在CONNECT BY子句中,我们引用了外部查询的列,这可能会引起问题吗? 实际上,在Oracle中,这样的写法是允许的,标量子查询可以引用外部查询的列。但是,如果表formtable_main_1233确实没有d4_enclose列,那么就会报错。 因此,我们首先需要确认表结构。 但是,如果无法直接查看表结构,我们可以尝试修改SQL,先去掉子查询,直接查询f.d4_enclose,看是否存在: ```sql select f.d4_enclose from formtable_main_1233 f; ``` 如果这个查询报同样的错误,那么就是列不存在或者拼写错误。 如果确认列存在,那么可能是子查询的层次问题。我们注意到,在子查询中,我们使用了三层嵌套: - 最内层:使用CONNECT BY拆分字符串,这里引用了f.d4_enclose。 - 中间层:通过子查询获取拆分后的每个值,然后去关联docdetail表。 - 最外层:使用listagg将多个docsubject拼接成一个字符串。 问题可能出在CONNECT BY子句不能直接引用外部查询的列?实际上,在Oracle中,CONNECT BY子句可以引用外部查询的列,因为它是标量子查询的一部分。 然而,我们注意到,在标量子查询中,我们有一个独立的CONNECT BY查询,它没有与外部表进行连接。但是,由于它引用了外部查询的列,所以实际上它对于外部查询的每一行都会执行一次。这种写法在Oracle中是允许的,但效率可能不高。 但是,如果列d4_enclose不存在,那么错误就会发生。 因此,我们重点检查列是否存在。如果列确实存在,那么可能是别名f的作用域问题。在标量子查询中,我们可以访问外部查询的别名,但要注意,如果有多层嵌套,内层子查询可能会覆盖外层别名。不过在这个例子中,只有一层外部别名f,所以应该可以访问。 另外,注意在子查询中,我们给拆分的结果命名为split_value,然后在中间层子查询中,我们使用sub.id和sub.docsubject,但是最内层查询返回的列是split_value(它被用作子查询i.id的条件)。这里,最内层查询返回的split_value应该是一个字符串,而docdetail表的id字段是什么类型?如果id是数值类型,那么我们需要将split_value转换为数值类型,否则可能会出现类型不匹配的错误。但这不是当前报错的原因,当前报错是列名无效。 所以,回到错误本身:invalid identifier。我们首先要确认列d4_enclose是否存在。 如果确认列存在,那么可能是列名的大小写问题。如果创建表时使用了双引号,例如: ```sql CREATE TABLE formtable_main_1233 (..., "d4_enclose", ...); ``` 那么查询时必须使用双引号和相同的大小写,即: ```sql select f."d4_enclose" ... ``` 但通常不建议这样创建表。 因此,修改后的SQL可能是: ```sql select (select listagg(sub.docsubject,',') within group (order by sub.id) from (select i.id,i.docsubject from docdetail i where i.id in ( select trim(REGEXP_SUBSTR(f."d4_enclose", '[^,]+', 1, LEVEL)) AS split_value from DUAL CONNECT BY LEVEL <= REGEXP_COUNT(f."d4_enclose", ',') + 1 ) ) sub ) as d4_enclose from formtable_main_1233 f ``` 但是,如果列名在数据库中是默认的大写(即D4_ENCLOSE),那么我们在查询时使用f.d4_enclose会被自动转换成大写,所以也是可以的。除非列名是混合大小写且用双引号创建。 因此,我们建议先检查列名是否存在,以及是否使用了双引号。 如果问题仍然存在,我们可以尝试简化查询,例如不使用标量子查询,而使用LEFT JOIN等。 另外,我们注意到,在子查询中,我们使用了: ```sql select i.id, i.docsubject from docdetail i where i.id in (...) ``` 这里,我们只选取了id和docsubject,但是外层listagg使用了sub.id和sub.docsubject,并且按sub.id排序。但是,我们并没有在子查询中获取id,而是直接使用了i.id。这里注意,中间层子查询(别名为sub)的列是i.id和i.docsubject,所以sub.id就是i.id。 但是,这里有一个潜在问题:如果d4_enclose列中存储的字符串包含非数字字符,那么拆分出来的split_value是字符串,而docdetail表的id是数值类型,那么就会因为类型不匹配而报错。所以,在拆分后,应该将split_value转换为数字,或者确保d4_enclose中都是数字。 不过,当前报错是列名无效,所以先解决列名问题。 如果经过检查,发现表formtable_main_1233中确实没有d4_enclose列,那么我们需要使用正确的列名。如果列名是别的,比如d4_enclose_list等,那么就需要替换。 如果问题出在作用域,我们可以尝试将最内层的CONNECT BY子句移动到FROM子句中,作为一个内联视图,并确保它能够引用外部查询的列。例如,改写为: ```sql select (select listagg(sub.docsubject,',') within group (order by sub.id) from (select i.id,i.docsubject from docdetail i where i.id in ( select split_value from ( select trim(REGEXP_SUBSTR(f.d4_enclose, '[^,]+', 1, LEVEL)) AS split_value from DUAL CONNECT BY LEVEL <= REGEXP_COUNT(f.d4_enclose, ',') + 1 ) )) sub ) as d4_enclose from formtable_main_1233 f ``` 这样改写并没有改变作用域,因为内联视图仍然可以引用外部查询的列(在Oracle中,内联视图可以引用外部查询的列,只要它在同一层FROM中)。但这里我们只是多加了一层子查询,实际上并没有改变引用方式。 所以,我们回到最初的问题:列名无效。请先确认列名是否存在。 如果用户无法确认,我们可以尝试使用一个替代方案:将主查询中的f表别名去掉,直接使用表名,但这样可能不会解决问题。 综上所述,请先检查表结构,确认d4_enclose列是否存在。 如果存在,那么可能是数据库版本或解析顺序的问题?我们可以尝试将标量子查询改写为LEFT JOIN形式,这样可能更清晰。 改写思路:将主表formtable_main_1233与拆分后的字符串表进行连接,然后再与docdetail表连接,最后聚合。 示例: ```sql SELECT LISTAGG(dd.docsubject, ',') WITHIN GROUP (ORDER BY dd.id) AS d4_enclose FROM formtable_main_1233 f LEFT JOIN ( SELECT TRIM(REGEXP_SUBSTR(f.d4_enclose, '[^,]+', 1, LEVEL)) AS split_value, f.id AS main_id -- 需要主表的主键来关联 FROM formtable_main_1233 f CROSS JOIN DUAL CONNECT BY LEVEL <= REGEXP_COUNT(f.d4_enclose, ',') + 1 ) split_tbl ON split_tbl.main_id = f.id LEFT JOIN docdetail dd ON dd.id = split_tbl.split_value GROUP BY f.id -- 假设需要按主表的主键分组 ``` 但是,这个写法中,在split_tbl子查询中,我们引用了f.d4_enclose,并且使用了CONNECT BY,但是这里f是子查询外部的表,这样引用是不允许的(因为子查询内部不能直接引用外部表的列,除非是相关子查询,而这里不是)。所以,我们需要使用相关子查询,但相关子查询在FROM子句中比较麻烦。 我们可以使用LATERAL关键字(Oracle 12c以上)来创建横向连接: ```sql SELECT LISTAGG(dd.docsubject, ',') WITHIN GROUP (ORDER BY dd.id) AS d4_enclose FROM formtable_main_1233 f LEFT JOIN LATERAL ( SELECT TRIM(REGEXP_SUBSTR(f.d4_enclose, '[^,]+', 1, LEVEL)) AS split_value FROM DUAL CONNECT BY LEVEL <= REGEXP_COUNT(f.d4_enclose, ',') + 1 ) split_tbl ON 1=1 LEFT JOIN docdetail dd ON dd.id = split_tbl.split_value GROUP BY f.id ``` 但是,Oracle中横向连接使用CROSS APPLY或OUTER APPLY,而LATERAL关键字通常与APPLY一起使用。在Oracle 12c之前,可以使用TABLE函数和管道函数,但比较复杂。 如果数据库版本低于12c,我们可以使用XMLTABLE或者使用PL/SQL函数来拆分字符串。 鉴于原始报错是列名无效,我们建议先确认列名是否存在。 如果列名确实存在,那么可能是原始SQL在某个环节被截断了或者有其他隐藏字符,可以尝试重新输入列名。 最后,如果以上都不能解决,我们可以尝试简化:将拆分字符串的部分独立出来,写成一个函数,然后在SQL中调用该函数。 例如,创建一个函数: ```sql CREATE OR REPLACE FUNCTION split_string(p_str IN VARCHAR2) RETURN SYS.ODCIVARCHAR2LIST IS v_tab SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST(); BEGIN SELECT TRIM(REGEXP_SUBSTR(p_str, '[^,]+', 1, LEVEL)) AS split_value BULK COLLECT INTO v_tab FROM DUAL CONNECT BY LEVEL <= REGEXP_COUNT(p_str, ',') + 1; RETURN v_tab; END; / ``` 然后在SQL中使用: ```sql select (select listagg(i.docsubject,',') within group (order by i.id) from docdetail i where i.id IN (SELECT * FROM TABLE(split_string(f.d4_enclose))) ) as d4_enclose from formtable_main_1233 f ``` 这样,我们避免了在子查询中直接使用CONNECT BY,而是通过函数来拆分字符串。同时,注意函数返回的是一个集合,我们可以用TABLE()函数将其转换成表。 但是,函数方式可能会影响性能。 综上所述,请先检查d4_enclose列是否存在。 如果问题仍然存在,请提供表结构以便进一步分析。</think>### 原因分析 报错 `f.d4_enclose invalid identifier` 的根本原因是:**子查询无法直接访问外部查询的别名**。在 Oracle 中,标量子查询(SELECT 列表中的子查询)是独立执行的,无法直接引用外部查询的 `f` 别名。这违反了 Oracle 的作用域规则。 ### 解决方案 需要将拆分逻辑移动到主查询的 `FROM` 子句中,使用 `CROSS JOIN` 或 `LATERAL` 实现关联查询。以下是两种优化方案: --- #### 方案 1:使用 `CROSS JOIN`(通用写法) ```sql SELECT LISTAGG(sub.docsubject, ',') WITHIN GROUP (ORDER BY sub.id) AS d4_enclose FROM formtable_main_1233 f CROSS JOIN ( SELECT TRIM(REGEXP_SUBSTR(f.d4_enclose, '[^,]+', 1, LEVEL)) AS split_value FROM DUAL CONNECT BY LEVEL <= REGEXP_COUNT(f.d4_enclose, ',') + 1 ) split_data JOIN docdetail sub ON sub.id = split_data.split_value GROUP BY f.rowid; -- 按主表行分组 ``` #### 方案 2:使用 `LATERAL`(Oracle 12c+ 推荐) ```sql SELECT LISTAGG(sub.docsubject, ',') WITHIN GROUP (ORDER BY sub.id) AS d4_enclose FROM formtable_main_1233 f CROSS JOIN LATERAL ( SELECT TRIM(REGEXP_SUBSTR(f.d4_enclose, '[^,]+', 1, LEVEL)) AS split_value FROM DUAL CONNECT BY LEVEL <= REGEXP_COUNT(f.d4_enclose, ',') + 1 ) split_data JOIN docdetail sub ON sub.id = split_data.split_value GROUP BY f.rowid; ``` --- ### 关键点解释 1. **作用域修复**: - 通过 `CROSS JOIN` 或 `LATERAL` 将拆分逻辑与主表关联,使 `f.d4_enclose` 可直接访问。 - `LATERAL` 是 Oracle 12c+ 的现代化语法,明确允许子查询引用外部列。 2. **字符串拆分逻辑**: ```sql REGEXP_SUBSTR(f.d4_enclose, '[^,]+', 1, LEVEL) -- 按逗号分割字符串 CONNECT BY LEVEL <= REGEXP_COUNT(...) + 1 -- 生成拆分后的行数 ``` 3. **聚合拼接**: ```sql LISTAGG(sub.docsubject, ',') WITHIN GROUP (ORDER BY sub.id) ``` - 将匹配的 `docsubject` 按 `id` 排序后拼接成字符串 4. **分组依据**: ```sql GROUP BY f.rowid -- 确保每行主表数据独立聚合 ``` ### 示例数据流 假设原始数据: | d4_enclose | docdetail 数据 | |------------|---------------------| | "101,102" | id=101, subject=AAA | | | id=102, subject=BBB | 输出结果: ``` d4_enclose ---------- AAA,BBB ``` --- ### 其他注意事项 1. **性能优化**: - 如果 `d4_enclose` 字段较长,建议在 `docdetail.id` 和 `formtable_main_1233` 主键上建立索引 - 大数据量时考虑物化视图或定期预聚合 2. **空值处理**: 添加 `LEFT JOIN` 防止拆分值为空时报错: ```sql LEFT JOIN docdetail sub ON sub.id = split_data.split_value ``` 3. **类型转换**: 如果 `docdetail.id` 是数字类型,需显式转换: ```sql ON sub.id = TO_NUMBER(split_data.split_value) ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值