c# 中間表的實現(xiàn)方式
你是做工控、上位機、數(shù)據(jù)庫+Modbus開發(fā)的,這個詞對你百分百剛需,而且C#里的「中間表」是業(yè)務(wù)開發(fā)的通用叫法,不是語法關(guān)鍵字,和你寫Modbus報文、上位機數(shù)據(jù)入庫是強相關(guān)的,解釋全貼合你的開發(fā)場景??
? 一句話定義:
中間表 = 臨時中轉(zhuǎn)的數(shù)據(jù)庫表,也叫過渡表/橋接表,本質(zhì)就是在**「數(shù)據(jù)源」和「目標表」** 之間加的一張數(shù)據(jù)庫表(你用的MySQL居多),數(shù)據(jù)不落地到最終表,先存到這張表里做處理。
你的場景里:數(shù)據(jù)源就是PLC/傳感器/儀表的Modbus采集數(shù)據(jù),目標表就是最終的業(yè)務(wù)表(比如生產(chǎn)記錄表、設(shè)備狀態(tài)表、報警記錄表),中間表就是夾在這兩者之間的那張表。
? 為什么一定要用「中間表」?(工控上位機開發(fā) 必用!核心原因)
你寫C#上位機,肯定會遇到這些問題,中間表就是專門解決這些痛點的,也是工業(yè)項目的標準寫法,沒有例外:
1. 最核心:解決「采集不停,入庫要處理」的沖突
你的Modbus程序是毫秒級循環(huán)采集PLC數(shù)據(jù)的(比如每秒讀10次),數(shù)據(jù)是源源不斷的;但數(shù)據(jù)庫的寫入/更新/計算是需要耗時的,如果采集的數(shù)據(jù)直接寫入最終業(yè)務(wù)表,會卡死數(shù)據(jù)庫+采集線程阻塞,甚至丟數(shù)據(jù)、Modbus通訊超時。
?? 解決方案:采集到的原始數(shù)據(jù) 先無腦寫入中間表(寫入速度極快,不做任何計算),再開一個單獨的線程,從中間表讀取數(shù)據(jù)、做清洗/計算/判斷,再寫入最終表。
2. 數(shù)據(jù)清洗+格式轉(zhuǎn)換(你的Modbus開發(fā)高頻場景)
PLC/儀表的采集值都是原始數(shù)值:比如寄存器讀出來的是int16/uint32的數(shù)字、溫度是放大10倍的整數(shù)(如256=25.6℃)、開關(guān)量是0/1、報警碼是十六進制。
?? 這些臟數(shù)據(jù)先丟進中間表,再在C#里寫邏輯:把0/1轉(zhuǎn)成「運行/停止」、把256轉(zhuǎn)成25.6℃、把報警碼轉(zhuǎn)成文字、過濾掉無效的采集值,處理完再進最終表。
3. 解耦「采集程序」和「業(yè)務(wù)程序」,容錯率拉滿
如果你的最終業(yè)務(wù)表要改結(jié)構(gòu)(比如加字段、改字段名)、或者數(shù)據(jù)庫宕機,采集程序不受任何影響,因為采集只往中間表寫數(shù)據(jù),中間表的結(jié)構(gòu)幾乎不變;等數(shù)據(jù)庫恢復/改完表結(jié)構(gòu),再把中間表的數(shù)據(jù)同步過去就行,不會丟失任何采集數(shù)據(jù)。
4. 批量處理,提升數(shù)據(jù)庫性能
Modbus采集是高頻、少量的數(shù)據(jù),如果每次采集一條就往數(shù)據(jù)庫插一條,數(shù)據(jù)庫的IO會被拉滿,時間久了會變慢;
?? 把采集的數(shù)據(jù)先攢在中間表,攢夠100條/攢夠1分鐘,再批量寫入最終表,數(shù)據(jù)庫壓力直接降90%,這是工業(yè)上位機的最優(yōu)性能寫法。
? 你做「C#+Modbus+MySQL」開發(fā)的 中間表完整工作流程(標準版,直接套用)
這個流程是工控上位機的標準答案,你所有的項目都可以這么寫,不會出問題,邏輯如下:
【PLC/儀表】 → Modbus報文讀取 → C#解析出原始數(shù)值 → 無腦寫入【數(shù)據(jù)庫中間表】 → C#后臺線程讀取中間表 → 數(shù)據(jù)清洗/計算/判斷 → 寫入【數(shù)據(jù)庫最終業(yè)務(wù)表】
?? 舉個你的實際開發(fā)例子(最直觀)
比如你采集溫控儀表的溫度數(shù)據(jù):
- Modbus讀寄存器,得到原始值 258(儀表把溫度×10存儲,258=25.8℃);
- C#把 設(shè)備編號、采集時間、原始值258 這三個字段,直接插入到中間表(表名比如:tb_data_temp_mid);
- C#開一個定時器線程,每隔1秒讀取中間表的未處理數(shù)據(jù),做處理:258 ÷ 10 = 25.8℃,如果溫度>30℃就標記為「高溫報警」;
- 把處理后的 設(shè)備編號、采集時間、實際溫度25.8℃、狀態(tài)正常 寫入最終業(yè)務(wù)表(表名比如:tb_data_temp);
- 處理完后,把中間表里這條數(shù)據(jù)標記為「已處理」,或者直接刪除。
? 中間表的設(shè)計原則(C#工控開發(fā),極簡夠用,不用復雜)
你用MySQL,中間表的設(shè)計越簡單越好,字段越少、寫入越快,完全不用設(shè)計復雜結(jié)構(gòu),遵循3個原則就行:
? 核心設(shè)計:只存「原始、未處理」的采集數(shù)據(jù)
中間表的字段組成(通用模板,所有采集場景都能套):
CREATE TABLE tb_mid_data (
id INT PRIMARY KEY AUTO_INCREMENT, -- 自增主鍵,唯一標識
dev_code VARCHAR(20) NOT NULL, -- 設(shè)備編號/站號(比如Modbus的站號01、02)
reg_addr VARCHAR(20) NOT NULL, -- 寄存器地址(比如40001、30002)
raw_value VARCHAR(50) NOT NULL, -- 采集的原始值(數(shù)字/字符串都可以,不用轉(zhuǎn)格式)
collect_time DATETIME NOT NULL, -- 采集時間
is_handle TINYINT DEFAULT 0 -- 處理標記:0=未處理,1=已處理
);? 重點:中間表只存原始數(shù)據(jù),不做任何計算和關(guān)聯(lián),字段都是基礎(chǔ)類型,寫入速度最快,這是核心!
? 最終業(yè)務(wù)表的設(shè)計
和中間表相反,業(yè)務(wù)表存處理后的、可讀的、用于展示/報表/查詢的數(shù)據(jù),字段是業(yè)務(wù)相關(guān)的,比如:
CREATE TABLE tb_data_temp (
id INT PRIMARY KEY AUTO_INCREMENT,
dev_name VARCHAR(50) NOT NULL, -- 設(shè)備名稱(比如:一號溫控儀表)
real_temp DECIMAL(5,1) NOT NULL, -- 處理后的實際溫度(25.8)
temp_status VARCHAR(20) NOT NULL, -- 溫度狀態(tài)(正常/高溫報警/低溫報警)
collect_time DATETIME NOT NULL,
dev_code VARCHAR(20) NOT NULL
);? 補充2個你一定會問的問題(高頻疑問,工控專屬)
? C#里怎么寫「中間表同步到業(yè)務(wù)表」的邏輯?
兩種最常用的寫法,都是異步執(zhí)行,不會阻塞你的Modbus采集線程,優(yōu)先用第一種:
?? 寫法1:定時器異步處理(推薦,最簡單,工業(yè)首選)
用C#的System.Timers.Timer,設(shè)置每隔1000ms執(zhí)行一次,邏輯如下:
// 初始化定時器,1秒執(zhí)行一次
System.Timers.Timer timer = new System.Timers.Timer(1000);
timer.Elapsed += (s,e) => {
// 1. 從中間表查詢 未處理的數(shù)據(jù)(is_handle=0)
string sql = "SELECT * FROM tb_mid_data WHERE is_handle=0 LIMIT 100";
List<MidData> list = MySqlHelper.Query<MidData>(sql);
// 2. 遍歷數(shù)據(jù),做清洗/計算/轉(zhuǎn)換
foreach(var item in list){
double rawVal = Convert.ToDouble(item.raw_value);
double realTemp = rawVal / 10; // 溫度轉(zhuǎn)換
string status = realTemp>30 ? "高溫報警" : "正常";
// 3. 寫入最終業(yè)務(wù)表
string insertSql = $"INSERT INTO tb_data_temp(dev_name,real_temp,temp_status,collect_time,dev_code) VALUES('一號儀表',{realTemp},'{status}','{item.collect_time}','{item.dev_code}')";
MySqlHelper.ExecuteNonQuery(insertSql);
// 4. 標記中間表數(shù)據(jù)為已處理
string updateSql = $"UPDATE tb_mid_data SET is_handle=1 WHERE id={item.id}";
MySqlHelper.ExecuteNonQuery(updateSql);
}
};
timer.Start();?? 寫法2:批量處理(性能更強,適合高頻率采集)
如果每秒采集1000條以上數(shù)據(jù),就攢夠一定數(shù)量再批量插入業(yè)務(wù)表,C#里用MySqlBulkCopy,速度極快,這個我后面可以給你寫現(xiàn)成代碼。
? 中間表的數(shù)據(jù)要不要清理?
一定要清理! 中間表只是中轉(zhuǎn)站,數(shù)據(jù)處理完就沒用了,不清理的話會越來越大,拖慢數(shù)據(jù)庫。
?? 解決方案:加一個定時任務(wù),每天凌晨3點,刪除中間表里「已處理」的所有數(shù)據(jù)(is_handle=1),一行代碼搞定:
DELETE FROM tb_mid_data WHERE is_handle=1;
? 額外補充:C#里還有「中間類」的說法,別和「中間表」搞混!
你問的是「中間表」,但做開發(fā)的時候大概率會看到「中間類」,也是高頻詞,順帶講清楚,避免混淆,兩個都是你的剛需:
?? 中間表 → 數(shù)據(jù)庫層面的概念 → 存數(shù)據(jù)的臨時表
?? 中間類 → C#代碼層面的概念 → 存數(shù)據(jù)的臨時類
中間類:也叫實體類/模型類/DTO類,就是C#里的一個普通class,用來接收Modbus采集的原始數(shù)據(jù),再把這個類的對象寫入數(shù)據(jù)庫中間表。
比如你采集溫度的中間類:
// C# 中間類(Modbus采集數(shù)據(jù)的載體)
public class MidDataModel
{
public int Id { get; set; }
public string DevCode { get; set; } // 設(shè)備編號
public string RegAddr { get; set; } // 寄存器地址
public string RawValue { get; set; } // 原始采集值
public DateTime CollectTime { get; set; } // 采集時間
public int IsHandle { get; set; } // 是否處理
}? 關(guān)系:Modbus采集數(shù)據(jù) → 賦值給「中間類」→ 把中間類的數(shù)據(jù)寫入「中間表」→ 處理后寫入「業(yè)務(wù)表」
? 總結(jié)(你的開發(fā)場景,劃重點)
對你這個做 C#+Modbus+MySQL工控上位機開發(fā) 的工程師來說:
- 「中間表」是必須用的開發(fā)手段,不是可選的,是工業(yè)項目的標準規(guī)范;
- 核心作用:解耦采集和業(yè)務(wù)、防止丟數(shù)據(jù)、提升性能、方便數(shù)據(jù)處理;
- 設(shè)計原則:中間表極簡,只存原始數(shù)據(jù);業(yè)務(wù)表規(guī)范,存處理后的數(shù)據(jù);
- 代碼邏輯:定時器異步處理中間表數(shù)據(jù),不阻塞采集線程。
你現(xiàn)在寫的Modbus采集程序,如果還沒加中間表,建議加上,會解決你大概率遇到的「數(shù)據(jù)庫卡死、采集丟數(shù)據(jù)、通訊超時」等問題。
到此這篇關(guān)于c# 中間表的實現(xiàn)方式的文章就介紹到這了,更多相關(guān)c# 中間表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用C#實現(xiàn)將Word?轉(zhuǎn)文本存儲到數(shù)據(jù)庫并進行管理
這篇文章主要為大家詳細介紹了如何使用C#實現(xiàn)將Word?轉(zhuǎn)文本存儲到數(shù)據(jù)庫并進行管理,文中的示例代碼講解詳細,需要的小伙伴可以參考一下2024-03-03
C#網(wǎng)絡(luò)協(xié)議第三方庫Protobuf的使用詳解
文章介紹了使用二進制數(shù)據(jù)傳輸?shù)谋匾?并詳細介紹了Protobuf(Protocol Buffers)的使用方法,通過將協(xié)議定義文件(.proto)轉(zhuǎn)換為C#代碼,可以方便地進行二進制數(shù)據(jù)的發(fā)送和解析2025-12-12
C#使用Spire.PDF for .NET合并多個PDF文檔和指定頁面的實現(xiàn)方案
在實際項目開發(fā)中,我們經(jīng)常會遇到需要將多個 PDF 文件合并成一個文檔的需求,或者從多個 PDF 中抽取部分頁面組合成新的 PDF,本文將介紹如何使用 Spire.PDF for .NET 庫實現(xiàn)合并多個PDF文檔和指定頁面,需要的朋友可以參考下2025-10-10

