# SQL
喔天 超複雜的東東
>Thx ChatGPT~
[TOC]
## 新增資料

在這即可輸入你要的行列,每行的名稱以及類型「ID,int,不允許Null」

記得要儲存以及重跑一下資料庫,就可以看到加入的資料

選取前1000、200資料列都可以手動加入資料

常見權限問題:
----
右鍵資料庫
屬性 > 檔案 > 擁有者 > 新增目前使用者

## 建立資料庫
也可以使用新增查詢使用程式語言來新增資料庫
1. 開啟 SSMS → 新建查詢
1. 建立資料庫 → CREATE DATABASE MyStore;
1. 切換資料庫 → USE MyStore;
1. 建立資料表 → CREATE TABLE Customers (...);
1. 插入資料 → INSERT INTO Customers (...) VALUES (...);
1. 查詢資料 → SELECT * FROM Customers;
```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() -- 預設值為當前時間
);
```
### 設定主鍵
主鍵的特點:
* 唯一性(Unique):主鍵欄位內的值不能重複。
* 非空性(Not NULL):主鍵欄位的值不能為空(NULL)。
* 一個表只能有一個主鍵,但可以由 單個欄位或多個欄位組合 而成。
同一筆訂單 (OrderID) 不能有 重複的產品 (Product)。
```sql=
CREATE TABLE Orders (
OrderID INT,
CustomerName NVARCHAR(50),
Product NVARCHAR(100),
PRIMARY KEY (OrderID, Product) -- 設定 OrderID 和 Product 為「複合主鍵」
);
```
### 設定外鍵
🔹 透過「外鍵(FOREIGN KEY)」建立連結
🔹 假設我們有兩張表:
* Customers(客戶表)
* Orders(訂單表)
* Orders.CustomerID 參考 Customers.CustomerID,建立連結
```sql=
CREATE TABLE Customers (
CustomerID INT PRIMARY KEY, -- 主鍵
Name VARCHAR(50) NOT NULL
);
CREATE TABLE Orders (
OrderID INT PRIMARY KEY, -- 訂單的主鍵
CustomerID INT, -- 這是外鍵
OrderAmount DECIMAL(10,2),
-- 這條語法建立「連結」:
FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);
```
✅ 這樣 Orders 的 CustomerID 一定要對應 Customers 的 CustomerID,不能輸入不存在的客戶!
#### 💡💡連結的其他功能
當在進行 刪除或更新時 ,與你連結的那個資料也會一起進行
| 設定 | 效果 |
|--------------------------|------------------------------------------------------------|
| ON DELETE CASCADE | 如果刪除 Customers 裡的 CustomerID = 1,則 Orders 裡所有 CustomerID = 1 的訂單也會被刪除。 |
| ON DELETE SET NULL | 如果刪除 Customers.CustomerID = 1,則 Orders.CustomerID 會變成 NULL。 |
| ON UPDATE CASCADE | 如果 Customers.CustomerID = 1 改成 2,則 Orders.CustomerID = 1 也會自動變 2。 |
📌 這樣就能確保數據的正確性,避免孤立的資料!
### 主鍵 和 外鍵
* 主鍵 (PRIMARY KEY)
🔹 主鍵 是 表中的唯一識別碼,用來唯一標識每一筆資料。
🔹 一個表只能有一個主鍵,但主鍵可以包含多個欄位(複合鍵)。
🔹 主鍵的值不能重複,也不能是 NULL!
💡 範例:建立 Customers 表並設定 CustomerID 為主鍵
```sql=
CREATE TABLE Customers (
CustomerID INT PRIMARY KEY, -- 主鍵
Name VARCHAR(50) NOT NULL,
Email VARCHAR(100) UNIQUE
);
```
✅ CustomerID 是主鍵,代表每個客戶的唯一編號,不可重複或為 NULL。
* 外鍵 (FOREIGN KEY)
🔹 外鍵 是 參考另一個表的主鍵,**建立兩個表之間的關聯**。
🔹 外鍵的值必須來自於它所參考的主鍵,這樣才能確保數據的正確性!
🔹 外鍵可以有多個(同一個表內可以有多個外鍵)。
💡 範例:建立 Orders 表,並用 CustomerID 作為外鍵
```sql=
CREATE TABLE Orders (
OrderID INT PRIMARY KEY, -- 訂單主鍵
CustomerID INT, -- 這是外鍵
OrderAmount DECIMAL(10,2),
-- 設定 `CustomerID` 為外鍵,參考 `Customers` 表的 `CustomerID`
FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);
```
✅ 這樣 Orders 表中的 CustomerID 一定要對應 Customers 表的 CustomerID,否則 SQL Server 會拒絕插入數據!
💡外鍵的作用
🔹 確保資料的完整性
* 避免無效的資料寫入
🔹 建立關聯
* 讓不同表之間能夠關聯起來
🔹 防止刪除或更新錯誤
* 可以設定刪除或更新時的行為
### 主鍵 和 外鍵 的關係
| 名稱 | 說明 | 限制 |
|--------------------|----------------------------------------|------------------------------------------|
| 主鍵 (PRIMARY KEY) | 唯一標識表中的每一行數據 | 不能重複,不能為 NULL |
| 外鍵 (FOREIGN KEY) | 參考另一個表的 PRIMARY KEY,用來建立關聯 | 必須是另一個表的 PRIMARY KEY 中的值,或者為 NULL(除非 NOT NULL 限制) |
### 📌 總結
| 功能 | PRIMARY KEY(主鍵) | FOREIGN KEY(外鍵) |
|--------------------|--------------------------------------------------|-------------------------------------------------|
| 作用 | 唯一識別該表內的每一筆數據 | 參考另一張表的 PRIMARY KEY,建立關聯 |
| 是否唯一? | ✅ 是,不能重複 | ❌ 不是,可以重複(但必須是另一表的 PRIMARY KEY 值) |
| 是否可為 NULL? | ❌ 不可以 | ✅ 可以(除非 NOT NULL 限制) |
| 一張表可以有幾個? | 只能有一個 | 可以有多個 |
| 常見用途 | 客戶編號 (CustomerID)、訂單編號 (OrderID) | 訂單的 CustomerID 參考 Customers 表 |
## 連結資料
目前看來,我常與C#進行連結,這其中大概就只有那一長串的不一樣了
以下幾種都可以連結到的
* Windows 驗證:後面為=True;
* SQL Server 驗證:其中會有User Id=****;Password=****或者底部為.mdf;如此
範例:
```csharp=
// 使用 Windows 驗證(Trusted Connection)
string connectionString = "Server=localhost;Database=YourDatabase;Trusted_Connection=True;";
// 使用 SQL Server 驗證(需要帳號密碼)
string connectionString = "Server=localhost;Database=YourDatabase;User Id=sa;Password=YourPassword;";
```
### 連結測試
C#測試方法
```csharp=
string connectionString = "超長一串ㄟ";
using (SqlConnection conn = new SqlConnection(connectionString))
{
try
{
conn.Open();
MessageBox.Show("✅ 成功連接到 SQL Server!");
}
catch (Exception ex)
{
MessageBox.Show($"❌ 連線失敗:{ex.Message}");
}
}
```
### SQL server
在新增查詢裡撰寫:USE MyDatabase(資料表名稱);
SQL 觸發器範例
```sql=
USE MyDatabase; -- 確保操作的資料庫是正確的
CREATE TRIGGER trg_AfterInsertOrder
ON Orders
AFTER INSERT
AS
BEGIN
PRINT '新的訂單已插入!'; -- 觸發時顯示訊息
END;
```
✅ 當 Orders 表新增資料時,這個觸發器就會自動執行!
## 獲取 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 語法。

