Как получить общее количество строк в запросе GROUP BY? - PullRequest
44 голосов
/ 18 мая 2011

Из руководства по PDO:

PDOStatement :: rowCount () возвращает количество строк , затронутых последним оператор DELETE, INSERT или UPDATE выполнено соответствующим PDOStatement объект.

Если последний оператор SQL выполнен соответствующее PDOStatement было оператор SELECT , некоторые базы данных могут вернуть количество строк, возвращаемых это утверждение . Тем не менее, это поведение не гарантировано для всех базы данных и на них нельзя полагаться для портативных приложений.

Я узнал об этом только недавно. Я только что изменил свой слой абстракции БД, чтобы он больше не использовал SELECT COUNT(1) ..., потому что просто запрос реальных строк и затем подсчет результата будет намного более эффективным. И теперь PDO не поддерживает это!?

Я не использую PDO для MySQL и PgSQL, но я использую для SQLite. Есть ли способ (без полного изменения обратно dbal) подсчитывать строки в PDO? В MySQL это будет примерно так:

$q = $db->query('SELECT a, b, c FROM tbl WHERE oele = 2 GROUP BY boele');
$rows = $q->num_rows;
// and now use $q to get actual data

С драйверами MySQLi и PgSQL это возможно. Со всеми PDO это не так!?

PS. Моим первоначальным решением было расширить метод SQLResult-> count (мой собственный), чтобы заменить SELECT ... FROM на SELECT COUNT(1) FROM и просто вернуть это число (очень неэффективно, но только для SQLite PDO). Это не достаточно хорошо, потому что в приведенном выше примере запроса есть GROUP BY, который изменит значение / функцию COUNT(1).

Ответы [ 11 ]

44 голосов
/ 07 июня 2011

Вот решение для вас

$sql="SELECT count(*) FROM [tablename] WHERE key == ? ";
$sth = $this->db->prepare($sql);
$sth->execute(array($key));
$rows = $sth->fetch(PDO::FETCH_NUM);
echo $rows[0];
24 голосов
/ 05 июня 2011

Это немного неэффективно с памятью, но если вы все равно используете данные, я использую это часто:

$rows = $q->fetchAll();
$num_rows = count($rows);
8 голосов
/ 29 декабря 2014

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

$query = 'SELECT a, b, c FROM tbl WHERE oele = 2 GROUP BY boele';
$nrows = $db->query("SELECT COUNT(1) FROM ($query) x")->fetchColumn();

Возможно, он не самый эффективный, но кажется надежным, поскольку фактически подсчитывает результаты исходного запроса.

4 голосов
/ 02 июня 2011

Я не использую PDO для MySQL и PgSQL, но я использую для SQLite.Есть ли способ (без полного изменения обратного значения dbal) подсчитывать строки, подобные этой, в PDO?

В соответствии с этим комментарием проблема SQLite была вызвана изменением API в3.x.

Тем не менее, вы, возможно, захотите проверить, как PDO фактически реализует эту функциональность, прежде чем использовать ее.

Я не знаком с ее внутренними компонентами, но я бы с подозрением отнесся кМысль о том, что PDO анализирует ваш SQL (поскольку синтаксическая ошибка SQL появится в журналах БД), не говоря уже о том, чтобы попытаться осмыслить его, чтобы подсчитать строки с использованием оптимальной стратегии.

Предполагая, что это не такдействительно, реалистичные стратегии для него, чтобы он возвращал счетчик всех применимых строк в операторе select, включает в себя манипулирование строкой в ​​предложении limit из вашего оператора SQL и любой из:

  1. Запуск select count ()на него как на подзапрос (таким образом, избегая проблемы, описанной вами в PS);
  2. Открытие курсора, запуск извлечения всех и подсчет строк;или
  3. Если сначала открыть такой курсор и аналогичным образом сосчитать оставшиеся строки.

Однако гораздо лучшим способом подсчета будет выполнение полностью оптимизированного запроса.это будет так.Чаще всего это означает переписывание значимых фрагментов исходного запроса, который вы пытаетесь разбить на страницы - удаление ненужных полей и порядка операций, и т. Д.

Наконец, если ваши наборы данных достаточно велики, что учитывает любыеВ некотором роде вы можете также захотеть исследовать возврат оценки, полученной из статистики , и / или периодическое кэширование результата в Memcache.В какой-то момент иметь точный счет уже бесполезно ...

2 голосов
/ 03 июня 2011

Если вы готовы отказаться от намека на абстракцию, вы можете использовать собственный класс-обертку, который просто передает все через PDO.Скажем, что-то вроде этого: (Предупреждение, код не проверен)

class SQLitePDOWrapper
{
    private $pdo;

