C#实现跨不同Oracle数据库两表关联查询返回结果
C# 跨两个Oracle库关联查询实现方案
方案选型说明
当前场景中student和class表分别位于两个独立的Oracle实例,无法直接写单条SQL做本地关联,根据数据库侧权限不同有两种落地方式:
- 应用层内存关联:不需要修改数据库配置,纯C#代码实现,适合中小数据量场景
- DBLink关联:需要DBA在两个库之间配置DBLink,直接单SQL查询,性能更好,适合大数据量场景
前置准备
先通过NuGet安装Oracle官方维护的驱动,弃用微软早已停止更新的System.Data.OracleClient:
- .NET Framework环境安装
Oracle.ManagedDataAccess - .NET Core/.NET 5+环境安装
Oracle.ManagedDataAccess.Core
方案1:应用层内存关联实现(无数据库依赖)
核心逻辑是分别从两个库拉取需要的字段数据,在内存中通过studentid做内连接匹配,代码全部封装在studentgetmethod()方法中:
using Oracle.ManagedDataAccess.Client; using System.Collections.Generic; using System.Linq; // 自定义返回结果实体,可根据实际业务字段调整 public class StudentClassResult { public int StudentId { get; set; } public string StudentName { get; set; } public string ClassName { get; set; } // 其他需要返回的字段自行在此定义 } public List<StudentClassResult> studentgetmethod() { var matchResult = new List<StudentClassResult>(); // 注意:生产环境不要硬编码连接字符串,统一从配置文件读取 const string studentDbConnStr = "connectionstring1对应的完整连接串"; const string classDbConnStr = "connectionstring2对应的完整连接串"; // 第一步:读取student库的需要数据,存入字典做关联索引 var studentMap = new Dictionary<int, StudentClassResult>(); using (var conn = new OracleConnection(studentDbConnStr)) { conn.Open(); const string queryStudentSql = "SELECT STUDENTID, STUDENTNAME FROM STUDENT"; using (var cmd = new OracleCommand(queryStudentSql, conn)) using (var reader = cmd.ExecuteReader()) { var stuIdColIndex = reader.GetOrdinal("STUDENTID"); var stuNameColIndex = reader.GetOrdinal("STUDENTNAME"); while (reader.Read()) { var studentId = reader.GetInt32(stuIdColIndex); if (!studentMap.ContainsKey(studentId)) { studentMap.Add(studentId, new StudentClassResult { StudentId = studentId, StudentName = reader.GetString(stuNameColIndex) }); } } } } // 无student数据直接返回空结果,避免无效查询 if (!studentMap.Any()) return matchResult; var validStudentIds = studentMap.Keys.ToList(); // 第二步:读取class库中匹配studentid的数据,内存做关联 using (var conn = new OracleConnection(classDbConnStr)) { conn.Open(); // 动态拼接IN条件,参数化查询避免SQL注入 var inParamPlaceholders = string.Join(",", validStudentIds.Select((id, idx) => $":stuId{idx}")); var queryClassSql = $"SELECT STUDENTID, CLASSNAME FROM CLASS WHERE STUDENTID IN ({inParamPlaceholders})"; using (var cmd = new OracleCommand(queryClassSql, conn)) { // 绑定IN参数 for (var i = 0; i < validStudentIds.Count; i++) { cmd.Parameters.Add(new OracleParameter($":stuId{i}", validStudentIds[i])); } using (var reader = cmd.ExecuteReader()) { var stuIdColIndex = reader.GetOrdinal("STUDENTID"); var classNameColIndex = reader.GetOrdinal("CLASSNAME"); while (reader.Read()) { var studentId = reader.GetInt32(stuIdColIndex); if (studentMap.TryGetValue(studentId, out var studentInfo)) { studentInfo.ClassName = reader.GetString(classNameColIndex); matchResult.Add(studentInfo); } } } } } return matchResult; }
方案2:DBLink关联实现(需要数据库侧配置)
如果有数据库操作权限,可以让DBA在connectionstring1对应的student库上创建指向class库的DBLink(假设DBLink名称为DBLINK_TO_CLASS_DB),就可以直接通过单条连接完成关联查询,性能更高,代码更简洁:
using Oracle.ManagedDataAccess.Client; using System.Collections.Generic; public List<StudentClassResult> studentgetmethod() { var matchResult = new List<StudentClassResult>(); const string studentDbConnStr = "connectionstring1对应的完整连接串"; using (var conn = new OracleConnection(studentDbConnStr)) { conn.Open(); const string joinQuerySql = @" SELECT s.STUDENTID, s.STUDENTNAME, c.CLASSNAME FROM STUDENT s INNER JOIN CLASS@DBLINK_TO_CLASS_DB c ON s.STUDENTID = c.STUDENTID"; using (var cmd = new OracleCommand(joinQuerySql, conn)) using (var reader = cmd.ExecuteReader()) { var stuIdColIndex = reader.GetOrdinal("STUDENTID"); var stuNameColIndex = reader.GetOrdinal("STUDENTNAME"); var classNameColIndex = reader.GetOrdinal("CLASSNAME"); while (reader.Read()) { matchResult.Add(new StudentClassResult { StudentId = reader.GetInt32(stuIdColIndex), StudentName = reader.GetString(stuNameColIndex), ClassName = reader.GetString(classNameColIndex) }); } } } return matchResult; }
注意事项
- 禁止硬编码数据库连接串,生产环境需将连接串存入配置文件并对账号密码做加密处理
- 查询时只返回业务需要的字段,禁止使用
SELECT *拉取全量字段,减少不必要的网络传输和内存占用 - 单表数据量超过10万级时不建议使用内存关联方案,优先选择DBLink或者提前做数据同步
- 所有数据库连接、Command、DataReader对象必须用
using块包裹,保证资源自动释放,避免数据库连接泄漏
内容的提问来源于stack exchange,提问作者Kris Kyun
相关产品推荐
相关产品推荐

