PDO의 쿼리 대 실행


129

둘 다 똑같은 일을합니까?

prepare사이를 사용하는 것 외에 다른 점이 있습니까?

$sth = $db->query("SELECT * FROM table");
$result = $sth->fetchAll();

$sth = $db->prepare("SELECT * FROM table");
$sth->execute();
$result = $sth->fetchAll();

?

답변:


145

query 표준 SQL 문을 실행하고 SQL 주입 및 기타 문제를 피하기 위해 모든 데이터를 올바르게 이스케이프해야합니다.

execute매개 변수를 바인드하거나 인용 할 필요가 없도록 매개 변수를 바인드 할 수있는 준비된 명령문을 실행합니다. execute쿼리를 여러 번 반복하면 성능이 향상됩니다. 준비된 진술의 예 :

$sth = $dbh->prepare('SELECT name, colour, calories FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->bindParam(':calories', $calories);
$sth->bindParam(':colour', $colour);
$sth->execute();
// $calories or $color do not need to be escaped or quoted since the
//    data is separated from the query

모범 사례는 준비된 진술과 execute보안 강화 를 고수하는 것 입니다.

PDO 준비 명령문이 SQL 삽입을 방지하기에 충분합니까?를 참조하십시오 .


이 링크는 의견에 이미 비판 된 상당히 어리석은 대답으로 질문으로 이어집니다.
당신의 상식

당신이 준비를 사용하는 경우 주사를 중지 : calories 하는 것과 같은 종류 입니까 아니면 보안을 높이는 mysql_real_escape_string()것 이상이 필요 $sth->bindParam(':calories', $calories);합니까?
Dan

이유는 무엇입니까 query반환 PDOStatement을 대신에, 부울 같은 execute?
Leo


47

아니요, 동일하지 않습니다. 제공되는 클라이언트 측에서 이스케이프 처리하는 것 외에도 준비된 명령문은 서버 측에서 한 번 컴파일 된 다음 각 실행시 다른 매개 변수전달할 수 있습니다 . 당신이 할 수있는 것을 의미 :

$sth = $db->prepare("SELECT * FROM table WHERE foo = ?");
$sth->execute(array(1));
$results = $sth->fetchAll(PDO::FETCH_ASSOC);

$sth->execute(array(2));
$results = $sth->fetchAll(PDO::FETCH_ASSOC);

소규모에서는 눈에 띄지 않지만 일반적으로 성능 향상을 제공합니다. 준비된 명령문 (MySQL 버전)에 대해 자세히 알아보십시오 .


왜 더 빠를 지 설명했던 방식이 마음에 듭니다.
timfreilly


3

Gilean의 답변 은 훌륭하지만 때로는 모범 사례에 드문 예외가 있음을 추가하고 싶었습니다. 환경을 두 가지 방법으로 테스트하여 무엇이 가장 효과가 좋은지 확인할 수 있습니다.

한 가지 경우, 나는 MS SQL Server 용으로query 제대로 지원되지 않는 Microsoft ODBC 드라이버를 사용하여 PHP7을 실행하는 Ubuntu Linux 상자에서 신뢰할 수있는 데이터를 대량 전송하기 때문에 내 목적에 더 빨리 작동 한다는 것을 알았습니다 .

나는 속도를 위해 짜려고했던 ETL에 대한 스크립트를 오래 실행했기 때문에이 질문에 도달했습니다 . & query보다 빠를 수있는 것은 직관적 이었습니다. 두 개가 아닌 하나의 함수 만 호출했기 때문입니다. 매개 변수 바인딩 조작은 탁월한 보호 기능을 제공하지만 비용이 많이 들고 불필요한 경우 피할 수 있습니다.prepareexecute

몇 가지 드문 조건이 주어지면 :

  1. 당신이 준비된 문을 다시 사용할 수없는 경우 때문에 그것은 마이크로 소프트 ODBC 드라이버에서 지원 아니에요 .

  2. 입력을 삭제하는 것에 대해 걱정하지 않으면 간단한 이스케이프가 가능합니다. Microsoft ODBC 드라이버가 특정 데이터 유형 바인딩을 지원하지 않기 때문일 수 있습니다 .

  3. PDO::lastInsertId Microsoft ODBC 드라이버는 지원하지 않습니다.

다음은 내 환경을 테스트하는 데 사용한 방법이며 다음과 같이 환경을 더 잘 복제 할 수 있기를 바랍니다.

먼저 Microsoft SQL Server에서 기본 테이블을 만들었습니다.

CREATE TABLE performancetest (
    sid INT IDENTITY PRIMARY KEY,
    id INT,
    val VARCHAR(100)
);

이제 성능 지표에 대한 기본 시간 테스트입니다.

$logs = [];

$test = function (String $type, Int $count = 3000) use ($pdo, &$logs) {
    $start = microtime(true);
    $i = 0;
    while ($i < $count) {
        $sql = "INSERT INTO performancetest (id, val) OUTPUT INSERTED.sid VALUES ($i,'value $i')";
        if ($type === 'query') {
            $smt = $pdo->query($sql);
        } else {
            $smt = $pdo->prepare($sql);
            $smt ->execute();
        }
        $sid = $smt->fetch(PDO::FETCH_ASSOC)['sid'];
        $i++;
    }
    $total = (microtime(true) - $start);
    $logs[$type] []= $total;
    echo "$total $type\n";
};

$trials = 15;
$i = 0;
while ($i < $trials) {
    if (random_int(0,1) === 0) {
        $test('query');
    } else {
        $test('prepare');
    }
    $i++;
}

foreach ($logs as $type => $log) {
    $total = 0;
    foreach ($log as $record) {
        $total += $record;
    }
    $count = count($log);
    echo "($count) $type Average: ".$total/$count.PHP_EOL;
}

특정 환경에서 여러 가지 시험 및 카운트를 수행했으며 / query보다 20-30 % 빠른 결과를 일관되게 얻었 습니다.prepareexecute

5.8128969669342 준비
5.8688418865204 준비
4.2948560714722 쿼리
4.9533629417419 쿼리
5.9051351547241 준비
4.332102060318 쿼리
5.9672858715057 준비
5.0667371749878 쿼리
3.8260300159454 쿼리
4.0791549682617 쿼리
4.3775160312653 쿼리
3.6910600662231 질의
제조 5.2708210945129
6.2671611309052 준비
7.3791449069977 준비를
6.0673267160143 (7) 평균 조제
(8) 쿼리 평균 : 4.3276024162769

이 테스트가 MySQL과 같은 다른 환경에서 어떻게 비교되는지 궁금합니다.


"경험적 증거"(또는 오히려 인공적인 테스트)의 문제는 그들이 당신의 (알 수없는) 특정 조건을 반영하고 실제 경험적 증거는 물론 다른 사람에게는 다를 수 있다는 것입니다. 그러나 어떤 사람들은 그것을 당연한 것으로 받아들이고 그 단어를 더 널리 퍼뜨릴 것입니다.
당신의 상식

@YourCommonSense 전적으로 동의합니다. 대담한 드문 조건에서 명확하다고 생각했기 때문에 의견을 보내 주셔서 감사합니다. 나는 회의적인 양의 회의론을 가정하지만 분명하지는 않습니다. 내 수정 된 답변을 확인하고 개선 방법을 알려주십시오.
Jeff Puckett
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.