MySQLi_Recordset: смешивание SPL и заявление/запрос результаты


Я сделал шлицов на основе класса с именем "записей", который обертывает, как MySQLi_STMT и объектов MySQLi_Result и позволяет рассматривать либо как 3-мерный массив. Это требует php5.3+.

Я очень расстроилась по поводу медленных циклов foreach над моим набора записей объекта, тем медленнее внутренней искать и принести скорость в наборе, и по тому, что ничто не может даже сравниться с Старого а ($MySQLI_STMT->получить()) {}. Я уже кэшированные определенной информации, результате, например, число_строк и имя класса, и попытаться избежать цепочки внутренних вызовов функций, который значительно ускорит процес. К сожалению, это снижает надлежащий внутренние данные проверки. Постоянное data_seek звонки замедлить немного. Я бы для этого лучше работать. Любые предложения или понимание как ускорить циклы foreach и внутренние выборки строк?

Вот выдержки из важных частей...

class Recordset implements Iterator, ArrayAccess, Countable {

    private $MySQLi_set; // MySQLi_STMT or MySQLi_Result object
    private $MySQLi_set_type; // cache of MySQLi_STMT or MySQLi_Result object type to avoid repeat instanceof calls
    private $field_metadata; // array of field info objects returned by query
    private $num_rows; // cache row count to avoid repeat mysqli_x->num_rows call
    private $bind_row; // binding array for MySQLi_STMT results
    private $pointer; // for tracking iterations over records in query


    function __construct($sql) {
        $bind_params = func_get_args();
        array_shift($bind_params);
        try {
            if (count($bind_params) === 0) {
                // expecting MYSQLI_Result obj returned, replace code as necessary
                $this->MySQLi_set = DB_Main::query($sql);  
                }
            else {
                // expecting MySQLi_STMT obj returned, replace code as necessary
                $this->MySQLi_set = DB_Main::statement($sql, $bind_params);  
                }
            }
        catch (Exception $e) {
            throw new RuntimeException(((count($bind_params)) ? 'Statement' : 'Query') . ' failed:  ' . $e->getMessage(), $e->getCode(), $e);
            }
        // cache name of object to avoid repeat instanceof calls
        $this->MySQLi_set_type = get_class($this->MySQLi_set);
        if ($this->MySQLi_set_type === 'mysqli_result') {
            // set $this->field_metadata to array of field info objects
            $this->field_metadata = $this->MySQLi_set->fetch_fields();
            }
        else if ($this->MySQLi_set_type === 'mysqli_stmt') {
            // Set up special handling for MySQLi_STMT objects
            // set $this->field_metadata to array of field info objects used later for result bindings for MySQLi_STMT object
            $result_metadata = $this->MySQLi_set->result_metadata();
            $this->field_metadata = $result_metadata->fetch_fields();   
            $result_metadata->free_result();
            unset($result_metadata);
            // store results to allow object to act as a countable, seekable array
            if (!$this->MySQLi_set->store_result())
                throw new RuntimeException('MySQLi_STMT Result failed to store.  Error: ' . $this->MySQLi_set->error);
            // because bind_result() cannot bind to an object or array, use the 'call_user_func_array()' technique instead
            $this->bind_row = array();
            $bind_row2 = array();
            foreach($this->field_metadata as $field)
                 $bind_row2[$field->name] =& $this->bind_row[$field->name];
            if (!call_user_func_array(array($this->MySQLi_set, 'bind_result'), $this->bind_row))
                throw new RuntimeException($this->MySQLi_set->error);
            if ($this->MySQLi_set->error)
                throw new RuntimeException($this->MySQLi_set->error);
            }
        else {
            throw new RuntimeException('No MySQLi_STMT or MySQLi_Result object to work with.  Class: ' . $this->MySQLi_set_type . ' attempted.');
            }
        // cache row count to avoid constant calls to MySQLi_x->num_rows
        $this->num_rows = $this->MySQLi_set->num_rows;
        // if rows were returned, set internal pointer to 0
        if ($this->num_rows > 0) $this->pointer = 0;
        }


    function fetchRow($offset = null) {
        // private function internally using regular fetch() methods to progress through records
        // sets $this->bind_row to an associative array of the next record for MySQLi_STMT wrapped objects
        $this->MySQLi_set->data_seek((($offset !== null) ? (int) $offset : $this->pointer));
        if ($this->MySQLi_set_type === 'mysqli_result')
            return $this->MySQLi_set->fetch_assoc();
        // else this is a MySQLi_STMT object
        $fetch_status = $this->MySQLi_set->fetch();
        if ($fetch_status === true) return $this->bind_row;
        else if ($fetch_status === null) return null; // no more rows
        // MySQLi_STMT row fetch failed
        throw new RuntimeException('Error fetching row: . ' . $this->MySQLi_set->error);
        }


