2

私のコードは次のようになります。

// 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

4 に答える 4

5

ループ内で「プリペアド」するのに、なぜプリペアドステートメントを使用するのですか?(DB機能内)

ループの前に準備を行い、ループ内で値を指定して実行します。もちろん、これにはDBメソッドの書き直しが必要になります。

ああ、ところで。ID列が主キーですか?もしそうなら、あなたは別の問題を抱えています。なぜなら、「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));     
}
于 2009-08-10T12:34:18.777 に答える
3

テーブルに大量のデータを挿入する場合は、1つのクエリにデータを挿入してみてください。

$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()呼び出しをより頻繁に発行してみてください。

于 2009-08-10T13:35:34.140 に答える
1

残念ながら、問題はコードの構造にあると思います。

INSERT ステートメントのループでは、ステートメントはすべて同一であり、毎回 $db->prepare を呼び出す必要はありません。プリペアド ステートメントの背後にある考え方は、$db->prepare() を 1 回呼び出すと、同じステートメント オブジェクトに対して 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;
}

したがって、同じクエリを多数の値で実行する場合は、挿入する値の 2 次元配列を作成し、 を呼び出しますDB('INSERT INTO....', $values)。DB() 関数は、関数の 2 番目のパラメーター ($query の後) が配列であるかどうかを確認し、配列の値に対して $query を実行してループします。このように、ループは毎回 SQL ステートメントを再準備する必要はなく、異なる値で再実行するだけです。関数の戻り値は、各クエリの結果の配列になります。

于 2009-08-10T12:42:20.713 に答える
1

最初に投稿された DB 関数は、実行されるたびに filesystem stat() システム コールを発行して、クエリ文字列がファイルかどうかを確認します。それだけが実行速度の低下の原因ではありませんが、一因となっています。

于 2009-08-22T02:02:10.713 に答える