Подготовка операторов SQL с помощью PDO - PullRequest
2 голосов
/ 10 августа 2009

Мой код выглядит так:

// Connect to SQLite DB
DB('/path/to/sqlite.db');

DB('BEGIN TRANSACTION;');

// These loops are just examples.
for ($i = 1; $i <= 10000; $i++)
{
    for ($j = 1; $j <= 100; $j++)
    {
        DB('INSERT INTO "test" ("id", "name") VALUES (?, ?);', $i, 'Testing ' . $j);
    }
}

DB('END TRANSACTION;');

А вот функция DB ():

function DB($query)
{
    static $db = null;

    if (is_file($query) === true)
    {
        $db = new PDO('sqlite:' . $query, null, null, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING));
    }

    else if (is_a($db, 'PDO') === true)
    {
        $result = $db->prepare($query);

        if (is_a($result, 'PDOStatement') === true)
        {
            if ($result->execute(array_slice(func_get_args(), 1)) === true)
            {
                if (stripos($query, 'INSERT') === 0)
                {
                    return $db->lastInsertId();
                }

                if (stripos($query, 'SELECT') === 0)
                {
                    return $result->fetchAll(PDO::FETCH_ASSOC);
                }

                return $result->rowCount();
            }
        }

        return false;
    }

    return true;
}

Проблема в том, что вызов DB () во внутреннем цикле занимает много времени, я думал, что выполнение чего-то подобного может значительно ускорить выполнение:

DB('BEGIN TRANSACTION;');

for ($i = 1; $i <= 10000; $i++)
{
    $queries = array();

    for ($j = 1; $j <= 100; $j++)
    {
        $queries[] = 'INSERT INTO "test" ("id", "name") VALUES (?, ?);' /*, $i, 'Testing ' . $j;*/
    }

    DB(implode("\n", $queries));
}

DB('END TRANSACTION;');

Проблема в том, что я не знаю, как мне подготовить (заменить и убрать) все знаки вопроса с соответствующими переменными, есть ли способ, которым я могу это сделать?

Ответы [ 4 ]

5 голосов
/ 10 августа 2009

Почему вы используете подготовленные операторы, если вы «готовите» их в цикле? (в функции БД)

Сделать подготовку перед циклом, а в цикле просто дать значения и выполнить. Конечно, для этого потребуется переписать ваш метод БД.

Да, и кстати. Ваш идентификатор столбца первичный ключ? если так, у вас есть другая проблема, потому что "i" будет для 100 "j" таким же:)

Например:

$sth = $dbh->prepare('INSERT INTO "test" ("id", "name") VALUES (:id, :name)');
$j=0;
for ($i = 1; $i <= 10000; $i++){
   $j = ($j==100) ? 0 : $j++;
   $sth->execute(array(':id' => $i, ':name' => 'Testing ' . $j));     
}
3 голосов
/ 10 августа 2009

Если вы вставляете много данных в таблицу, попробуйте вставить данные в один запрос.

$query = 'INSERT INTO "test" ("id", "name") VALUES ';
$data = array();
for ($i = 1; $i <= 10000; $i++) {
  for ($j = 1; $j <= 100; $j++) {
    $query .= '(?,?),';
    $data[] = $i;
    $data[] = 'Testing '.$j;
  }
}

$query = substr($query, 0, -1);
DB($query, $data);

Это должно устранить накладные расходы с запросами на одну вставку. Существует ограничение на длину запроса, однако, если у вас возникают проблемы с длиной запроса, попробуйте чаще выполнять вызовы DB () в цикле for.

1 голос
/ 22 августа 2009

Функция БД в том виде, в котором она была опубликована, каждый раз запускает системный вызов stat () файловой системы, чтобы проверить, является ли строка запроса файлом. Хотя это не единственная ответственность за медленное выполнение, это способствует этому.

1 голос
/ 10 августа 2009

К сожалению, я думаю, что проблема может быть со структурой вашего кода.

В вашем цикле операторов INSERT все операторы идентичны, и нет необходимости каждый раз вызывать $ db-> prepare. Идея подготовленных операторов заключается в том, что вы вызываете $ db-> prepare () один раз, а execute () может вызываться несколько раз для одного и того же объекта оператора. Вы каждый раз вызываете $ db-> prepare (), что приводит к дополнительным расходам при разборе оператора SQL и создании нового объекта.

Подумайте о переписывании вашей функции DB () следующим образом:

function do_query($db, $pdo_statement, $query, $args)
{
    if ($pdo_statement->execute($args) === true)
    {
        if (stripos($query, 'INSERT') === 0)
        {
          return $db->lastInsertId();
        }
        if (stripos($query, 'SELECT') === 0)
        {
          return $result->fetchAll(PDO::FETCH_ASSOC);
        }
        return $result->rowCount();
    }
}

function DB($query)
{
    static $db = null;

    if (is_file($query) === true)
    {
      $db = new PDO('sqlite:' . $query, null, null, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING));
    }

    else if (is_a($db, 'PDO') === true)
    {
      $result = $db->prepare($query);

      if (is_a($result, 'PDOStatement') === true)
      {
        $args = func_get_args();
        if (is_array($args[1])) {
            $ret = array();
            foreach ($args[1] as $args) {
                $ret[] = do_query($db, $query, $result, $args);
            }
            return $ret;
        }

        return do_query($db, $query, $result, array_slice(func_get_args(), 1));
      }

      return false;
    }

    return true;
}

Таким образом, если вы хотите выполнить один и тот же запрос с большим количеством значений, вы создаете двумерный массив значений, которые хотите вставить, и вызываете DB('INSERT INTO....', $values). Функция DB () проверяет, является ли второй параметр функции (после $ query) массивом, и если это так, он выполняет цикл $ query для значений в массиве. Таким образом, цикл не предполагает повторной подготовки оператора SQL каждый раз, просто выполняет его заново с другими значениями. Возвращаемое значение функции будет массивом результатов каждого запроса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...