使用PDO准备SQL语句

时间:2009-08-10 12:22:54

标签: php database sqlite pdo

我的代码如下所示:

// 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 个答案:

答案 0 :(得分:5)

如果你在循环中“准备”它们,为什么要使用预准备语句? (在DB功能中)

为循环做准备,在循环中只给出值并执行。 当然这需要重写你的数据库方法。

哦,顺便说一下。你的ID列是主键吗?如果是这样你有另一个问题,那么“我”将是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));     
}

答案 1 :(得分:3)

如果要将大量数据插入表中,请尝试在一个查询中插入数据。

$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);

这应该可以消除单个插入查询的开销。 但是查询长度有一个限制,如果你遇到查询长度问题,请尝试在for循环中更频繁地发出DB()调用。

答案 2 :(得分:1)

不幸的是,我认为问题可能出在你的代码结构上。

在你的INSERT语句循环中,语句都是相同的,每次都不需要调用$ db-&gt; prepare。准备语句背后的想法是,您只需调用$ db-&gt; prepare()一次,并且可以在同一语句对象上多次调用execute()。你每次都在调用$ db-&gt; 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语句,只是使用不同的值重新执行它。函数的返回值将是每个查询结果的数组。

答案 3 :(得分:1)

最初发布的数据库函数每次运行时都会发出一个文件系统stat()系统调用,以检查查询字符串是否为文件。虽然这不仅仅是执行缓慢的原因,但却有助于它。