系统动态游标SYS_REFCURSOR的使用

本文详细介绍Oracle系统动态游标SYS_REFCURSOR的使用方法,包括数据准备、存储过程创建、测试用例及普通动态游标的对比。通过具体实例展示如何在PL/SQL中利用SYS_REFCURSOR进行数据检索。

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

系统动态游标SYS_REFCURSOR的使用

准备数据

-- 创建表
create table test_user_info(
    user_id integer primary key,--primary key
    user_name varchar2(20),
    sex varchar2(2)
);
  
-- 插入测试数据
insert into test_user_info(user_id,user_name,sex) values(1,'JerryWong','M');
insert into test_user_info(user_id,user_name,sex) values(2,'tingting','F'); 
commit;
-- 查数 
select * from test_user_info;

准备存储过程

-- 创建过程取数 
create or replace procedure test_0124(p_cursor out sys_refcursor)
as
begin
  open p_cursor for
  select * from test_user_info;
end;

使用测试

-- 测试1
declare
  v_cursor sys_refcursor;
  u test_user_info%rowtype;
begin
  test_0124(v_cursor);
  --loop fetch v_cursor into u.user_id, u.user_name,u.sex;    
  loop 
    fetch v_cursor into u;          
    exit when v_cursor%notfound;     
    dbms_output.put_line(u.user_id||'-'||u.user_name||'-'||u.sex); 
  end loop;
end;


-- 测试2
declare
  v_cursor sys_refcursor;
  user_id integer;
  user_name varchar2(20);
  sex varchar2(2);
begin
  test_0124(v_cursor);
  loop 
    fetch v_cursor into user_id, user_name,sex;          
    exit when v_cursor%notfound;
    dbms_output.put_line(user_id||'-'||user_name||'-'||sex);
  end loop;
end;

删除测试数据

-- 删除测试过程
drop procedure test_0124
-- 删除测试表
drop table test_user_info

附:普通动态游标的创建

declare
  type rc is ref cursor; -- 定义类型
  cursor c is
    select * from dual; -- 普通静态游标

  r_cursor  rc; -- 普通动态游标
  sr_cursor sys_refcursor; -- 系统动态游标
begin
  if (to_char(sysdate, 'mi') >= 40) then
    -- ref cursor with dynamic sql 
    open r_cursor for 'select * from dim_employee';
    open sr_cursor for 'select * from dim_org_dept';
    
  elsif (to_char(sysdate, 'mi') <= 20) then
    -- ref cursor with static sql 
    open r_cursor for select * from dim_org_dept;
    open sr_cursor for select * from dim_employee;
      
  else
    -- ref cursor with static sql 
    open r_cursor for select * from dual;
    open sr_cursor for select * from dual;
      
  end if;
  
  -- the "normal" static cursor 
  open c;
  