我建議第一個用到 新增查詢編輯器視窗 ,剩下的用到 剪貼簿 ,用貼上的發法集中在一個 查詢編輯器視窗
### 範例
假設你選擇 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)
```
## 創建資料語法
* NEWID() 隨機生成一個ID:96833a93-1924-4cd2-93af-0e7354723d79
* SYSDATETIMEOFFSET() 生成一個時間:2022-11-11 08:20:15 -07:00
## 插入(新增)資料
```sql=
INSERT INTO Users (UserName, Age, Email)
VALUES ('小明', 25, 'xiaoming@example.com');
```
### 插入多筆資料
```sql=
INSERT INTO Users (UserName, Age, Email)
VALUES
('小華', 30, 'xiaohua@example.com'),
('小美', 28, 'xiaomei@example.com');
```
### 關聯性的插入資料
這樣可以確保 TicketID 和 MuseumID 在相應的表中已經存在,避免外鍵錯誤。
```sql=
INSERT INTO AdmissionRecord (ID, TicketID, MuseumID, RecordTime, IsRejected)
VALUES (NEWID(), (SELECT TOP 1 TicketID FROM Tickets), (SELECT TOP 1 MuseumID FROM Museums), SYSDATETIMEOFFSET(), 0);
```
## 查詢資料
### 查詢所有欄位
```sql=
SELECT * FROM Users;
```
### 查詢特定欄位
```sql=
SELECT UserName, Age FROM Users;
```
### 查詢多種條件
📌 AND 與 OR 的區別
AND:兩個條件都必須滿足,才會返回結果。
OR:只要其中一個條件成立,就會返回結果。
普通時候可以加括號。AND 和 OR 混用,就需要加括號
```sql
SELECT * FROM Users WHERE UserName = 'Anna' AND Password = '12345';
```
### 只查詢 UserName 和 Password
```sql
SELECT UserName, Password FROM Users;
```
### WHERE 使用條件查詢
```sql=
SELECT * FROM Users WHERE Age > 25;
```
### 使用 LIKE 模糊查詢
```sql=
SELECT * FROM Users WHERE UserName LIKE '小%'; -- 找出名字以「小」開頭的使用者
```
### JOIN 和 ON
[SQL server:JOIN_ON](/mgbIC7QqSh2G9l3p5yq8Dw)這裡有一些說明
查詢 Orders(訂單表)和 Users(使用者表)中的資料,根據 UserID 這個欄位來進行關聯。
```sql=
SELECT * FROM Orders JOIN Users ON Orders.UserID = Users.UserID;
```
🔹 JOIN:用來合併多張表
🔹 ON:指定關聯條件
### 特定條件搜尋
----
✅ 正確寫法 — 使用 JOIN:
假設你想要從 BatteryChargeRecord 中查出 ExpectedMaxCapacity,前提是這筆資料的 VehicleID 和 Vehicle 資料表中的 ID 對應。
你應該使用 INNER JOIN,例如:
```SQL=
SELECT B.[ExpectedMaxCapacity]
FROM [Zyberion_Full].[dbo].[BatteryChargeRecord] AS B
JOIN [Zyberion_Full].[dbo].[Vehicle] AS V
ON B.[VehicleID] = V.[ID]
```
這樣就可以正確地將兩張表結合起來,並且查出符合條件的 ExpectedMaxCapacity。
❓延伸補充:你可能想要加條件(例如特定車輛名稱)
如果你想查某輛特定車的資料,可以再加條件,例如:
```sql=
SELECT B.[ExpectedMaxCapacity]
FROM [Zyberion_Full].[dbo].[BatteryChargeRecord] AS B
JOIN [Zyberion_Full].[dbo].[Vehicle] AS V
ON B.[VehicleID] = V.[ID]
WHERE V.[Name] = 'Z-Car-01'
```
### 排序 ASC、DESC
💡使用 SQL 查詢排序非主鍵欄位時,其他欄位的資料也會「一併」隨之排序。
ORDER BY 用來對查詢結果進行排序,可以 升序(遞增)或 降序(遞減)排列。
在 ORDER BY 裡不能使用 AND,因為 AND 是條件運算符(通常用在 WHERE 裡),而 ORDER BY 應該用 ,(逗號)來排序多個欄位。
```sql=
SELECT * FROM Users ORDER BY Age DESC; -- 依照年齡遞減排序
```
🔹 ORDER BY:排序查詢結果
🔹 ASC(預設):遞增(小→大)
🔹 DESC:遞減(大→小)
### ORDER BY 列出相同的資料
```sql=
SELECT [Orders].[OrderID]
,[Users].[Name]
,[Orders].[Product]
FROM [dbo].[Users]
JOIN [dbo].[Orders] ON [Orders].[UserID] = [Users].[UserID]
ORDER BY [Users].[Name]; -- 讓相同 Name 排在一起
```
### ROW_NUMBER() 多少到多少
ROW_NUMBER() 會根據 ORDER BY Id 重新排序,然後分配行號。
可以篩選特定行號,例如:
```sql=
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY Id) AS RowNum
FROM Users
) AS TempTable
WHERE RowNum > 5 AND RowNum <= 10; -- 取得第 6 到 10 筆資料
```
### GROUP BY計算使用
在 SQL 裡面使用 聚合函數(像是 MAX()、MIN()、AVG()、SUM()、COUNT() 等),就必須使用 GROUP BY
#### 基本語法
```sql=
SELECT 分組欄位, 聚合函數(...)
FROM 資料表
GROUP BY 分組欄位
```
#### 範例說明
假設你有一張 BatteryChargeRecord 表如下:
| VehicleID | ExpectedMaxCapacity |
| --------- | ------------------- |
| V001 | 98 |
| V001 | 95 |
| V002 | 92 |
| V002 | 96 |
你想要查出每台車目前曾出現過的 最大容量:
```sql=
SELECT VehicleID, MAX(ExpectedMaxCapacity) AS MaxCap
FROM BatteryChargeRecord
GROUP BY VehicleID
```
結果會是:
| VehicleID | MaxCap |
| --------- | ------ |
| V001 | 98 |
| V002 | 96 |
#### 更多用法
| 函數 | 用法 | 說明 |
| ---------- | ---- | -------- |
| `COUNT(*)` | 計算筆數 | 每一組的資料筆數 |
| `SUM(欄位)` | 加總 | 每一組的總和 |
| `AVG(欄位)` | 平均值 | 每一組的平均值 |
| `MAX(欄位)` | 最大值 | 每一組的最大值 |
| `MIN(欄位)` | 最小值 | 每一組的最小值 |
### ROWNUM 前幾筆資料
* ROWNUM 是 Oracle 專有 的行號 (Row Number) 欄位。
* 它會在 查詢結果返回時 依序從 1 開始自動編號。
* 無法在 ORDER BY 之後使用,因為行號是在排序前就分配好的!
```csharp=
SELECT ROWNUM, Users.*
FROM Users
WHERE ROWNUM <= 5; -- 取前 5 筆資料
```
### CASE 自訂排序規則
CASE 讓 VIP 使用者排在最前面
```sql=
SELECT * FROM Users
ORDER BY
CASE
WHEN UserType = 'VIP' THEN 1
WHEN UserType = 'Regular' THEN 2
ELSE 3
END,
Age ASC;
```
1. UserType = 'VIP' 的排最前
1. UserType = 'Regular' 的排第二
1. 其他使用者排最後
1. 同類型內,再按 Age 遞增排序
----
方法二:
如果資料=這個條件,就顯示如何
```sql=
CASE EngineType WHEN '1' THEN 'gasoline or hybrid' ELSE 'pure electric' END AS EngineTypeDisplay
➜ 加了一個轉換後的新欄位叫 EngineTypeDisplay
CASE BrandCode WHEN 'L7' THEN 'V' ELSE 'X' END AS Category
➜ 同樣新增了 Category 欄位
```
### 隨機排序
Users 表的資料將 隨機排列(每次查詢結果都不同)
```sql=
SELECT * FROM Users
ORDER BY NEWID();
```
### TOP 前幾筆資料
但 TOP 不能用於 分頁查詢,所以 SQL Server 2012 之後推薦使用 OFFSET FETCH。
```sql=
SELECT TOP 5 * FROM Users ORDER BY Age DESC;
```
### 後幾筆資料
SQL Server 沒有直接提供 OFFSET 來取得最後幾筆,但我們可以透過 ORDER BY + DESC + FETCH 來達成。
| 方法 | 方式 | 優點 | 缺點 |
|-------------------------------|----------------------------|-------------------------|--------------------------|
| ORDER BY DESC + FETCH | 先倒序,再取前 N 筆 | 簡單直覺 | 需要再反向排序 |
| COUNT(*) + OFFSET | 計算總筆數,然後 OFFSET | 不依賴 ID | 需要多一次查詢 |
| TOP + ORDER BY DESC | 直接取最後 N 筆 | 執行效能好 | 需要子查詢來調整順序 |
### 分頁查詢
```sql=
SELECT *
FROM Users
ORDER BY Id ASC
OFFSET 10 ROWS -- 跳過前 10 筆
FETCH NEXT 5 ROWS ONLY; -- 取出 5 筆
```
* OFFSET 10 ROWS → 跳過前 10 筆資料
* FETCH NEXT 5 ROWS ONLY → 取 5 筆資料
| 關鍵字 | 作用 |
|----------------------------|---------------------------|
| OFFSET X ROWS | 跳過 X 筆資料,X 為要跳過的行數 |
| FETCH NEXT Y ROWS ONLY | 取得 Y 筆資料,Y 為要取出的行數 |
### 最後一筆資料(最大 Id 的資料)
🔹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 或以上:代表資料 存在
---
✅優點:
可計算符合條件的筆數,適合用來檢查 多筆記錄 是否存在。
❌缺點:
即使找到第一筆,仍然會繼續搜尋,可能影響效能。
## 更新資料(修改資料)
⚠️⚠️不能直接修改 PRIMARY KEY,但可以間接修改!
因為 PRIMARY KEY 是唯一識別一筆資料的欄位,不能有重複值,直接 UPDATE 可能會影響關聯性。
如果真的要修改主鍵,通常有兩種方法:
* 刪除舊的主鍵,然後新增新值(適用於沒有外鍵關聯的情況)
* 關閉外鍵約束(Foreign Key Constraint),修改後再開啟(適用於有關聯的情況)
```sql=
UPDATE Users
SET Age = 26
WHERE UserName = '小明';
```
### 修改所有資料
(⚠️ 謹慎使用!)
```sql=
UPDATE Users
SET Age = 25;
```
⚠️🚨 這會將 Users 表的 Age 欄位全部改為 25!
一定要加上 WHERE 限制影響範圍。
### UPDATE 搭配 CASE 條件
```sql=
UPDATE Users
SET Age = CASE
WHEN UserID = 1 THEN 30
WHEN UserID = 2 THEN 25
ELSE Age -- 其他的保持不變
END;
```
* UserID = 1 的 Age 改為 30
* UserID = 2 的 Age 改為 25
* 其他 UserID 的 Age 不變
### 搭配 JOIN(更新關聯表)
```sql=
UPDATE Users
SET Users.Age = Orders.NewAge
FROM Users
JOIN Orders ON Users.UserID = Orders.UserID
WHERE Orders.OrderID = 1001;
```
* 找到 Orders 表中 OrderID = 1001 的 UserID
* 用 Orders 表的 NewAge 更新 Users 表的 Age
### 搭配 EXISTS
只更新存在的資料
只更新「有下過訂單」的使用者,把 Status 改為 'Active'
```sql=
UPDATE Users
SET Status = 'Active'
WHERE EXISTS (
SELECT 1 FROM Orders WHERE Orders.UserID = Users.UserID
);
```
* 只顯示 有下過訂單 (Orders 表有對應 UserID) 的 Users
* EXISTS 只要有回傳結果就會是 TRUE,不管 SELECT 選的是什麼(SELECT 1 只是代表查詢有結果)
### 限制更新筆數
只更新 前 5 筆 的 Users,把 Age 改成 30。
```sql=
UPDATE TOP (5) Users
SET Age = 30;
```
### ⚠️常見錯誤預防
忘記加 WHERE → 所有資料都會被更新
* 用 SELECT 先測試
先確認影響的資料,再執行 UPDATE。
```sql=
SELECT * FROM Users WHERE UserID = 1;
```
* 更新後還原
如果沒有 BACKUP,可以透過 TRANSACTION 來保護
```sql=
BEGIN TRANSACTION;
UPDATE Users SET Age = 30 WHERE UserID = 1;
ROLLBACK; -- 取消變更
```
如果確定沒問題:
```sql=
COMMIT; -- 確認變更
```
## DELETE 刪除資料
```sql=
DELETE FROM Users WHERE UserName = '小華';
```
### FROM 刪除所有資料(但不刪表)
```sql=
DELETE FROM Users;
```
### TRUNCATE 刪除並重置 ID(清空表)
```sql=
TRUNCATE TABLE Users;
```
### DROP 刪除表
```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);
```
## 索引(Index)
索引(Index)是什麼?
* 沒有索引:每次查詢時,資料庫需要 掃描整個表(Full Table Scan),效率較低。
* 有索引:資料庫會 快速定位到對應的資料行,查詢速度提升。
### 索引設定:
```sql=
CREATE INDEX idx_users_age ON Users(Age);
```
Users 表的 Age 欄位上建立索引、索引名稱為 idx_users_age、加快以 Age 為條件的查詢
----
#### ❌沒有索引時
假設 Users 表有 100 萬筆 用戶資料:
```sql=
SELECT * FROM Users WHERE Age = 25;
```
1. 從第一筆資料開始 逐行掃描(Full Table Scan)
1. 檢查 Age 是否等於 25
1. 若找到匹配的資料,才返回結果
這種查詢方式 速度較慢,尤其當資料量很大時,效能會大幅降低。
#### ✅ 有索引後
當我們執行:
```sql=
CREATE INDEX idx_users_age ON Users(Age);
```
* SQL Server 會建立一個索引表(像目錄一樣)
* 這樣查詢 Age = 25 時,資料庫可以 直接找到符合條件的資料,而不是從頭掃描整個表,大幅提升查詢效率。
再執行:
```sql=
SELECT * FROM Users WHERE Age = 25;
```
資料庫會:
1. 先檢查索引 idx_users_age
1. 直接找到 Age = 25 的資料行位置
1. 快速返回結果
這樣可以 節省大量的查詢時間!!
----
### 使用時機
#### ✅ 適合使用索引的情境
經常作為 WHERE 條件的欄位
```sql=
SELECT * FROM Users WHERE Age = 30;
```
JOIN 操作時的欄位
```sql=
SELECT * FROM Orders JOIN Users ON Orders.UserID = Users.UserID;
```
ORDER BY 或 GROUP BY 的欄位
```sql=
SELECT Age, COUNT(*) FROM Users GROUP BY Age;
```
#### ❌ 不適合索引的情境
1. 經常更新 (UPDATE) 或刪除 (DELETE) 的欄位
索引需要同步更新,頻繁變更的欄位可能影響效能。
2. 小型表格
如果表格只有幾百筆資料,索引效益不大,反而佔用額外空間。
3. 太多索引
過多的索引會影響 插入(INSERT)與更新(UPDATE) 效率。
----
### 其他類型
#### 1️⃣ 唯一索引(Unique Index)
```sql=
CREATE UNIQUE INDEX idx_users_email ON Users(Email);
```
📌 效果:
* 不能插入 重複的 Email
* 查詢 Email 時更快
---
#### 2️⃣ 複合索引(Composite Index)
```sql=
CREATE INDEX idx_users_name_age ON Users(Name, Age);
```
📌 效果:
查詢 Name 和 Age 時,效能更快:
```sql=
SELECT * FROM Users WHERE Name = '小明' AND Age = 25;
```
### 總結
🔹 CREATE INDEX idx_users_age ON Users(Age);
✔ 在 Age 欄位建立索引,加速查詢
✔ 適合 WHERE 條件中常用的欄位
✔ 可以改善 ORDER BY 或 GROUP BY 的效能
✔ 適用於大規模資料表(幾十萬筆以上)
📌 但要小心:
* 不要在 經常更新的欄位 建立索引,會影響效能
* 索引會佔用額外空間
* 不是每個欄位都需要索引,應該根據實際需求來建立
索引就像一本書的目錄,用得好可以加快查詢速度,但用太多反而會影響資料庫效能
## 建立視圖(View)
```sql=
CREATE VIEW AdultUsers AS
SELECT * FROM Users WHERE Age >= 18;
```
### 使用
```sq=
SELECT * FROM AdultUsers;
```
## 存儲程序(Stored Procedure)
💡💡存儲程序(SP)是什麼? 事先寫好的 SQL 指令集合,像是 SQL 版本的函式。
| 優點 | 說明 |
|--------------------------|------------------------------------------------------------|
| 提高執行效能 | SQL Server 會預先編譯 SP,避免每次執行時重新解析 SQL。 |
| 降低重複寫 SQL 的成本 | 只要呼叫 SP,不用每次都重新寫 SQL 指令,程式碼更整潔。 |
| 增加安全性 | 可以設定**權限**🔐,限制哪些使用者能執行存儲程序。 |
| 支援參數與邏輯運算 | SP 可以接收輸入參數、進行計算、回傳結果,類似程式語言的函式。 |
| 減少資料庫與應用程式的流量 | 只要傳送 SP 名稱和參數,不需要傳整段 SQL 指令,減少伺服器負擔。 |
### 建立一個查詢用戶的存儲程序
假設有一個 Users 表:
```sql=
CREATE TABLE Users (
UserID INT PRIMARY KEY,
Name VARCHAR(100),
Age INT
);
```
建立一個存儲程序,讓使用者透過 UserID 查詢姓名和年齡:
```sql=
CREATE PROCEDURE GetUserInfo
@UserID INT -- 定義輸入參數
AS
BEGIN
-- 查詢指定 UserID 的使用者資料
SELECT Name, Age
FROM Users
WHERE UserID = @UserID;
END;
```
📌 這樣 GetUserInfo 就變成 SQL Server 裡的一個「函式」,可以隨時呼叫!
---
💠執行
💡💡當輸入我們函式名稱:GetUserInfo,就會執行查詢的步驟
```sql=
EXEC GetUserInfo @UserID = 1;
```
如果 UserID = 1,就會回傳該用戶的 Name 和 Age。
### 傳回多個結果
* 範例:傳回用戶總數
```sql=
CREATE PROCEDURE GetUserCount
@TotalUsers INT OUTPUT -- 定義輸出參數
AS
BEGIN
SELECT @TotalUsers = COUNT(*) FROM Users; -- 計算總用戶數
END;
```
* 執行 SP 並取得 OUTPUT 值
```sql=
DECLARE @Count INT;
EXEC GetUserCount @TotalUsers = @Count OUTPUT;
PRINT '總用戶數: ' + CAST(@Count AS VARCHAR);
```
📌這樣可以回傳 Users 表內的總筆數!
----
### IF、WHILE 邏輯
存儲程序可以像程式一樣,使用 IF 判斷式或 WHILE 迴圈。
🔹 範例:根據年齡篩選用戶
```sql=
CREATE PROCEDURE GetUsersByAge
@MinAge INT,
@MaxAge INT
AS
BEGIN
IF @MinAge > @MaxAge
BEGIN
PRINT '錯誤: 最小年齡不能大於最大年齡';
RETURN;
END
-- 查詢符合年齡範圍的使用者
SELECT * FROM Users WHERE Age BETWEEN @MinAge AND @MaxAge;
END;
```
🔹 執行:
```sql=
EXEC GetUsersByAge @MinAge = 18, @MaxAge = 30;
```
📌 這樣可以避免錯誤輸入,並確保查詢條件正確!
---
### 修改或刪除存儲程序
🔹 修改存儲程序
使用 ALTER PROCEDURE 來修改:
```sql=
ALTER PROCEDURE GetUserInfo
@UserID INT
AS
BEGIN
SELECT Name, Age FROM Users WHERE UserID = @UserID;
PRINT '查詢完成'; -- 加入提示訊息
END;
```
🔹 刪除存儲程序
```sql=
DROP PROCEDURE GetUserInfo;
```
## 存儲程序權限設定
GRANT、REVOKE、DENY 來管理 存儲程序 的執行權限,確保只有**特定使用者**能夠執行、修改或刪除存儲程序
| 權限 | 作用 |
|--------------------|--------------------------------------|
| EXECUTE | 執行存儲程序 |
| ALTER | 修改存儲程序 |
| CONTROL | 擁有完整的控制權,包括修改、刪除 |
| TAKE OWNERSHIP | 允許變更擁有者 |
| VIEW DEFINITION | 允許查看存儲程序的結構,但不能執行 |
### 如何設定
🔹 允許某個使用者執行存儲程序
```sql=
GRANT EXECUTE ON GetUserInfo TO UserName;
```
📌 這樣 UserName 就能執行 GetUserInfo 存儲程序!
---
🔹 收回某個使用者的執行權限
如果不想讓某個使用者執行存儲程序:
```sql=
REVOKE EXECUTE ON GetUserInfo FROM UserName;
```
📌 這樣 UserName 就無法執行 GetUserInfo 了!
-------
🔹 明確禁止某個使用者執行存儲程序
如果要完全 禁止 某個使用者使用 EXECUTE:
```sql=
DENY EXECUTE ON GetUserInfo TO UserName;
```
📌 DENY 比 REVOKE 更強,會覆蓋任何已授予的權限!
---
### 不同角色的權限
在 SQL Server 中,可以透過 角色(Role) 來管理多個使用者的權限,這樣不用逐個設定權限。
🔹 允許 db_executor 角色執行所有存儲程序
```sql=
GRANT EXECUTE TO db_executor;
```
📌 這樣所有屬於 db_executor 角色的使用者都能執行存儲程序!
----
🔹 讓某個使用者加入 db_executor 角色
```sql=
EXEC sp_addrolemember 'db_executor', 'UserName';
```
📌 這樣 UserName 就可以執行所有存儲程序了!
----
### 查看權限
🔹 查看某個存儲程序的權限
```sql=
SELECT * FROM sys.database_permissions
WHERE major_id = OBJECT_ID('GetUserInfo');
```
📌 這樣可以查詢 GetUserInfo 存儲程序的權限!
-----
🔹 查看某個使用者的權限
```sql=
SELECT * FROM sys.database_permissions
WHERE grantee_principal_id = DATABASE_PRINCIPAL_ID('UserName');
```
📌 這樣可以檢查 UserName 在這個資料庫中的所有權限!
## 觸發器(Trigger)
資料表發生特定事件(INSERT、UPDATE、DELETE)時自動執行。
當 Users 表有新記錄插入時,自動記錄到 Logs 表:
```sql=
CREATE TABLE Logs (
LogID INT PRIMARY KEY IDENTITY(1,1),
Message NVARCHAR(255),
CreatedAt DATETIME DEFAULT GETDATE()
);
CREATE TRIGGER trg_AfterInsert ON Users
AFTER INSERT
AS
BEGIN
INSERT INTO Logs (Message)
SELECT '新使用者 ' + UserName + ' 已被新增' FROM inserted;
END;
```
### 觸發器的類型
| 類型 | 執行時機 | 說明 |
|--------------------------|--------------------|------------------------------------------------------------|
| AFTER 觸發器(FOR 觸發器) | 資料變更後 | 只有當 SQL 指令成功執行後才會觸發,適用於 INSERT、UPDATE、DELETE。 |
| INSTEAD OF 觸發器 | 取代 SQL 指令 | 在 INSERT、UPDATE、DELETE 執行前攔截並執行替代動作,可以用來限制或改變操作。 |
| DDL 觸發器 | 監控資料庫結構變更 | 監控 CREATE TABLE、ALTER TABLE、DROP TABLE 等結構變更。 |
### 1. AFTER 觸發器(FOR 觸發器)
當 INSERT、UPDATE 或 DELETE 成功執行後,觸發器才會執行!
🔹 範例:當新用戶加入時,自動在 Logs 表中記錄
假設我們有一個 Users 表:
```sql=
CREATE TABLE Users (
UserID INT PRIMARY KEY,
Name VARCHAR(100),
Age INT
);
```
再建立 Logs 表來記錄變更:
```sql=
CREATE TABLE Logs (
LogID INT IDENTITY(1,1) PRIMARY KEY,
LogMessage VARCHAR(255),
LogDate DATETIME DEFAULT GETDATE()
);
```
🔹 建立 AFTER INSERT 觸發器
```sql=
CREATE TRIGGER trg_AfterInsert_Users
ON Users
AFTER INSERT
AS
BEGIN
INSERT INTO Logs (LogMessage)
SELECT '新用戶加入:' + Name + ',年齡:' + CAST(Age AS VARCHAR)
FROM inserted;
END;
```
📌 這樣,每當有新用戶被加入 Users,Logs 表就會自動新增一筆記錄!
🔹 測試觸發器
```sql=
INSERT INTO Users (UserID, Name, Age) VALUES (1, 'Alice', 25);
SELECT * FROM Logs; -- 應該會看到「新用戶加入:Alice,年齡:25」
```
### 2. INSTEAD OF 觸發器
這種觸發器可以「攔截」原本的 INSERT、UPDATE 或 DELETE 操作,然後執行替代的指令。
🔹 範例:防止用戶刪除
```sql=
CREATE TRIGGER trg_InsteadOfDelete_Users
ON Users
INSTEAD OF DELETE
AS
BEGIN
PRINT '刪除操作被攔截,Users 表的資料不能刪除!';
END;
```
📌 這樣,Users 表的資料就無法刪除了!
🔹 測試觸發器
```sql=
DELETE FROM Users WHERE UserID = 1;
-- 執行後會顯示「刪除操作被攔截,Users 表的資料不能刪除!」但資料不會被刪除
```
### 3. DDL 觸發器(監控資料庫結構變更)
有時候,你可能希望監控資料庫的結構變更,例如防止某些重要的 TABLE 被刪除。
🔹 防止刪除 Users 表
```sql=
CREATE TRIGGER trg_PreventDrop_Users
ON DATABASE
FOR DROP_TABLE
AS
BEGIN
PRINT '禁止刪除 Users 表!';
ROLLBACK; -- 取消刪除操作
END;
```
📌 這樣 Users 表就不能被刪除了!
🔹 測試
```sql=
DROP TABLE Users;
-- 會顯示「禁止刪除 Users 表!」並且 `Users` 表不會被刪除。
```
### 刪除觸發器
如果不需要某個觸發器,可以刪除:
```sql=
DROP TRIGGER trg_AfterInsert_Users;
```
📌 這樣 trg_AfterInsert_Users 這個觸發器就被移除了!
如果是 DDL 觸發器,則需要加上 ON DATABASE:
```sql=
DROP TRIGGER trg_PreventDrop_Users ON DATABASE;
```
## 交易(Transaction)
💡💡簡單來說,交易就像「包裹」,要嘛全部成功,要嘛全部取消!
確保多個 SQL 語句一起執行,若發生錯誤則回滾:
```sql=
BEGIN TRANSACTION;
UPDATE Users SET Age = Age + 1 WHERE UserName = '小明';
DELETE FROM Users WHERE UserName = '小華';
COMMIT;
```
如果發生錯誤,回滾:
```sql=
BEGIN TRANSACTION;
UPDATE Users SET Age = Age + 1 WHERE UserName = '小明';
DELETE FROM Users WHERE UserName = '小華';
IF @@ERROR <> 0
BEGIN
ROLLBACK;
END
ELSE
BEGIN
COMMIT;
END;
```
### 交易的四大特性(ACID)
| 特性 | 說明 |
|------------------------|-------------------------------------------------------|
| Atomicity(原子性) | 全部成功或全部回滾,確保不會只執行部分 SQL。 |
| Consistency(一致性) | 確保交易後資料庫維持一致性,不會留下錯誤資料。 |
| Isolation(隔離性) | 多個交易同時執行時,彼此不互相干擾。 |
| Durability(持久性) | 交易成功後,資料永久儲存,即使系統崩潰也不會丟失。 |
### 基本語法
```sql=
BEGIN TRANSACTION; -- 開始交易
SQL 操作...
COMMIT TRANSACTION; -- 確認提交
```
📌 如果交易成功,COMMIT 會永久儲存變更!
### 交易的回滾(ROLLBACK)
如果發生錯誤,可以使用 ROLLBACK 取消變更,確保資料完整性!
🔹 例子:銀行轉帳
```sql=
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 1000 WHERE AccountID = 1; -- 扣款
UPDATE Accounts SET Balance = Balance + 1000 WHERE AccountID = 2; -- 存款
IF @ERROR <> 0 -- 檢查是否有錯誤
ROLLBACK TRANSACTION; -- 取消交易
ELSE
COMMIT TRANSACTION; -- 確認交易
```
📌 如果扣款成功但存款失敗,ROLLBACK 會回復原狀,避免金額消失!
### TRY...CATCH 搭配交易
可以使用 TRY...CATCH 來處理錯誤,讓交易更加安全。
🔹 例子:防止錯誤導致資料錯亂
```sql=
BEGIN TRANSACTION;
BEGIN TRY
UPDATE Accounts SET Balance = Balance - 1000 WHERE AccountID = 1;
UPDATE Accounts SET Balance = Balance + 1000 WHERE AccountID = 2;
COMMIT TRANSACTION; -- 交易成功,提交
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION; -- 發生錯誤,自動回滾
PRINT '交易失敗,已回滾!';
END CATCH
```
📌 這樣即使 UPDATE 發生錯誤,也不會影響資料庫的一致性!
### 隔離等級
交易的隔離性決定了多個交易同時執行時,是否會互相影響。
| 隔離等級 | 說明 | 解決的問題 |
|-------------------------|------------------------------------|---------------------------------|
| READ UNCOMMITTED | 允許讀取未提交的資料 | 可能讀到錯誤數據(Dirty Read) |
| READ COMMITTED(預設) | 只能讀取已提交的資料 | 避免 Dirty Read,但可能發生 Non-Repeatable Read |
| REPEATABLE READ | 交易內讀取的資料不會被其他交易改變 | 避免 Non-Repeatable Read,但可能發生 Phantom Read |
| SERIALIZABLE | 完全鎖住資料,確保交易隔離 | 避免 Phantom Read,但效能最低 |
🔹 設定交易的隔離等級
```sql=
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
-- SQL 操作...
COMMIT TRANSACTION;
```
📌 這樣可以確保交易內的資料完全不被其他交易影響!
### 總結
| 功能 | 語法 |
|--------------------|-------------------------------|
| 開始交易 | BEGIN TRANSACTION; |
| 提交交易 | COMMIT TRANSACTION; |
| 回滾交易 | ROLLBACK TRANSACTION; |
| 使用錯誤處理 | TRY...CATCH |
| 設定隔離等級 | SET TRANSACTION ISOLATION LEVEL ... |
## 資料表別名
其實這兩種寫法是一樣的意思:
```SQL=
FROM Vehicle AS V
```
與
```SQL=
FROM Vehicle V
```
效果 **完全相同**,都是給 Vehicle 這張資料表取一個別名叫 V,後面就可以用 V.欄位名 來引用。
| 寫法 | 說明 |
| -------------- | --------------- |
| `Vehicle V` | ✅ 最常見寫法,簡潔清楚 |
| `Vehicle AS V` | 🔸 比較正式,有些人覺得好讀 |
可以使程式碼更簡短好懂,實際範例:
```sql=
FROM Vehicle V
JOIN AccountWithVehicle AWV ON V.ID = AWV.VehicleID
```
## NEWID() 特定ID
使用 NEWID() 函數,它會自動生成一個新的 GUID(全局唯一標識符)。
**💠GUID 格式**
NEWID() 返回的 GUID 格式如下:
* 96833A93 (8位十六進制數)
* 1924 (4位十六進制數)
* 4CD2 (4位十六進制數)
* 93AF (4位十六進制數)
* 0E7354723D79 (12位十六進制數)
## ALTER 用來修改資料
### 新增欄位 (ADD)
* 新增欄位 (ADD):
```sql=
ALTER TABLE TableName
ADD ColumnName DataType;
```
例如,新增一個 Age 欄位:
```sql=
ALTER TABLE Employees
ADD Age INT;
```
* 刪除欄位 (DROP COLUMN):
```sql=
ALTER TABLE TableName
DROP COLUMN ColumnName;
```
例如,刪除 Age 欄位:
```sql=
ALTER TABLE Employees
DROP COLUMN Age;
```
* 修改欄位資料型態 (ALTER COLUMN):
```sql=
TABLE TableName
ALTER COLUMN ColumnName NewDataType;
```
例如,將 Age 欄位的資料型態修改為 VARCHAR:
```sql=
ALTER TABLE Employees
ALTER COLUMN Age VARCHAR(100);
```
### 修改表格的名稱
(RENAME 在某些 SQL 系統中,通常使用 sp_rename 在 SQL Server 中)
```sql=
EXEC sp_rename 'OldTableName', 'NewTableName';
```
### 新增約束
* 新增主鍵:
```sql=
ALTER TABLE TableName
ADD CONSTRAINT PK_ConstraintName PRIMARY KEY (ColumnName);
```
例如,將 EmployeeID 設為主鍵:
```sql=
ALTER TABLE Employees
ADD CONSTRAINT PK_EmployeeID PRIMARY KEY (EmployeeID);
```
* 新增外鍵:
```sql=
ALTER TABLE TableName
ADD CONSTRAINT FK_ConstraintName FOREIGN KEY (ColumnName)
REFERENCES OtherTable (OtherColumn);
```
例如,將 DepartmentID 設為外鍵,參照 Departments 表的 DepartmentID 欄位:
```sql=
ALTER TABLE Employees
ADD CONSTRAINT FK_DepartmentID FOREIGN KEY (DepartmentID)
REFERENCES Departments (DepartmentID);
```
### 刪除約束
刪除外鍵:
```sql=
ALTER TABLE TableName
DROP CONSTRAINT FK_ConstraintName;
```
例如,刪除外鍵約束:
```sql=
ALTER TABLE Employees
DROP CONSTRAINT FK_DepartmentID;
```
## CTE 臨時結果集
它可以在同一個 WITH 查詢內被多次引用。這讓查詢更易讀,也能讓一些操作更高效。
```sql=
WITH CTE名稱 AS (
-- 這裡放查詢
SELECT 欄位1, 欄位2, ...
FROM 表名
WHERE 條件
)
-- 這裡可以使用 CTE
SELECT * FROM CTE名稱;
```
### 去除重複的資料
```sql=
WITH CTE AS (
SELECT
Username,
HashPassword,
ROW_NUMBER() OVER (PARTITION BY Username ORDER BY (SELECT NULL)) AS rn
FROM Account
)
DELETE FROM CTE WHERE rn > 1;
```
這段 SQL 的作用:
1. **WITH CTE AS (...)** 定義了一個 CTE,名稱為 CTE。
2. SELECT Username, HashPassword, **ROW_NUMBER() OVER (PARTITION BY Username ORDER BY (SELECT NULL)) AS rn:**
* **ROW_NUMBER()** 是 SQL Server 的視窗函數,用來對每個 **Username** 分組
**(PARTITION BY Username)**,並給它們編號。
* **ORDER BY (SELECT NULL)** 代表不指定排序,這樣 **ROW_NUMBER()** 會隨機給 **Username** 相同的資料編號。
3. **DELETE FROM CTE WHERE rn > 1;**
**rn > 1** 代表刪除重複的資料,只保留第一筆。
這樣就能刪除重複的 Username,並保留其中第一筆資料。
💠補充:保留「最後一筆」
```sql=
WITH CTE AS (
SELECT
Username,
HashPassword,
ROW_NUMBER() OVER (PARTITION BY Username ORDER BY LastUpdatedTime DESC) AS rn
FROM Account
)
DELETE FROM CTE WHERE rn > 1;
```
這樣 ORDER BY LastUpdatedTime DESC 會讓 ROW_NUMBER() 把最新的時間編號為 1,其他重複的行則刪除。
----
### 為什麼要用 CTE?
1. 讓 SQL 更清晰:
你可以把 CTE 視為臨時表,讓 SQL 查詢更直觀。
2. 可以重複使用:
在同一個查詢中,CTE 可以被多次使用。
3. 性能優化:
某些情況下,CTE 可以優化查詢,減少不必要的運算。
## 資料庫圖表
從鑰匙那個地方按住拖拉即可於另一邊資料進行連結

### 如果無法開啟
將自己設為 dbo
1. 執行這段 SQL(請替換 [你的資料庫名稱] 和 [你的登入名稱]):
```sql=
USE [你的資料庫名稱];
ALTER AUTHORIZATION ON DATABASE::[你的資料庫名稱] TO [你的登入名稱];
```
2. 如果不確定自己的登入名稱,可以執行:
```sql=
SELECT SUSER_NAME();
```
3. 執行後,重新嘗試建立圖表。
### 如果無法開啟2
右鍵資料庫
屬性 > 檔案 > 擁有者 > 新增目前使用者
[查看圖片](https://drive.google.com/file/d/1LY7HaK_AMTLRFtTzQbImh-eP_nfcjM6_/view?usp=sharing)
# 資料匯出匯入
兩個差別很大,看是要匯入 **資料庫** 還是 **資料表**
## 匯出匯入資料
[SQL Server資料匯入與匯出](https://www.youtube.com/watch?v=C2jEhvT0ha8&ab_channel=%E8%AC%9D%E9%82%A6%E6%98%8C) 這個老師整理得很好!!
就大概這個位置:匯入、匯出點下去就對,剩下中文應該也懂吧:DD

## 還原檔案
[SQL Server Management Studio:完整備份及還原資料庫教學](https://mnya.tw/cc/word/2227.html) 這個整理得很完整

出錯無法處理的原因
----
資料要放在資料夾的:C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Backup
再不行的話換伺服器位置:

出錯無法處理2
-----
按 Win + R ➡️ 輸入:
```
services.msc
```
或者 Win + Q 搜尋
```
服務
```
找 SQL Server (XXX),確定有啟動。
Server Name (XXX)就是:
* ANNA(如果是 MSSQLSERVER)
* ANNA\SQLEXPRESS(如果是 SQLEXPRESS)
## 備份資料
他到時候就會存到他寫的位置去,十分方便:))
[遊覽圖片說明](https://drive.google.com/file/d/1fNS5IMXSJNm5hbEwH4AOhZQFPRqFWAnd/view?usp=sharing)