# C# with SQL server [SQL筆記](/Yvu8r41uQveze80lKLJvWQ)有一些相關語法、概念可供了解 (如果有需要下載SQL sever 這裡有教學可以看 https://ithelp.ithome.com.tw/m/articles/10235779 **目錄:** [TOC] ## 新增資料 ![螢幕擷取畫面 2025-02-25 103957](https://hackmd.io/_uploads/B19jTo9c1e.png) 在這即可輸入你要的行列,每行的名稱以及類型「ID,int,不允許Null」 ![螢幕擷取畫面 2025-02-25 104128](https://hackmd.io/_uploads/By91Co99kx.png) 記得要儲存以及重跑一下資料庫,就可以看到加入的資料 ![螢幕擷取畫面 2025-02-25 104551](https://hackmd.io/_uploads/H1Jb1hc5Je.png) 選取前1000、200資料列都可以手動加入資料 ![螢幕擷取畫面 2025-02-25 104743](https://hackmd.io/_uploads/rJMPy2c5ye.png) ## 建立資料庫 也可以使用新增查詢使用程式語言來新增資料庫 ```sql= CREATE DATABASE MyDatabase; ``` ### 使用資料庫 ```sql= USE MyDatabase; ``` ## 建立資料表 ```sql= CREATE TABLE Users ( UserID INT PRIMARY KEY IDENTITY(1,1), -- 自增主鍵 UserName NVARCHAR(50) NOT NULL, -- 使用 NVARCHAR 支援 Unicode Age INT CHECK (Age >= 0), -- 年齡必須大於等於 0 Email NVARCHAR(100) UNIQUE, -- Email 欄位唯一 CreatedAt DATETIME DEFAULT GETDATE() -- 預設值為當前時間 ); ``` ## 連結資料 目前看來,我常與C#進行連結,這其中大概就只有那一長串的不一樣了 以下幾種都可以連結到的 在 SSMS 內,打開 「連線屬性」 (右鍵資料庫 → 屬性 → 連線 ),可以找到伺服器名稱。 * Windows 驗證:後面為=True; * SQL Server 驗證:其中會有User Id=****;Password=****或者底部為.mdf;如此 範例: ```csharp= // 使用 Windows 驗證(Trusted Connection) string connectionString = "Server=你的伺服器名稱;Database=你的資料庫名稱;Trusted_Connection=True;"; // 使用 SQL Server 驗證(需要帳號密碼) string connectionString = "Server=你的伺服器名稱;Database=你的資料庫名稱;User Id=你的帳號;Password=你的密碼;"; ``` ### C#連結測試 ```csharp= string connectionString = "Server=localhost\SQLEXPRESS;Database=master;Trusted_Connection=True;"; using (SqlConnection conn = new SqlConnection(connectionString)) { try { conn.Open(); MessageBox.Show("✅ 成功連接到 SQL Server!"); } catch (Exception ex) { MessageBox.Show($"❌ 連線失敗:{ex.Message}"); } } ``` ## 獲取 SQL 語法 1. 開啟 SQL Server Management Studio (SSMS),並連接到你的 SQL Server。 1. 在 「物件總管」(Object Explorer) 中,找到你的 資料庫 (Database),然後展開它。 1. 在 「資料表 (Tables)」 裡找到你要查詢的表,例如 test3。 1. 右鍵點擊資料表,選擇 「Script Table as」,然後選擇你需要的 SQL 語法: * SELECT To → 產生 SELECT 查詢語法 * INSERT To → 產生 INSERT INTO 語法 * UPDATE To → 產生 UPDATE 語法 * DELETE To → 產生 DELETE 語法 5. 選擇 To New Query Editor Window,SSMS 會自動開啟一個新視窗,並顯示對應的 SQL 語法。 ![image](https://hackmd.io/_uploads/rkp0M42c1e.png) 我建議第一個用到 新增查詢編輯器視窗 ,剩下的用到 剪貼簿 ,用貼上的發法集中在一個 查詢編輯器視窗 1. CREATE : 這個指令用於創建新的資料庫物件,例如表格、索引、視圖等。 2. ALTER : 這個指令用於修改現有的資料庫物件,例如增加/刪除欄位、變更資料類型等。 1. 建立或改變為: 這個指令看起來是用於同時創建和修改資料庫物件,可能是一個複合指令。 1. DROP : 這個指令用於刪除資料庫物件,例如刪除表格、索引等。 1. DROP 並 CREATE : 這個指令看起來是先刪除再創建資料庫物件,可能是一個原子操作。 2. SELECT : 這是SQL中最常用的查詢指令,用於從數據表中選取和返回數據。它可以指定要查詢的列、過濾條件、排序方式等。 1. INSERT : 這個指令用於向數據表中插入新的記錄。可以指定要插入的列和值。 1. UPDATE : 這個指令用於更新數據表中現有的記錄。可以指定要更新的列和新的值,並添加過濾條件。 1. DELETE : 這個指令用於從數據表中刪除記錄。可以指定要刪除的記錄的過濾條件。 3. EXECUTE : 這個指令用於執行一個SQL語句或存儲過程。 ### 範例 假設你選擇 INSERT To,SSMS 可能會產生: ```sql= INSERT INTO [dbo].[test3] ([ID], [Name], [Age]) VALUES (<ID, int,>, <Name, nvarchar(50),>, <Age, int,>) ``` 你可以根據需求修改,例如: ```sql= INSERT INTO [dbo].[test3] ([ID], [Name], [Age]) VALUES (1, 'Anna', 25) ``` # C#編寫建議 ### 使用 SqlCommand 執行 SQL 建議使用 參數化查詢 ```csharp= string query = "INSERT INTO test3 (ID, Name, Age) VALUES (@id, @name, @age)"; using (SqlConnection conn = new SqlConnection("你的連線字串")) { SqlCommand cmd = new SqlCommand(query, conn); cmd.Parameters.AddWithValue("@id", 1); cmd.Parameters.AddWithValue("@name", "Anna"); cmd.Parameters.AddWithValue("@age", 25); conn.Open(); cmd.ExecuteNonQuery(); } ``` ### 注入攻擊 ❌❌ 傳統寫法 (⚠️ 不安全) 這樣的寫法 直接將變數串接進 SQL 指令,容易受到 SQL Injection (SQL 注入攻擊) 的影響 SQL Injection:是發生於應用程式與資料庫層的安全漏洞 在設計不良的程式當中忽略了字元檢查,那麼這些夾帶進去的惡意指令就會被資料庫伺服器誤認為是正常的SQL指令而執行,因此遭到破壞或是入侵。 ```csharp string query = "INSERT INTO test3 (ID, Name, Age) VALUES (" + id + ", '" + name + "', " + age + ")"; ``` ### 多行sql語法(reader開啟關閉) reader有開啟(Open)就有關閉(Close) ```csharp= //取得ID string query = @" SELECT [ID] FROM [dbo].[Account] WHERE [Username] = @Username AND [HashPassword] = @HashPassword"; using (SqlConnection conn = new SqlConnection(conn_string)) { using (SqlCommand cmd = new SqlCommand(query, conn)) { //寫入帳號密碼,這種方法有效避免sql注入風險 cmd.Parameters.AddWithValue("@Username", textBox1.Text); cmd.Parameters.AddWithValue("@HashPassword", textBox2.Text); conn.Open(); //資料庫開啟 using (SqlDataReader reader = cmd.ExecuteReader()) { if (!reader.Read()) //讀取失敗 { MessageBox.Show("帳號或密碼錯誤"); return; } //儲存帳號資訊 string id = reader["ID"].ToString(); // 關閉 reader 後執行 UPDATE reader.Close(); ////////////////////////////////////////////////////////////////////////// //更新登陸時間 string updateQuery = @" UPDATE [dbo].[Account] SET [LastUpdatedTime] = @LastUpdatedTime WHERE [ID] = @ID"; using (SqlCommand updateCmd = new SqlCommand(updateQuery, conn)) { updateCmd.Parameters.AddWithValue("@ID", id); updateCmd.Parameters.AddWithValue("@LastUpdatedTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss zzz")); updateCmd.ExecuteNonQuery(); } } } } ``` 如果要做兩個資料庫的動作,害怕**名子混在一起**,可以進行分開查詢: ```csharp= string accountId = ""; string vehicleId = ""; using (SqlConnection conn = new SqlConnection(conn_string)) { conn.Open(); // 查帳號 ID string query1 = "SELECT [ID] FROM [dbo].[Account] WHERE [Email] = @Email"; using (SqlCommand cmd1 = new SqlCommand(query1, conn)) { cmd1.Parameters.AddWithValue("@Email", email); using (SqlDataReader reader = cmd1.ExecuteReader()) { if (reader.Read()) { accountId = reader["ID"].ToString(); // 這裡的 reader["ID"] 是帳號的 ID } } } // 查車輛 ID string query2 = "SELECT [ID] FROM [dbo].[Vehicle] WHERE [AccountID] = @AccountID"; using (SqlCommand cmd2 = new SqlCommand(query2, conn)) { cmd2.Parameters.AddWithValue("@AccountID", accountId); using (SqlDataReader reader = cmd2.ExecuteReader()) { if (reader.Read()) { vehicleId = reader["ID"].ToString(); // 這裡的 reader["ID"] 是車輛的 ID } } } } ``` 如果一個sql語言中有多個表單資料: 就需要用AS特別標註 ```sql= SELECT V.ID AS VehicleID, V.PlateNumber, A.ID AS AccountID, A.Email, AWV.ID AS LinkID FROM Vehicle V --省略AS JOIN AccountWithVehicle AWV ON V.ID = AWV.VehicleID JOIN Account A ON AWV.AccountID = A.ID ``` ```csharp= using (SqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { string vehicleId = reader["VehicleID"].ToString(); string plate = reader["PlateNumber"].ToString(); string accountId = reader["AccountID"].ToString(); string email = reader["Email"].ToString(); string linkId = reader["LinkID"].ToString(); } } ``` 總結: | 原因 | 說明 | | ---------------------------------------------- | -------------- | | 每個查詢用不同 `SqlCommand` 變數(如 `cmd1`、`cmd2`) | 避免 query 與參數混用 | | 每個讀取用不同 `SqlDataReader`(如 `reader1`、`reader2`) | 分清楚是哪張資料表的結果 | | 儲存資料用不同變數(如 `accountId`、`vehicleId`) | 確保內容不會覆蓋或混淆 | | 只開一次連線 (`conn.Open()`) | 避免重複開啟導致錯誤 | --- 💡💡記得最後的最後都要加上 ```csharp= conn.Open(); cmd.ExecuteNonQuery(); //是非同步非查詢作業(例如 INSERT/UPDATE/DELETE)。 cmd.ExecuteReader() //是同步查詢作業。 ``` 如果要查詢資料庫,請勿加 **ExecuteNonQuery()** 不能「先開始一個非同步操作」然後又「同步查詢資料」! ```csharp= using(SqlDataReader reader = comm.ExecuteReader()) //會報錯:不可因為已在進行的非同步作業擱置而先執行命令。 ``` ⚠️如果結束一段SQL語法後還要接一段,記得加上 reader.Close() SqlCommand 也是得再宣告一個使用 ```csharp= reader.Close(); // 先關閉 reader,避免與 UPDATE 衝突 string query = ...; using (SqlCommand updateCmd = new SqlCommand(updateQuery, conn)){...} ``` ---- 讀取資料後可進行的編輯方式: ```csharp= using(var reader = cmd.ExecuteReader()) { if (reader.Read()) { FirstName = reader["FirstName"].ToString(); LastName = reader["LastName"].ToString(); ..... } } ``` ### 寫空的語法 ✅ 這句: ```sql= (@EngineType IS NULL OR BM.EngineType = @EngineType ``` 意思是: 如果 `@EngineType` 是 `NULL`(代表「未指定」或「全部」),那就不要套用這個篩選條件。 否則就要符合指定的 `EngineType`。 👉 適合用於參數類型為 **數值(int) 或 可為 NULL 的欄位**。 ✅ 這句: ```sql= (@BrandCode = '' OR BM.Code = @BrandCode) ``` 意思是: 如果 @`BrandCode` 是空字串 `''`(代表「全部」),那就不要篩選品牌代號。 否則就必須等於指定的 `BrandCode`。 👉 適合用於**參數類型為 字串(string)**,例如品牌代號 Code 是 varchar。 ⚖️ 差異總結 | 比較項目 | `IS NULL` 用法 | `= ''` 用法 | | --------- | ---------------------------------------------------- | ------------------------------------------ | | 適用型別 | `int`, `datetime`, `bit` 等 | `nvarchar`, `varchar` 等字串類型 | | 預設值表示「全部」 | `null` | 空字串 `""` | | C#參數要怎麼設 | `command.Parameters.AddWithValue(..., DBNull.Value)` | `command.Parameters.AddWithValue(..., "")` | | SQL 可讀性 | 適合數值與空日期 | 適合純文字比較 | 🧠 小技巧建議你這樣記: | 條件型別 | 要排除的「全部」值 | SQL條件語法 | | ------- | ------------- | ------------------------- | | 數字或可空欄位 | `null` | `(@值 IS NULL OR 欄位 = @值)` | | 字串欄位 | `""` 或 `'全部'` | `(@值 = '' OR 欄位 = @值)` | ## SELECT 查詢資料 ```sql= SELECT 欄位1, 欄位2, ... FROM 資料表名稱 WHERE 條件; ``` ```csharp= using System; using System.Data.SqlClient; class Program { static void Main() { string connectionString = "Server=你的伺服器名稱;Database=你的資料庫名稱;Trusted_Connection=True;"; string query = "SELECT ID, Name, Age FROM test3"; using (SqlConnection conn = new SqlConnection(connectionString)) { SqlCommand cmd = new SqlCommand(query, conn); conn.Open(); using (SqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) //搜尋每一筆資料 { //在這裡加入條件就可以輸出所有符合條件的資料 Console.WriteLine($"ID: {reader["ID"]}, Name: {reader["Name"]}, Age: {reader["Age"]}"); } } } } } ``` 判斷資料存在 以及 存取單筆資料庫資料 ```csharp= if (reader.Read()) // 帳戶存在 { int accountStatus = Convert.ToInt32(reader["AccountStatus"]); string firstName = reader["FirstName"].ToString(); // **檢查帳戶狀態** if (accountStatus == 2) { MessageBox.Show("帳戶已被鎖定,無法登入", "警告"); return; } } ``` 🔹 ExecuteReader():用於查詢 (SELECT) 並回傳多筆資料。 ### 查詢所有資料( * ) ```sql SELECT * FROM Users; ``` ### 只查詢 UserName 和 Password ```sql SELECT UserName, Password FROM Users; ``` ### 查詢特定使用者 WHERE ```sql SELECT * FROM Users WHERE UserName = 'Anna'; ``` ### 查詢多種條件 (AND、OR) 📌 AND 與 OR 的區別 AND:兩個條件都必須滿足,才會返回結果。 OR:只要其中一個條件成立,就會返回結果。 普通時候可以加括號。AND 和 OR 混用,就需要加括號 ```sql SELECT * FROM Users WHERE UserName = 'Anna' AND Password = '12345'; ``` (在 while 底下使用判斷式來塞選,才可以列出所有相關資料) * 查詢 Id 大於 1 的使用者 ```sql SELECT * FROM Users WHERE Id > 1; ``` ### 最後一筆資料(最大 Id 的資料)ORDER BY 🔹SELECT TOP 1 *:取 最上面 1 筆資料 排序方式 | SQL 語法 | Entity Framework 語法 | 結果 ----------------------|------------------------|-------------------------------|---------------- 升序(順序排列) | ORDER BY Id ASC | OrderBy(u => u.Id) | Id 小 → 大 降序(最新資料在前) | ORDER BY Id DESC | OrderByDescending(u => u.Id) | Id 大 → 小 ```sql SELECT TOP 1 * FROM Users ORDER BY Id DESC ``` * 獲取 最後插入的 Id IDENT_CURRENT('Users') 會回傳 最後一筆 Id,不受 SESSION 限制 ```sql SELECT IDENT_CURRENT('Users') ``` ### EXISTS 資料有無存在(最佳方法) ```csharp= string query = "SELECT CASE WHEN EXISTS (SELECT 1 FROM Users WHERE UserName = @UserName) THEN 1 ELSE 0 END"; using (SqlCommand cmd = new SqlCommand(query, conn)) { cmd.Parameters.AddWithValue("@UserName", userNameToCheck); int exists = (int)cmd.ExecuteScalar(); // 執行查詢並取得結果 if (exists == 1) Console.WriteLine("資料存在"); else Console.WriteLine("資料不存在"); } ``` 📌 優點: ✅ EXISTS 會在找到符合條件的第一筆資料後就停止,不會繼續搜尋,效能較好。 ✅ 適合用於 條件判斷(例如 IF、CASE)。 ### COUNT(*) 資料有無存在 ```csharp= string query = "SELECT COUNT(*) FROM Users WHERE UserName = @UserName"; SqlCommand cmd = new SqlCommand(query, conn); cmd.Parameters.AddWithValue("@UserName", "Anna"); int count = (int)cmd.ExecuteScalar(); if (count > 0) Console.WriteLine("資料存在"); else Console.WriteLine("資料不存在"); ``` 📌 回傳值: * 0:代表資料 不存在 * 1 或以上:代表資料 存在 (也可以查看多少資料在內ouo 📌 優點: ✅ 可計算符合條件的筆數,適合用來檢查 多筆記錄 是否存在。 📌 缺點: ❌ 即使找到第一筆,仍然會繼續搜尋,可能影響效能。 ### 輸出所有符合條件的資料 除了在輸出的時候加上if條件來寫法: ```csharp= string SELECT = "SELECT [Id], [User], [Password] FROM [dbo].[Table_1];"; using (SqlConnection conn = new SqlConnection(connectionString)) { SqlCommand comm = new SqlCommand(SELECT, conn); conn.Open(); using (SqlDataReader reader = comm.ExecuteReader()) { dataGridView1.Rows.Clear(); while (reader.Read()) { if ((reader["Id"]).ToString() == textBox1.Text || (reader["User"]).ToString() == textBox2.Text || (reader["Password"]).ToString() == textBox3.Text) //是不是有更好的方法輸出所有符合條件的資料 { dataGridView1.Rows.Add(reader["Id"], reader["User"], reader["Password"]); } } } } ``` 還可以使用: 加快速度~ ```csharp= string SELECT = "SELECT [Id], [User], [Password] FROM [dbo].[Table_1] WHERE ([Id] = @id OR @id IS NULL) AND ([User] = @user OR @user IS NULL) AND ([Password] = @password OR @password IS NULL)"; using (SqlConnection conn = new SqlConnection(connectionString)) { SqlCommand comm = new SqlCommand(SELECT, conn); // 使用參數化查詢來防止SQL注入 comm.Parameters.AddWithValue("@id", string.IsNullOrEmpty(textBox1.Text) ? (object)DBNull.Value : textBox1.Text); comm.Parameters.AddWithValue("@user", string.IsNullOrEmpty(textBox2.Text) ? (object)DBNull.Value : textBox2.Text); comm.Parameters.AddWithValue("@password", string.IsNullOrEmpty(textBox3.Text) ? (object)DBNull.Value : textBox3.Text); conn.Open(); using (SqlDataReader reader = comm.ExecuteReader()) { dataGridView1.Rows.Clear(); while (reader.Read()) { dataGridView1.Rows.Add(reader["Id"], reader["User"], reader["Password"]); } } } ``` ## INSERT 新增資料 ```csharp= using System; using System.Data.SqlClient; class Program { static void Main() { string connectionString = "Server=你的伺服器名稱;Database=你的資料庫名稱;Trusted_Connection=True;"; string query = "INSERT INTO test3 (ID, Name, Age) VALUES (@id, @name, @age)"; using (SqlConnection conn = new SqlConnection(connectionString)) { SqlCommand cmd = new SqlCommand(query, conn); cmd.Parameters.AddWithValue("@id", 2); cmd.Parameters.AddWithValue("@name", "Bob"); cmd.Parameters.AddWithValue("@age", 30); conn.Open(); int rowsAffected = cmd.ExecuteNonQuery(); Console.WriteLine($"成功新增 {rowsAffected} 筆資料"); } } } ``` 🔹 ExecuteNonQuery():用於執行 INSERT、UPDATE、DELETE,回傳影響的資料筆數。 ### 小小總結 用途 | SQL 語法 | C# (ADO.NET) | C# (Entity Framework) ------------------|-------------------------------------------------|-------------------------------------------|------------------------------------------- 新增單筆資料 | INSERT INTO Users (User, Password) VALUES ('Anna', '123456'); | 使用 cmd.ExecuteNonQuery(); | db.Users.Add(user); db.SaveChanges(); 新增多筆資料 | INSERT INTO Users (User, Password) VALUES ('A', '1'), ('B', '2'); | 需使用迴圈插入 | 需使用迴圈插入 取得 Id | OUTPUT INSERTED.Id | (int)cmd.ExecuteScalar(); | db.Users.Add(user); db.SaveChanges(); user.Id; ## UPDATE 更新資料(修改) ```csharp= string query = "UPDATE test3 SET Name = @name WHERE ID = @id"; using (SqlConnection conn = new SqlConnection(connectionString)) { SqlCommand cmd = new SqlCommand(query, conn); cmd.Parameters.AddWithValue("@name", "Charlie"); cmd.Parameters.AddWithValue("@id", 2); conn.Open(); int rowsAffected = cmd.ExecuteNonQuery(); Console.WriteLine($"成功更新 {rowsAffected} 筆資料"); } ``` ### 語法說明 條件與想改變的資料可為同一欄 ( 將 Password 為 '123' 的使用者,更新為 '54321' ) ```sql= UPDATE 資料表名稱 SET 欄位1 = 值1, 欄位2 = 值2, ... WHERE 條件; ``` * 範例 有一個Anna資料在Id-1,想改變Password 成 '54321' 💡舊的資料: | Id | UserName | Password | |----|----------|----------| | 1 | Anna | 12345 | | 2 | Bob | 67890 | ```sql= UPDATE Users SET Password = '54321' WHERE Id = 1; ``` 💡新的資料: | Id | UserName | Password | |----|----------|----------| | 1 | Anna | 54321 | | 2 | Bob | 67890 | ## DELETE 刪除資料 ```csharp= string query = "DELETE FROM test3 WHERE ID = @id"; using (SqlConnection conn = new SqlConnection(connectionString)) { SqlCommand cmd = new SqlCommand(query, conn); cmd.Parameters.AddWithValue("@id", 2); conn.Open(); int rowsAffected = cmd.ExecuteNonQuery(); Console.WriteLine($"成功刪除 {rowsAffected} 筆資料"); } ``` ### 刪除所有資料 ```sql DELETE FROM Users ``` ### 刪除整個表 (結構+資料) ```sql DROP TABLE Users ``` ### 刪除指定的那一行 刪除與該 Id 同一行的 Name 和 Password 不影響其他相同 Name 和 Password 的行 ```sql DELETE FROM Users WHERE Id = @Id AND Name = (SELECT Name FROM Users WHERE Id = @Id) AND Password = (SELECT Password FROM Users WHERE Id = @Id); ``` ## SqlConnection 📌 SqlConnection 主要用途 * 建立與 SQL Server 資料庫的連線 * 執行 SQL 查詢(SELECT、INSERT、UPDATE、DELETE) * 管理資料庫連線的開啟 (Open()) 和關閉 (Close()) Dispose() 釋放 SqlConnection 佔用的資源 State 取得當前連線狀態("Open"、"Closed" 等) * 配合 SqlCommand 來執行 SQL 語句 * 與 SqlDataReader 或 SqlDataAdapter 搭配讀取資料 ## SqlCommand 需要注意的地方 ✅ 使用 using 確保自動釋放資源 ✅ 使用參數化查詢 (@參數) 避免 SQL Injection ✅ 確保 SqlConnection 連線字串正確,否則會無法連線 ✅ 如果 SQL Server 使用帳密登入,需要加上 User ID 和 Password 📌SqlCommand 是 C# 用來執行 SQL 指令 的物件,通常與 SqlConnection 搭配使用,能夠執行: * 查詢 (SELECT) → 取得資料 * 新增 (INSERT)、修改 (UPDATE)、刪除 (DELETE) → 操作資料 * 執行儲存程序 (Stored Procedure) * 執行資料庫指令 (ALTER TABLE, CREATE TABLE...等) ### 小小總結 * SqlCommand 用來執行 SQL 指令 * ExecuteReader():執行 SELECT,回傳 SqlDataReader * ExecuteNonQuery():執行 INSERT, UPDATE, DELETE,回傳影響的資料筆數 * ExecuteScalar():執行 COUNT(*) 或單一數值查詢 * 可搭配參數 (@參數) 避免 SQL Injection * 可執行儲存程序 (Stored Procedure) | 方法 | 作用 | |----------------------|--------------------------------------------| | ExecuteReader() | 回傳查詢結果 (SELECT),用 SqlDataReader 讀取 | | ExecuteNonQuery() | 執行 INSERT、UPDATE、DELETE,回傳影響的資料筆數 | | ExecuteScalar() | 回傳單一值(例如 COUNT(*)) | ## SqlDataReader SqlDataReader 是 C# 與 SQL Server 之間的資料讀取工具,專門用來 逐行讀取 SQL 查詢結果,適用於大量資料的高效讀取。 ### 特性 1. 只能前向讀取(Forward-Only) 📜 不能回頭,只能逐行往下讀。 2. 串流讀取(Streaming) 🚀 不會一次載入所有資料到記憶體,適合處理大量資料。 3. 唯讀(Read-Only) 🔒 不能修改資料,只能讀取。 4. 效能較快 🏎️ 因為是串流讀取,比 DataSet、DataTable 省記憶體。 ### 注意事項 * reader.Read() 回傳 true 表示還有資料可以讀取,會移動到下一行。 * 資料類型要匹配,例如 GetInt32(0) 代表該欄位應該是 int。 * 欄位索引從 0 開始(第一欄是 0,第二欄是 1,依此類推)。 ### 使用時機 ✅ 當你只需要讀取資料,而且不會修改。 ✅ 當資料量大時,避免一次載入全部到記憶體。 ✅ 當需要即時顯示資料,如讀取資料時同時寫入畫面。 ### 與DataTable 或 DataSet差別 如果需要修改、排序、篩選,請使用 DataTable 或 DataSet。 | 項目 | SqlDataReader | DataTable / DataSet | |--------------|-----------------------------|-------------------------------| | 適用場景 | 大量資料讀取(只讀) | 需要修改、過濾、排序 | | 效能 | 較快(逐行讀取) | 較慢(會載入全部資料) | | 記憶體使用 | 低(只存當前行) | 高(全部載入) | | 移動方式 | 只能往下讀 | 可隨意跳行 | # 資料匯出匯入 這個老師整理得很好!! [SQL Server資料匯入與匯出](https://www.youtube.com/watch?v=C2jEhvT0ha8&ab_channel=%E8%AC%9D%E9%82%A6%E6%98%8C) ![image](https://hackmd.io/_uploads/By3K1ZJoyx.png) # 作者書寫測試檔案 存於github,壓縮檔案類型[sql_test](https://github.com/Anna0212Anna0212/sql_test) C#檔案直接推送檔案[sql_test2.0](https://github.com/Anna0212Anna0212/sql_test2.0) 資料庫檔案都做成了Excel在檔案當中可做參考