记录一次oracle处理接收参数是String类型的数字

在开发中遇到一个问题,当SQL语句中的比较条件是String类型的百分比值时,直接使用可能导致结果错误。Oracle数据库在处理这类情况时,需要将String转换为Number类型,例如使用`to_number()`函数,否则结果可能不正确。MyBatis-Plus生成的SQL可能未做适当转换,因此需要手动修正,确保数据类型匹配以获取准确结果。

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

        在一次开发项目中,遇到求大于等于x.x%数据,在页面传递的x.x是String类型,在sql语句中接收x.x后返回的数据不符合要求。把语句copy到PL/SQL执行后返回的数据是正确的,代码执行后返回的数据就是不对。。。。。

        经排查后发现x.x是String类型,mybatis-plus处理语句后查询语句变成select * from table where num>'x.x',而我们在PL/SQL执行的时候是select * from table where num>x.x,注意下两者区别,前者多了''.大意了,排查后把语句改成select * from table where num>to_number('x.x')返回结果正确。

        这种小错误可能对刚入门的小伙伴来说有点奇葩,语句能正常执行,但结果就是不对。可能也有点莫名其妙,这跟oracle处理机制有关,表中列类型为number,传入为String类型执行语句也不会报错,只是结果可能不是自己想要的,尤其接收参数列为计算临时列可能结果更不准,这时就要把String类型的参数转换为number类型了。

        如:select b.c from (select a.a/a.b as c from (select a,b from table a)) b where b.c >= '5.5'

这种返回结果肯定是不对的,此时就要注意将'5.5'转换为5.5,语句修改为select b.c from (select a.a/a.b as c from (select a,b from table a)) b where b.c >= to_number('5.5'),返回结果正确。

        如果不正确之处,欢迎看的老铁指出。。。。

