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

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

== 소프트웨어 지원 ==
[[MySQL]],<ref>{{웹 인용|last= Oracle|title= 20.9.4. C API Prepared Statements|url= http://dev.mysql.com/doc/refman/5.5/en/c-api-prepared-statements.html|work= MySQL 5.5 Reference Manual|accessdate= 27 March 2012|archive-date= 2017-06-30|archive-url= https://web.archive.org/web/20170630122002/https://dev.mysql.com/doc/refman/5.5/en/c-api-prepared-statements.html|url-status= dead}}</ref> [[오라클 데이터베이스|오라클]],<ref>{{웹 인용|title= 13 Oracle Dynamic SQL|url= http://download.oracle.com/docs/cd/B10501_01/appdev.920/a97269/pc_13dyn.htm|work= Pro*C/C++ Precompiler Programmer's Guide, Release 9.2|publisher= Oracle|accessdate= 25 September 2011}}</ref> [[IBM DB2|DB2]],<ref>{{웹 인용|title= Using the PREPARE and EXECUTE statements|url= http://publib.boulder.ibm.com/infocenter/iseries/v5r4/index.jsp?topic=%2Fsqlp%2Frbafyplepexc.htm|work= i5/OS Information Center, Version 5 Release 4|publisher= IBM|accessdate= 25 September 2011}}{{깨진 링크|url=http://publib.boulder.ibm.com/infocenter/iseries/v5r4/index.jsp?topic=%2Fsqlp%2Frbafyplepexc.htm }}</ref> [[마이크로소프트 SQL 서버]]<ref>{{웹 인용|title= SQL Server 2008 R2: Preparing SQL Statements|url= http://msdn.microsoft.com/en-us/library/ms175528.aspx|work= MSDN Library|publisher= Microsoft|accessdate= 25 September 2011}}</ref>, [[PostgreSQL]]을 포함한 주요 [[DBMS]]들은 프리페어드 스테이트먼트를 폭넓게 지원한다.<ref name="postgres">{{웹 인용|title= PREPARE|url= http://www.postgresql.org/docs/9.5/static/sql-prepare.html|work= PostgreSQL 9.5.1 Documentation|publisher= PostgreSQL Global Development Group|accessdate= 27 February 2016}}</ref>

== 예 ==
=== 자바 JDBC ===
<syntaxhighlight lang="java">
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));
      }
    }
  }
}
</syntaxhighlight>

=== PHP PDO ===
<syntaxhighlight lang="php">
<?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();
}
?>
</syntaxhighlight>

=== 펄 DBI ===
<syntaxhighlight lang="perl">
my $stmt = $db->prepare("SELECT * FROM users WHERE USERNAME = ? AND PASSWORD = ?");
$stmt->execute($username, $password);
</syntaxhighlight>

=== C# ADO.NET ===
<syntaxhighlight lang="csharp">
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())
    {
        // ...
    }
}
</syntaxhighlight>

=== 파이썬 DB-API ===
<syntaxhighlight lang="python">
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()
</syntaxhighlight>

=== 매직 다이렉트 SQL ===
 Virtual username  Alpha 20   init: 'sister'
 Virtual password  Alpha 20   init: 'yellow'

 SQL Command:   <syntaxhighlight lang="sql" inline>SELECT * FROM users WHERE USERNAME=:1 AND PASSWORD=:2</syntaxhighlight>

 Input Arguments:
 1:  username
 2:  password

== 같이 보기 ==
* [[코드 인젝션]]
== 각주 ==
{{각주}}

{{전거 통제}}

[[분류:데이터베이스]]
[[분류:SQL]]