프리페어드 스테이트먼트

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

프리페어드 스테이트먼트의 일반적인 워크플로는 다음과 같다:
 준비(Prepare): 먼저 애플리케이션은 문의 틀을 만들고 이를 DBMS로 보낸다. 특정값은 지정하지 않은 채로 남겨지며 이들은 "변수", "플레이스홀더", "바인드값"으로 부른다. (아래의 "?" 레이블 참고):
 
 그 다음, DBMS는 문의 틀을 컴파일하며(최적화 및 변환) 아직 실행하지 않고 결과만 저장한다.
 실행(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

각주 

데이터베이스
SQL