<think>我们使用C#实现HJ212-2017协议的客户端,主要步骤包括:1.接收数据报文(通常通过TCP或UDP,这以TCP为例) 2.解析数据报文(根据HJ212-2017协议格式) 3.校验数据(包括CRC16校验)4.将校验通过的数据存储到Oracle数据库以下是一个简化的实现步骤:###步骤1:创建TCP监听服务使用`TcpListener`监听指定端口,接收来自数据采集设备的数据。###步骤2:解析HJ212-2017协议报文HJ212-2017协议报文格式示例:``` ##[数据段长度]QN=数据时间;ST=站台编号;CN=命令编号;PW=密码;MN=设备唯一标识;Flag=标志位;CP=数据区&&[数据区]CRC16\r\n```其中:-数据段长度:从`QN`开始到`&&`之前(不包括`&&`)的字符数,用固定4位数字表示,不足4位前面补零。 -数据区:由多个字段组成,例如`Field1=Value1;Field2=Value2;...`。- CRC16:对从`QN`开始到`&&`之前(不包括`&&`)的数据段进行CRC16校验(多项式0x1021,初始0xFFFF)。###步骤3:CRC16校验实现HJ212协议规定的CRC16校验算法(CCITT标准)。###步骤4:存储到Oracle数据库使用Oracle的ADO.NET提供程序(如Oracle.ManagedDataAccess)将解析后的数据存入数据库。###代码示例####1.创建TCP监听服务```csharpusing System; usingSystem.Net; usingSystem.Net.Sockets; usingSystem.Text; usingSystem.Threading; classHJ212Client{private TcpListener_listener;privateconst intPort =12345;//监听端口public voidStart(){_listener= newTcpListener(IPAddress.Any,Port);_listener.Start();Console.WriteLine("监听已启动,等待连接...");while(true){TcpClient client= _listener.AcceptTcpClient();Thread thread= newThread(HandleClient);thread.Start(client);}}privatevoid HandleClient(objectobj){TcpClientclient =(TcpClient)obj;NetworkStreamstream =client.GetStream();byte[] buffer= newbyte[1024];int bytesRead;try{while ((bytesRead= stream.Read(buffer,0,buffer.Length)) !=0){string dataReceived= Encoding.ASCII.GetString(buffer,0,bytesRead);Console.WriteLine($"收到数据: {dataReceived}");//解析并处理报文if(TryParseHJ212(dataReceived,out HJ212Datadata)){//校验通过,存储到数据库SaveToDatabase(data);}}}catch(Exceptionex){Console.WriteLine($"异常:{ex.Message}");}finally{client.Close();}} }``` ####2.解析报文和CRC校验```csharp//HJ212数据结构publicclass HJ212Data{publicstring QN {get;set;} //请求编码public stringST {get;set;} //系统编码publicstring CN{ get; set; }//命令编码public stringPW {get;set;} //密码public stringMN {get;set;} //设备标识publicstring Flag{ get; set; }//标志位public Dictionary<string,string>CP {get;set;} //数据区} //解析HJ212报文privatebool TryParseHJ212(stringframe,out HJ212Datadata){data =null;//检查帧头和帧尾if (!frame.StartsWith("##")|| !frame.EndsWith("\r\n"))returnfalse;//提取数据段长度(4位)string lenStr =frame.Substring(2,4);if (!int.TryParse(lenStr,out intdataLen))return false;//提取数据段(从第6个字符开始,长度为dataLen)string dataSegment =frame.Substring(6,dataLen);//提取CRC16校验码(数据段后,结尾前,4个字符的十六进制)string crcStr= frame.Substring(6+ dataLen,4);if (!ushort.TryParse(crcStr,System.Globalization.NumberStyles.HexNumber, null, outushortreceivedCrc))returnfalse;//计算数据段的CRC16ushort calculatedCrc= CalculateCRC16(Encoding.ASCII.GetBytes(dataSegment));if (receivedCrc !=calculatedCrc){Console.WriteLine($"CRC校验失败:接收的CRC={receivedCrc:X4},计算的CRC={calculatedCrc:X4}");returnfalse;}//解析数据段(数据段格式:QN=...;ST=...;...&&)//注意:数据段最后是"&&",我们去掉它if(!dataSegment.EndsWith("&&"))return false;string dataContent =dataSegment.Substring(0, dataSegment.Length-2);//将数据段按分号分割string[]fields= dataContent.Split(';');data= newHJ212Data();data.CP= newDictionary<string, string>();foreach (string fieldin fields){int index= field.IndexOf('=');if (index <=0)continue;stringkey =field.Substring(0,index);stringvalue =field.Substring(index+1);switch(key){case "QN": data.QN= value; break;case "ST":data.ST =value;break;case"CN": data.CN= value; break;case "PW":data.PW =value;break;case"MN": data.MN= value; break;case "Flag":data.Flag =value;break;default://其他字段放入CPdata.CP[key] =value;break;}}return true; }// CRC16计算(多项式0x1021,初始0xFFFF) privateushortCalculateCRC16(byte[]data){ushortcrc=0xFFFF;for(inti =0;i <data.Length; i++){crc^=(ushort)(data[i] <<8);for (int j=0;j <8; j++){if ((crc&0x8000) !=0)crc= (ushort)((crc<<1)^0x1021);elsecrc <<=1;}}returncrc; }``` ####3.存储到Oracle数据库首先,安装Oracle.ManagedDataAccess NuGet包。 ```csharpusingOracle.ManagedDataAccess.Client; privatevoid SaveToDatabase(HJ212Datadata){string connectionString ="UserId=your_username;Password=your_password;DataSource=your_data_source;";using (OracleConnectionconn =new OracleConnection(connectionString)){conn.Open();//根据实际表结构编写插入语句//假设我们有一个表,包含字段:QN,ST,CN,PW,MN,Flag,以及数据区的字段(这以数据区中的B01为例)string sql= @"INSERT INTOHJ212_DATA (QN, ST, CN, PW, MN, Flag, B01,B02, ...)VALUES (:QN, :ST,:CN, :PW,:MN, :Flag,:B01,:B02,...)";using (OracleCommandcmd =new OracleCommand(sql, conn)){cmd.Parameters.Add("QN",data.QN);cmd.Parameters.Add("ST", data.ST);cmd.Parameters.Add("CN",data.CN);cmd.Parameters.Add("PW", data.PW);cmd.Parameters.Add("MN",data.MN);cmd.Parameters.Add("Flag", data.Flag);//添加数据区字段(假设数据区包含B01、B02等)foreach (var itemin data.CP){//注意:实际参数名需要与SQL中的占位符一致//这假设数据区的键就是数据库字段名(注意防止SQL注入,但这键是协议规定的,一般安全)cmd.Parameters.Add(item.Key,item.Value);}cmd.ExecuteNonQuery();}} }``` ###注意事项1. **协议细节**:上述代码仅作为示例,实际HJ212-2017协议可能更复杂,例如数据区可能有嵌套结构(如`DataTime=20230101120000;B01-Rtd=36.5,B01-Flag=N;...`),需要进一步解析。 2. **并发处理**:多设备连接时,需要考虑并发和性能问题,可以使用异步或线程池。 3. **数据库设计**:需要根据实际监测数据设计数据库表结构。4.**错误处理**:增加异常处理和日志记录。 5. **安全性**:在开放端口接收数据时,注意网络安全。 ###扩展-对于复杂的数据区(CP),可能需要递归解析,例如遇到逗号分隔的复合(如`B01-Rtd=36.5,B01-Flag=N`)可以进一步拆分成多个键对。-考虑使用异步方法(如`async/await`)提高并发性能。§§相关问题§§1. HJ212-2017协议中数据区(CP)的复杂结构如何解析? 2.如何优化TCP服务以支持高并发连接?3.Oracle数据库连接池的最佳实践是什么?</think>下面我将详细介绍如何使用C#实现HJ212-2017协议客户端,包括接收数据报文、进行校验,并将数据存储到Oracle数据库的完整解决方案。 ### 一、整体架构设计 ```mermaid graph TD A[数据采集设备] -->|发送HJ212报文| B(网络接收模块) B --> C[协议解析模块] C --> D[CRC校验模块] D -->|校验通过| E[数据存储模块] D -->|校验失败| F[错误处理模块] E --> G[Oracle数据库] ``` ### 二、核心模块实现 #### 1. 网络接收模块(TCP监听) ```csharp using System.Net; using System.Net.Sockets; using System.Text; public class HJ212Receiver { private const int PORT = 12345; // 监听端口 private TcpListener _listener; public void Start() { _listener = new TcpListener(IPAddress.Any, PORT); _listener.Start(); Console.WriteLine($"HJ212监听已启动,端口: {PORT}"); while (true) { TcpClient client = _listener.AcceptTcpClient(); ThreadPool.QueueUserWorkItem(HandleClient, client); } } private void HandleClient(object state) { using (TcpClient client = (TcpClient)state) using (NetworkStream stream = client.GetStream()) { byte[] buffer = new byte[4096]; int bytesRead = stream.Read(buffer, 0, buffer.Length); string rawData = Encoding.ASCII.GetString(buffer, 0, bytesRead); Console.WriteLine($"收到原始数据: {rawData}"); HJ212Parser.ProcessData(rawData); } } } ``` #### 2. 协议解析与校验模块 ```csharp public static class HJ212Parser { public static void ProcessData(string rawData) { // 1. 验证基本格式 if (!rawData.StartsWith("##") || !rawData.EndsWith("\r\n")) throw new FormatException("无效的HJ212报文格式"); // 2. 提取数据段长度 string lenStr = rawData.Substring(2, 4); if (!int.TryParse(lenStr, out int dataLength)) throw new FormatException("无效的数据长度字段"); // 3. 提取数据段和CRC string dataSegment = rawData.Substring(6, dataLength); string receivedCrc = rawData.Substring(6 + dataLength, 4); // 4. CRC校验 string calculatedCrc = CalculateCrc(dataSegment); if (receivedCrc != calculatedCrc) throw new InvalidDataException($"CRC校验失败: 接收={receivedCrc}, 计算={calculatedCrc}"); // 5. 解析数据区 HJ212Data data = ParseDataSegment(dataSegment); // 6. 存储到数据库 OracleDataSaver.SaveToDatabase(data); } private static string CalculateCrc(string data) { byte[] bytes = Encoding.ASCII.GetBytes(data); ushort crc = 0xFFFF; foreach (byte b in bytes) { crc ^= (ushort)(b << 8); for (int i = 0; i < 8; i++) { crc = (crc & 0x8000) != 0 ? (ushort)((crc << 1) ^ 0x1021) : (ushort)(crc << 1); } } return crc.ToString("X4"); // 返回4位十六进制 } private static HJ212Data ParseDataSegment(string segment) { // 示例数据段: QN=20230815120000;ST=32;CN=2011;PW=123456;MN=010000A8900016F000169DC0;Flag=5;CP=&&DataTime=20230815120000;B01-Rtd=25.4,B01-Flag=N;B02-Rtd=50.1,B02-Flag=N&& var data = new HJ212Data(); string[] parts = segment.Split(';'); foreach (string part in parts) { if (part.Contains("=")) { string[] keyValue = part.Split('=', 2); string key = keyValue[0]; string value = keyValue[1]; switch (key) { case "QN": data.RequestTime = value; break; case "ST": data.SystemCode = value; break; case "CN": data.CommandCode = value; break; case "PW": data.Password = value; break; case "MN": data.DeviceId = value; break; case "Flag": data.Flags = value; break; case "CP": ParseCpData(value, data); break; } } } return data; } private static void ParseCpData(string cpData, HJ212Data data) { // 去除CP字段的&&分隔符 if (cpData.StartsWith("&&") && cpData.EndsWith("&&")) cpData = cpData.Substring(2, cpData.Length - 4); string[] measurements = cpData.Split(';'); foreach (string measurement in measurements) { if (measurement.Contains("=")) { string[] parts = measurement.Split('=', 2); data.Measurements[parts[0]] = parts[1]; } } } } public class HJ212Data { public string RequestTime { get; set; } public string SystemCode { get; set; } public string CommandCode { get; set; } public string Password { get; set; } public string DeviceId { get; set; } public string Flags { get; set; } public Dictionary<string, string> Measurements { get; } = new Dictionary<string, string>(); } ``` #### 3. Oracle数据库存储模块 ```csharp using Oracle.ManagedDataAccess.Client; public static class OracleDataSaver { private const string ConnectionString = "User Id=your_user;Password=your_pwd;Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)" + "(HOST=your_host)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=your_service)));"; public static void SaveToDatabase(HJ212Data data) { using (var conn = new OracleConnection(ConnectionString)) { conn.Open(); // 1. 保存主表信息 using (var cmd = new OracleCommand( "INSERT INTO HJ212_MAIN (REQUEST_TIME, SYSTEM_CODE, COMMAND_CODE, DEVICE_ID, FLAGS) " + "VALUES (:reqTime, :sysCode, :cmdCode, :devId, :flags)", conn)) { cmd.Parameters.Add("reqTime", data.RequestTime); cmd.Parameters.Add("sysCode", data.SystemCode); cmd.Parameters.Add("cmdCode", data.CommandCode); cmd.Parameters.Add("devId", data.DeviceId); cmd.Parameters.Add("flags", data.Flags); cmd.ExecuteNonQuery(); } // 2. 保存测量数据 foreach (var measurement in data.Measurements) { using (var cmd = new OracleCommand( "INSERT INTO HJ212_MEASUREMENTS (DEVICE_ID, PARAM_CODE, VALUE, REQUEST_TIME) " + "VALUES (:devId, :param, :value, :reqTime)", conn)) { cmd.Parameters.Add("devId", data.DeviceId); cmd.Parameters.Add("param", measurement.Key); cmd.Parameters.Add("value", measurement.Value); cmd.Parameters.Add("reqTime", data.RequestTime); cmd.ExecuteNonQuery(); } } } } } ``` ### 三、数据库表结构设计 ```sql -- 主表存储设备信息 CREATE TABLE HJ212_MAIN ( ID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, REQUEST_TIME VARCHAR2(14) NOT NULL, -- 格式: yyyyMMddHHmmss SYSTEM_CODE VARCHAR2(10) NOT NULL, COMMAND_CODE VARCHAR2(10) NOT NULL, DEVICE_ID VARCHAR2(30) NOT NULL, FLAGS VARCHAR2(10), RECEIVE_TIME TIMESTAMP DEFAULT SYSTIMESTAMP ); -- 测量数据表 CREATE TABLE HJ212_MEASUREMENTS ( ID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, DEVICE_ID VARCHAR2(30) NOT NULL, PARAM_CODE VARCHAR2(20) NOT NULL, -- 如 B01-Rtd VALUE VARCHAR2(50) NOT NULL, REQUEST_TIME VARCHAR2(14) NOT NULL, RECEIVE_TIME TIMESTAMP DEFAULT SYSTIMESTAMP, CONSTRAINT fk_device FOREIGN KEY (DEVICE_ID) REFERENCES HJ212_MAIN(DEVICE_ID) ); ``` ### 四、部署与使用步骤 1. **安装依赖**: ```bash Install-Package Oracle.ManagedDataAccess ``` 2. **启动服务**: ```csharp class Program { static void Main() { var receiver = new HJ212Receiver(); receiver.Start(); } } ``` 3. **配置调整**: - 修改`OracleDataSaver`中的连接字符串 - 根据实际设备调整端口号 - 根据监测需求扩展`HJ212Data`类 4. **错误处理增强**: ```csharp try { HJ212Parser.ProcessData(rawData); } catch (Exception ex) { File.AppendAllText("error.log", $"{DateTime.Now}: {ex.Message}\n"); // 可选:发送错误响应给设备 } ``` ### 五、性能优化建议 1. **连接池管理**: ```csharp // 在app.config中配置 <oracle.manageddataaccess.client> <version number="*"> <settings> <setting name="Connection Pooling" value="True"/> <setting name="Min Pool Size" value="5"/> <setting name="Max Pool Size" value="50"/> </settings> </version> </oracle.manageddataaccess.client> ``` 2. **批量插入优化**: ```csharp // 使用OracleBulkCopy进行批量插入 var bulkCopy = new OracleBulkCopy(conn); bulkCopy.DestinationTableName = "HJ212_MEASUREMENTS"; bulkCopy.WriteToServer(measurementsDataTable); ``` 3. **异步处理**: ```csharp ThreadPool.QueueUserWorkItem(async state => { await OracleDataSaver.SaveToDatabaseAsync(data); }); ``` ### 六、注意事项 1. **协议版本**:HJ212-2017与旧版的主要区别在于: - 增加了数据段长度字段 - 修改了CRC校验范围 - 扩展了数据区格式[^1] 2. **安全考虑**: - 使用防火墙限制访问IP - 数据库连接字符串加密存储 - 实施SQL参数化防止注入 3. **数据完整性**: - 添加数据库事务处理 - 实现重试机制 - 添加ACK响应确认
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值