在維護 EIP 系統的排程服務時,我想要讓 .ashx
能定期自動檢查主機或 Oracle 是否連得上。因此,我設計了一張名為 SCH_HOST_CHECK_LIST
的資料表,記錄所有要檢查的目標(IP、URL、Oracle 連線字串),並且讓程式讀取這張表後根據不同類型執行對應檢查。
一切看似順利,但我在執行 .ashx
測試時,意外發現:
雖然 SQL 在 SQL Developer 中查得到資料,
但程式卻始終回傳「查不到」。
這讓我開始排查可能的原因。
當我仔細比對查詢與權限設定時,發現:
我確實有成功下
INSERT
,但忘了COMMIT
!
Oracle 的每一個 session 都是獨立的,如果沒有明確提交,其他程式(另一個 session)是完全看不到這些資料的。這對開發測試階段來說,可能是一個很容易忽略的陷阱。
✅ 解法也很單純:記得在 SQL 編輯器或初始化資料腳本中,補上 COMMIT;
。
原本我在 TestConnection.ashx
中只用一行 SQL 測試:
string testSql = "SELECT CRDAT FROM EIP.SCH_HOST_CHECK_LIST WHERE ROWNUM = 1";
但當資料表不存在時就會報錯。因此我加入了兩個優化方向:
先檢查資料表是否存在
再檢查資料是否可讀
把 SCHEMA 與 TABLE 設為變數,方便未來共用
更新後的檢查順序如下:
string schema = "EIP";
string table = "SCH_HOST_CHECK_LIST";
string column = "CRDAT";
string checkTableSql = string.Format(
"SELECT COUNT(*) FROM ALL_TABLES WHERE OWNER = '{0}' AND TABLE_NAME = '{1}'",
schema, table);
string testSql = string.Format(
"SELECT {2} FROM {0}.{1} WHERE ROWNUM = 1",
schema, table, column);
最後印出目前登入者、資料庫名稱、呼叫主機等資訊,協助 debug。
✅ 實用的錯誤排查方式
在這次經驗中,我整理出一份「程式能執行但資料為空」的排查清單:
✔ 是否真的 COMMIT?
✔ 程式與資料庫帳號是否一致?
✔ 權限是否已授予?
✔ 使用的 DBList 與實際資料庫是否一致?
✔ SYS_CONTEXT('USERENV', ...) 是否能印出目前環境資訊?
這些簡單但實用的排查動作,幫我省下不少時間。
🤖 想讓 GPT 協助你判斷資料為何撈不到?可以這樣問:
我用 SQL 查得到資料,但程式中 da.GetDataMutilRow(sql) 為空,可能是什麼原因?
⚙️ 文末附錄:TestConnection.ashx 原始碼
<%@ WebHandler Language="C#" Class="TestConnection" %>
using System;
using System.Web;
public class TestConnection : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write("🧪 Oracle 連線測試中...\n\n");
// 可變動參數
string schema = "EIP";
string table = "SCH_HOST_CHECK_LIST";
string column = "CRDAT";
try
{
DataAccess da = new DataAccess();
// 查詢目前登入的 Session 資訊
string sqlSession = @"SELECT
SYS_CONTEXT('USERENV','SESSION_USER') AS SESSION_USER,
SYS_CONTEXT('USERENV','CURRENT_SCHEMA') AS CURRENT_SCHEMA,
SYS_CONTEXT('USERENV', 'DB_NAME') AS DB_NAME,
SYS_CONTEXT('USERENV','HOST') AS HOST,
SYS_CONTEXT('USERENV','IP_ADDRESS') AS IP
FROM DUAL";
var sessionInfo = da.GetDataOneRow(sqlSession);
context.Response.Write(string.Format("📌 資料庫登入帳號:{0}\n", sessionInfo["SESSION_USER"]));
context.Response.Write(string.Format("📌 SCHEMA:{0}\n", sessionInfo["CURRENT_SCHEMA"]));
context.Response.Write(string.Format("📌 主機的 service:{0}\n", sessionInfo["DB_NAME"]));
context.Response.Write(string.Format("📌 呼叫端主機名稱:{0}\n", sessionInfo["HOST"]));
context.Response.Write(string.Format("📌 呼叫端IP:{0}\n\n", sessionInfo["IP"]));
// 檢查資料表是否存在
string checkTableSql = string.Format(
"SELECT COUNT(*) FROM ALL_TABLES WHERE OWNER = '{0}' AND TABLE_NAME = '{1}'",
schema,
table
);
string tableExists = da.GetDataOneValue(checkTableSql);
if (tableExists == "0")
{
context.Response.Write(string.Format("❌ 資料表 {0}.{1} 不存在,請確認資料庫是否建立成功。\n", schema, table));
return;
}
else
{
context.Response.Write(string.Format("✅ 資料表 {0}.{1} 存在\n", schema, table));
}
// 測試是否能 SELECT 一筆資料
string testSql = string.Format("SELECT {2} FROM {0}.{1} WHERE ROWNUM = 1", schema, table, column);
var row = da.GetDataOneRow(testSql);
if (row.Count > 0)
{
context.Response.Write(string.Format("✔ 查到一筆 {0}.{1} 資料:{2}\n", schema, table, row[column]));
}
else
{
context.Response.Write(string.Format("⚠ 查不到 {0} 資料,請確認資料存在或權限開放。\n", table));
}
}
catch (Exception ex)
{
context.Response.Write(string.Format("❌ 發生錯誤:{0}\n", ex.Message));
}
}
public bool IsReusable { get { return false; } }
}