    function count() {return $this->num_rows;}

    function current() {return $this->fetchRow();}

    function key() {return $this->pointer;}

    function next() {$this->pointer++;}

    function rewind() {$this->pointer = 0;}

    function valid() {return $this->offsetExists($this->pointer);}


    function offsetExists($offset) {
        // Part of the ArrayAccess interface.  Used to determine if the selected record in the set actually exists or is out of bounds.
        // Example: 'isset($Recordset[22])' returns true if there were 23 or more records returned from the query.
        return ((int) $offset < $this->num_rows AND (int) $offset >= 0);
        }


    function offsetGet($offset) {
        // Part of the ArrayAccess interface.  Used to retrieve the assoc array of the chosen record in line.
        // allow index referencing without altering "internal array pointer"
        // Example: 'print_r($Recordset[22])' prints the 23rd record returned by the query.
        if (!((int) $offset < $this->num_rows AND (int) $offset >= 0))
            throw new OutOfBoundsException('Out of range of result set.');
        return $this->fetchRow($offset);
        }

    }

Код находится примерно в 200 строк, поэтому вместо того, чтобы сделать гигантский пост, вот сайт Pastebin ссылку... http://pastebin.com/n17MuEzf

Использование...

Объекты создаются путем передачи в строку SQL-запроса. Для обработки подготовленных заявлений, просто передайте дополнительные аргументы как подготовленные параметры привязки. В тех случаях, есть закулисные bind_param происходит создание MySQLi_STMT объект, чтобы быть обернуты. Эта часть не входит сюда, так что для целей этого поста, представь, что это магия.

Например, чтобы автоматически обрабатывать запрос как подготовленное заявление и вывода строк:

$recordset = new Recordset("SELECT * FROM tblComments WHERE(parentBlogID = ?)", 2);
if (count($recordset) > 0) {
    foreach ($recordset as $record)
        echo $record['commentText'] . '<br>';
    }
else echo "No records returned.";

Преимущества...

  • Позволяет достигнуть результата строк массива ключ (Эхо $записей[295]['название'];) или циклы foreach (по каждому элементу ($Recordsetas $отдых) {Эхо $отдых['название'];}).
  • Позволяет получить строку по номеру индекса, не мешая внутренний указатель результата. Другими словами, прямо в середине цикла foreach петли, вы можете сделать $х = $записей[259]['идентификатор']; не вызывая внутренних строки результирующего указателя выскочить из того.
  • Позволяет обмениваться этот класс для обычных 3-мерных массивов отвалов результат.
  • Во избежание дублирования в результате связывания код для каждого оператора во всем приложении.
  • Позволяет получить доступ к функции и свойства завернутый в mysqli объект напрямую ($записей->attr_get(1)).
  • Позволит подключить автоматический вывод фильтров или обработки шаблона.

Тесты...

Вот некоторые тесты скорости перебора 10000 записей из таблицы localhost с 6 колонками. Этот тест был сделан с подготовленного отчета закутавшись в наборе или нет. Время измеряется начиная до и заканчивая после каждого весь цикл вызова..

С Записей...

  • по каждому элементу($записей в $РЭЦ) {$х = $отдых;} ...
    0.1033 сек
  • для ($Я = 0, $счетчик = счетчик($записей); $я < $рассчитывать; $Я++) {$х = $записей[$я];} ...
    0.0445 сек
  • для ($Я = 0, $счетчик = счетчик($записей); $я < $рассчитывать; $Я++) {$х = $записей->fetchRow($я);} ...
    0.0344 сек
  • в то время как ($Х = $записей[$я]) {$я++; если (!использования isset($записей[$я])) перерыва;} ...
    0.0542 сек

Без записей, но все еще используя "привязку к массиву" техника и пре-хранение результатов...

  • для ($Я=0, $кол-во=MySQLI_STMT->число_строк; $я < $рассчитывать; $я++)) {$MySQLI_STMT->получить(); $х = $bind_row;} ...
    0.0414 сек
  • в то время как ($MySQLI_STMT->получить()) {$х = $bind_row;} ...
    0.009 сек


643
7
задан 29 января 2011 в 03:01 Источник Поделиться
Комментарии