**프리페어드 스테이트먼트**(prepared statement), **파라미터라이즈드 스테이트먼트**(parameterized statement)는 데이터베이스 관리 시스템(DBMS)에서 동일하거나 비슷한 데이터베이스 문을 높은 효율성으로 반복적으로 실행하기 위해 사용되는 기능이다. 일반적으로 쿼리나 업데이트와 같은 SQL 문과 함께 사용되는 프리페어드 스테이트먼트는 템플릿의 형태를 취하며, 그 템플릿 안으로 특정한 상수값이 매 실행 때마다 대체된다.

프리페어드 스테이트먼트의 일반적인 워크플로는 다음과 같다:

1. **준비**(Prepare): 먼저 애플리케이션은 문의 틀을 만들고 이를 DBMS로 보낸다. 특정값은 지정하지 않은 채로 남겨지며 이들은 "변수", "플레이스홀더", "바인드값"으로 부른다. (아래의 "?" 레이블 참고):
`INSERT INTO products (name, price) VALUES (?, ?);`
2. 그 다음, DBMS는 문의 틀을 컴파일하며(최적화 및 변환) 아직 실행하지 않고 결과만 저장한다.
3. **실행**(Execute): 나중에 애플리케이션이 문 틀의 변수에 값(바인드)을 지정하면 DBMS는 (결과를 반환할 수도 있는) 문을 실행한다. 애플리케이션은 여러 값으로 원하는 횟수만큼 문을 실행할 수 있다. 위의 예에서 첫 번째 변수로 "bike"로, 두 번째 변수로 "10900"을 지정한다.

소프트웨어 지원
--------

MySQL, 오라클, DB2, 마이크로소프트 SQL 서버, PostgreSQL을 포함한 주요 DBMS들은 프리페어드 스테이트먼트를 폭넓게 지원한다.

예
-

### 자바 JDBC

```
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Main {

 public static void main(String[] args) throws SQLException {
 MysqlDataSource ds = new MysqlDataSource();
 ds.setDatabaseName("mysql");
 ds.setUser("root");

 try (Connection conn = ds.getConnection()) {
 try (Statement stmt = conn.createStatement()) {
 stmt.executeUpdate("CREATE TABLE IF NOT EXISTS products (name VARCHAR(40), price INT)");
 }

 try (PreparedStatement stmt = conn.prepareStatement("INSERT INTO products VALUES (?, ?)")) {
 stmt.setString(1, "bike");
 stmt.setInt(2, 10900);
 stmt.executeUpdate();
 stmt.setString(1, "shoes");
 stmt.setInt(2, 7400);
 stmt.executeUpdate();
 stmt.setString(1, "phone");
 stmt.setInt(2, 29500);
 stmt.executeUpdate();
 }

 try (PreparedStatement stmt = conn.prepareStatement("SELECT \* FROM products WHERE name = ?")) {
 stmt.setString(1, "shoes");
 ResultSet rs = stmt.executeQuery();
 rs.next();
 System.out.println(rs.getInt(2));
 }
 }
 }
}

```

### PHP PDO

```
<?php
$stmt = null;

try {
  $conn = new PDO("mysql:dbname=mysql", "root");
  $conn->exec("CREATE TABLE IF NOT EXISTS products (name VARCHAR(40), price INT)");
  $stmt = $conn->prepare("INSERT INTO products VALUES (?, ?)");
  $params = array(array("bike", 10900),
                  array("shoes", 7400),
                  array("phone", 29500));
  foreach ($params as $param) $stmt->execute($param);
  $stmt = $conn->prepare("SELECT \* FROM products WHERE name = ?");
  $params = array("shoes");
  $stmt->execute($params);
  echo $stmt->fetch()[1];
} finally {
  if ($stmt !== null) $stmt->closeCursor();
}
?>

```

### 펄 DBI

```
my $stmt = $db->prepare("SELECT \* FROM users WHERE USERNAME = ? AND PASSWORD = ?");
$stmt->execute($username, $password);

```

### C# ADO.NET

```
using (SqlCommand command = connection.CreateCommand())
{
 command.CommandText = "SELECT \* FROM users WHERE USERNAME = @username AND ROOM = @room";
 command.Parameters.AddWithValue("@username", username);
 command.Parameters.AddWithValue("@room", room);

 using (SqlDataReader dataReader = command.ExecuteReader())
 {
 // ...
 }
}

```

### 파이썬 DB-API

```
import mysql.connector

conn = None
cursor = None

try:
    conn = mysql.connector.connect(database="mysql", user="root")
    cursor = conn.cursor()
    cursor.execute("CREATE TABLE IF NOT EXISTS products (name VARCHAR(40), price INT)")
    params = [("bike", 10900),
              ("shoes", 7400),
              ("phone", 29500)]
    cursor.executemany("INSERT INTO products VALUES (%s, %s)", params)
    params = ("shoes",)
    cursor.execute("SELECT \* FROM products WHERE name = %s", params)
    print(cursor.fetchall()[0][1])
finally:
    if cursor is not None: cursor.close()
    if conn is not None: conn.close()

```

### 매직 다이렉트 SQL

```
Virtual username  Alpha 20   init: 'sister'
Virtual password  Alpha 20   init: 'yellow'
```

```
SQL Command:   SELECT \* FROM users WHERE USERNAME=:1 AND PASSWORD=:2
```

```
Input Arguments:
1:  username
2:  password
```