    public function __construct( $dns, $uname = null, $pwd = null, $opts = null )
    {
        $this->pdo = new PDO( $dns, $unam, $pwd, $opts ); 
    }
    public function __call( $nm, $args )
    {
        $ret = call_user_func_array( array( $this->pdo, $nm ), $args );
        if( $ret instanceof PDOStatement )
        {
            return new StatementWrapper( $this, $ret, $args[ 0 ] ); 
               // I'm pretty sure args[ 0 ] will always be your query, 
               // even when binding
        }

        return $ret;
    }

}

class StatementWrapper
{
    private $pdo; private $stat; private $query;

    public function __construct( PDO $pdo, PDOStatement $stat, $query )
    {
        $this->pdo  = $pdo;
        $this->stat = $stat;
        this->query = $query;
    }

    public function rowCount()
    {
        if( strtolower( substr( $this->query, 0, 6 ) ) == 'select' )
        {
            // replace the select columns with a simple 'count(*)
            $res = $this->pdo->query( 
                     'SELECT COUNT(*)' . 
                          substr( $this->query, 
                              strpos( strtolower( $this->query ), 'from' ) ) 
                   )->fetch( PDO::FETCH_NUM );
            return $res[ 0 ];
        }
        return $this->stat->rowCount();
    }

    public function __call( $nm, $args )
    {
        return call_user_func_array( array( $this->stat, $nm ), $args );
    }
}
2 голосов
/ 02 июня 2011

Имейте в виду, что PDOStatement равно Traversable. Данный запрос:

$query = $dbh->query('
    SELECT
        *
    FROM
        test
');

Может повторяться:

$it = new IteratorIterator($query);
echo '<p>', iterator_count($it), ' items</p>';

// Have to run the query again unfortunately
$query->execute();
foreach ($query as $row) {
    echo '<p>', $row['title'], '</p>';
}

Или вы можете сделать что-то вроде этого:

$it = new IteratorIterator($query);
$it->rewind();

if ($it->valid()) {
    do {
        $row = $it->current();
        echo '<p>', $row['title'], '</p>';
        $it->next();
    } while ($it->valid());
} else {
    echo '<p>No results</p>';
}
1 голос
/ 18 мая 2011

Может быть, это поможет тебе?

$FoundRows = $DataObject->query('SELECT FOUND_ROWS() AS Count')->fetchColumn();
0 голосов
/ 13 октября 2015

Существует два способа подсчета количества строк.

$query = "SELECT count(*) as total from table1";
$prepare = $link->prepare($query);
$prepare->execute();
$row = $prepare->fetch(PDO::FETCH_ASSOC);
echo $row['total']; // This will return you a number of rows.

Или второй способ

$query = "SELECT field1, field2 from table1";
$prepare = $link->prepare($query);
$prepare->execute();
$row = $prepare->fetch(PDO::FETCH_NUM);
echo $rows[0]; // This will return you a number of rows as well.
0 голосов
/ 13 октября 2015

Это еще один вопрос, который, будучи ошибочно сформулирован, порождает МНОЖЕСТВО ужасных решений, все усложняющих задачу для решения несуществующей проблемы.

Чрезвычайно простое и очевидное правило для любого взаимодействия с базой данныхis

Всегда выбирайте только те данные, которые вам нужны.

С этой точки зрения вопрос неверный, а принятый ответ верный.Но другие предлагаемые решения просто ужасны.

Вопрос в том, «как ошибиться в подсчете».Никогда не следует отвечать на него прямо, но вместо этого единственный правильный ответ: «Никогда не следует выбирать строки для их подсчета. Вместо этого ВСЕГДА просите базу данных подсчитать строки для вас».Это правило настолько очевидно, что просто невозможно увидеть столько попыток нарушить его.

Изучив это правило, мы увидим, что это вопрос SQL , даже не связанный с PDO,И если бы его правильно спросили, с точки зрения SQL, ответ появился бы мгновенно - DISTINCT.

$num = $db->query('SELECT count(distinct boele) FROM tbl WHERE oele = 2')->fetchColumn();

- правильный ответ на этот конкретный вопрос.

Вступительный плакатсобственное решение также приемлемо с точки зрения вышеупомянутого правила, но в общих чертах будет менее эффективным.

0 голосов
/ 13 октября 2015

Как насчет размещения результатов запроса в массиве, где вы можете выполнить подсчет ($ array) и использовать строки результата запроса после?Пример:

$sc='SELECT * FROM comments';
$res=array();
foreach($db->query($sc) as $row){
    $res[]=$row;
}

echo "num rows: ".count($res);
echo "Select output:";
foreach($res as $row){ echo $row['comment'];}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...