# 原生 SQL

本頁說明如何以 `read()`、`write()`、`query()` 直接執行手寫 SQL。

## 三個入口

| 方法 | 連線池 | 未建立連線池時 |
|---|---|---|
| `read<T>(sql, params?)` | 讀取池 | 拋出 `Read pool connection is not available.` |
| `write<T>(sql, params?)` | 寫入池 | 拋出 `Write pool connection is not available.` |
| `query<T>(sql, params?, target?)` | `target`；省略時沿用最近一次 `table()` 的目標 | 拋出 `read connection is not available.` 或 `write connection is not available.` |

三者都經過 `query()` 執行，因此同樣有 [慢查詢記錄](/zh/slow-query-log) 與連線自動釋放。

## 參數綁定

以 `?` 佔位符傳入值，交給 mysql2 跳脫：

```typescript
import MySQLPool from "@pardnchiu/mysql-pool";
import type { ResultSetHeader, RowDataPacket } from "mysql2/promise";

try {
  const rows = await MySQLPool.read<RowDataPacket[]>(
    "SELECT id, name FROM users WHERE deleted_at IS NULL AND (role = ? OR role = ?)",
    ["admin", "editor"]
  );

  const header = await MySQLPool.write<ResultSetHeader>(
    "UPDATE users SET last_login = NOW() WHERE id = ?",
    [rows[0].id]
  );

  console.log(header.affectedRows);
} catch (err) {
  console.error("raw query failed:", err);
}
```

泛型 `T` 只是型別斷言，回傳值就是 mysql2 `query()` 結果陣列的第一個元素：SELECT 為資料列陣列，INSERT／UPDATE／DELETE 為 `ResultSetHeader`。

## 相關頁面

- [讀寫連線池](/zh/read-write-pools)
- [API 參考](/zh/api-reference)