end;
<think>我们正在讨论的是在SQLServer中调用Oracle存储过程,该存储过程包含SYS_REFCURSOR类型的输出参数。注意:SQLServer和Oracle是两种不同的数据库系统,不能直接在SQLServer中调用Oracle存储过程,除非通过链接服务器(LinkedServer)或应用程序层(如Java,C#等)进行跨数据库调用。然而,用户的问题是:“我想在SQLServer中调用带有sys_refcursor的存储过程的方法”,这实际上涉及到两个不同的数据库系统之间的交互。根据引用[3]提到,在保证存储过程名称相同参数顺序相同(Oracle中的游标置于最后)的情况下,使用同样的Java代码进行调用。这提示我们可以通过应用程序层(如Java)来同时调用两种数据库的存储过程,但并不是直接在SQLServer中调用Oracle的存储过程。如果用户确实需要在SQLServer环境中访问Oracle数据库中的存储过程返回的游标结果,我们可以使用以下方法:方法一:通过SQLServer的链接服务器(LinkedServer)调用Oracle存储过程方法二:通过应用程序(如Java,C#)作为中介,分别连接两个数据库,从Oracle获取数据再写入SQLServer(如果需要)但是,需要注意的是:1.SYS_REFCURSOROracle特有的类型,SQLServer中没有直接对应的类型。2.在SQLServer中通过链接服务器调用Oracle存储过程时,无法直接处理SYS_REFCURSOR输出参数,因为链接服务器通常只支持标量类型或表值结果。因此,更常见的做法是在应用程序中处理,或者将Oracle存储过程返回的结果集转换为表形式(例如使用管道函数或临时表),然后通过链接服务器查询。下面分别说明:方法一:使用链接服务器调用返回结果集的Oracle存储过程(需要将游标转换为表)在Oracle端,我们可以修改存储过程,使其返回一个表类型(例如使用管道函数)或者将结果集插入到全局临时表中,然后让SQLServer通过链接服务器查询该临时表。步骤:1.在Oracle中创建全局临时表(或者使用管道函数):```sqlCREATEGLOBALTEMPORARYTABLEtemp_employee_data(employee_idNUMBER,nameVARCHAR2(100),salaryNUMBER)ONCOMMITPRESERVEROWS;```2.修改存储过程,将游标数据插入临时表:```sqlCREATEORREPLACEPROCEDUREget_employee_data(p_department_idINNUMBER)ASBEGININSERTINTOtemp_employee_dataSELECTemployee_id,name,salaryFROMemployeesWHEREdepartment_id=p_department_id;END;```3.在SQLServer中配置指向Oracle的链接服务器(假设链接服务器名为ORACLE_LINK)。4.在SQLServer中先执行存储过程(注意:执行存储过程并不会返回结果,只是插入数据到临时表):```sqlEXEC('BEGINget_employee_data(10);END;')ATORACLE_LINK;```5.然后查询临时表:```sqlSELECT*FROMOPENQUERY(ORACLE_LINK,'SELECT*FROMtemp_employee_data');```缺点:临时表在会话结束时会自动删除,但这里需要保证在同一个会话中执行存储过程和查询,而链接服务器的每次调用可能是独立的会话。因此,可以考虑使用普通表(带事务控制)或者使用管道函数。方法二:使用管道函数(返回表类型)1.在Oracle中创建管道函数:```sqlCREATETYPEemp_recASOBJECT(employee_idNUMBER,nameVARCHAR2(100),salaryNUMBER);CREATETYPEemp_tabASTABLEOFemp_rec;CREATEORREPLACEFUNCTIONget_employee_data_func(p_department_idINNUMBER)RETURNemp_tabPIPELINEDASBEGINFORrecIN(SELECTemployee_id,name,salaryFROMemployeesWHEREdepartment_id=p_department_id)LOOPPIPEROW(emp_rec(rec.employee_id,rec.name,rec.salary));ENDLOOP;RETURN;END;```2.在SQLServer中查询:```sqlSELECT*FROMOPENQUERY(ORACLE_LINK,'SELECT*FROMTABLE(get_employee_data_func(10))');```方法三:通过应用程序(如C#)调用Oracle存储过程获取游标,然后将数据插入SQLServer(如果需要)考虑到用户的问题是在SQLServer中调用,所以重点放在链接服务器的方法上。然而,如果用户希望直接处理游标,在SQLServer中是不可能的,因为SQLServer没有游标类型可以接收OracleSYS_REFCURSOR。因此,我们只能通过将游标转换为表的形式来访问。总结:在SQLServer中调用包含SYS_REFCURSOROracle存储过程,需要将Oracle存储过程改为返回表的形式(通过临时表或管道函数),然后通过链接服务器查询。因此,回答如下:</think>### 如何在 SQL Server 中调用包含 SYS_REFCURSOR 参数的 Oracle 存储过程 在 SQL Server 中调用 Oracle 包含 `SYS_REFCURSOR` 参数的存储过程需要跨数据库操作,以下是两种主要方法: --- #### 方法一:通过链接服务器 (Linked Server) 调用 **前提条件**:配置好 SQL Server 到 Oracle 的链接服务器 1. **配置链接服务器** ```sql EXEC sp_addlinkedserver @server = 'ORACLE_LINK', @srvproduct = 'Oracle', @provider = 'OraOLEDB.Oracle', @datasrc = '你的Oracle服务名' ``` 2. **调用存储过程并获取游标结果** ```sql DECLARE @SQL NVARCHAR(MAX) = N' DECLARE cur SYS_REFCURSOR; BEGIN your_oracle_procedure(?, cur); -- ? 是输入参数 ? := cur; -- 返回游标引用 END;' DECLARE @Result TABLE ( employee_id INT, name NVARCHAR(50), salary DECIMAL(18,2) ); INSERT INTO @Result EXEC ('BEGIN ' + @SQL + ' END;', 输入参数值, @cursor = 0) AT ORACLE_LINK; -- 通过链接服务器执行 ``` 3. **查询结果** ```sql SELECT * FROM @Result; ``` > **关键点**:使用 `AT` 关键字指定链接服务器,并通过表变量接收结果集[^3][^4]。 --- #### 方法二:通过应用程序层中转(推荐) 使用 C# 等中间层处理跨数据库调用: ```csharp using (OracleConnection oraConn = new OracleConnection(oracleConnString)) using (SqlConnection sqlConn = new SqlConnection(sqlConnString)) { // 调用 Oracle 存储过程 OracleCommand oraCmd = new OracleCommand("your_oracle_procedure", oraConn); oraCmd.CommandType = CommandType.StoredProcedure; oraCmd.Parameters.Add("p_input", OracleDbType.Int32).Value = inputValue; oraCmd.Parameters.Add("p_cur", OracleDbType.RefCursor).Direction = ParameterDirection.Output; oraConn.Open(); OracleDataReader reader = oraCmd.ExecuteReader(); // 将结果插入 SQL Server sqlConn.Open(); using (SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConn)) { bulkCopy.DestinationTableName = "ResultTable"; bulkCopy.WriteToServer(reader); // 直接传输结果集 } } ``` > **优势**: > 1. 避免数据库间直接依赖 > 2. 支持大数据量传输(使用 `SqlBulkCopy`) > 3. 更灵活的错误处理[^3][^4] --- #### 关键注意事项: 1. **类型映射** Oracle 的 `NUMBER` 需映射为 SQL Server 的 `DECIMAL`,`VARCHAR2` 映射为 `NVARCHAR` 2. **性能优化** - 使用分页参数减少数据传输量 - 在 Oracle 存储过程中添加 `/*+ FIRST_ROWS(n) */` 提示 3. **错误处理** ```sql BEGIN TRY EXEC('...') AT ORACLE_LINK END TRY BEGIN CATCH PRINT ERROR_MESSAGE() -- 捕获跨数据库错误 END CATCH ``` 4. **替代方案** 定期将 Oracle 数据同步到 SQL Server 的临时表,直接查询本地表 > **适用场景**:报表集成、跨系统数据抽取、异构数据库迁移等[^3][^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值