Comment obtenir le nombre total de lignes d'un groupe par requête?

Du manuel AOP:

PDOStatement:: rowCount () renvoie le nombre de lignes affectées par la dernière supprimer, insérer ou mettre à jour instruction exécuté par le correspondant Objet PDOStatement.

Si la dernière instruction SQL exécutée par le PDOStatement associé était un SELECTinstruction, certaines bases de données peuvent renvoie le nombre de lignes renvoyées par cette déclaration. Cependant, ce le comportement est pas garanti pour tous bases de données et ne doivent pas être utilisées pour les applications portables.

Je l'ai découvert très récemment. Je venais de changer ma couche d'abstraction db pour ne plus utiliser SELECT COUNT(1) ..., car interroger les lignes réelles et compter le résultat serait beaucoup plus efficace. Et maintenant PDO ne supporte pas cela!?

Je n'utilise pas PDO pour MySQL et PgSQL, mais je le fais pour SQLite. Existe-t-il un moyen (sans changer complètement le DBAL back) de compter les lignes comme ça en PDO? Dans MySQL, ce serait quelque chose comme ceci:

$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

Avec les pilotes MySQLi et PgSQL, c'est possible. Avec tous les PDO, il ne l'est pas!?

PS. Ma solution initiale était d'étendre la méthode SQLResult- > count (la mienne) pour remplacer SELECT ... FROM par SELECT COUNT(1) FROM et juste retourner ce nombre (très inefficace, mais seulement pour SQLite PDO). Ce n'est pas assez bon cependant, car dans l'exemple de requête ci-dessus est un GROUP BY, ce qui changerait la signification/fonction du COUNT(1).

44
demandé sur Rudie 2011-05-18 12:29:37

11 réponses

Voici la solution pour vous

$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];
44
répondu RoboTamer 2017-12-06 14:17:38

C'est un peu inefficace en mémoire mais si vous utilisez les données de toute façon, je l'utilise fréquemment:

$rows = $q->fetchAll();
$num_rows = count($rows);
24
répondu mjec 2011-06-05 13:12:59

La méthode que j'ai fini par utiliser est très simple:

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

N'est peut-être pas le plus efficace, mais il semble infaillible, car il compte réellement les résultats de la requête d'origine.

8
répondu Rudie 2014-12-29 12:12:30

Je n'utilise pas PDO pour MySQL et PgSQL, mais je le fais pour SQLite. Existe-t-il un moyen (sans changer complètement le DBAL) de compter les lignes comme ceci dans PDO?

En conséquence de ce commentaire , le problème SQLite a été introduit par un changement D'API dans 3.X.

Cela dit, vous voudrez peut-être inspecter comment PDO implémente réellement la fonctionnalité avant de l'utiliser.

Je ne suis pas familier avec ses internes mais je serais méfiant à L'idée que PDO analyse votre SQL (puisqu'une erreur de syntaxe SQL apparaîtrait dans les journaux de la base de données) et encore moins essaie d'en donner le moindre sens afin de compter les lignes en utilisant une stratégie optimale.

En supposant que ce n'est pas le cas, les stratégies réalistes pour renvoyer un nombre de toutes les lignes applicables dans une instruction select incluent string-manipulant la clause limit de votre instruction SQL, et l'une des options suivantes:

  1. exécuter un select count() en tant que sous-requête (évitant ainsi le problème que vous avez décrit dans votre PS);
  2. ouvrir un curseur, exécuter fetch all et compter les lignes; ou
  3. après avoir ouvert un tel curseur en premier lieu, et de même compter les lignes restantes.

Une bien meilleure façon de compter, cependant, serait d'exécuter la requête entièrement optimisée qui le fera. Le plus souvent, cela signifie réécrire des morceaux significatifs de la requête initiale que vous essayez de paginer-supprimer les champs inutiles et les opérations order by, etc.

Enfin, si vos données les ensembles sont assez grands pour compter tout type de décalage, vous pouvez également étudier le retour de l'estimation dérivée des statistiques à la place, et/ou la mise en cache périodique du résultat dans Memcache. À un moment donné, avoir des comptes précisément corrects n'est plus utile...

4
répondu Denis de Bernardy 2011-06-02 09:35:50

Gardez à l'esprit qu'un PDOStatement est Traversable. Étant donné une requête:

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

Il peut être itéré sur:

$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>';
}

, Ou vous pouvez faire quelque chose comme ceci:

$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>';
}
2
répondu Nev Stokes 2011-06-02 18:19:33

Si vous êtes prêt à abandonner un soupçon d'abstraction, alors vous pouvez utiliser une classe wrapper personnalisée qui passe simplement tout au PDO. Dites, quelque chose comme ceci: (avertissement, code non testé)

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
répondu cwallenpoole 2011-06-03 11:05:07

Peut-être que cela fera l'affaire pour vous?

$FoundRows = $DataObject->query('SELECT FOUND_ROWS() AS Count')->fetchColumn();
1
répondu tradyblix 2011-05-18 08:40:51

Vous devez utiliser rowCount-renvoie le nombre de lignes affectées par la dernière instruction SQL

$query = $dbh->prepare("SELECT * FROM table_name");
$query->execute();
$count =$query->rowCount();
echo $count;
0
répondu Rahul Saxena 2015-05-08 09:42:14

Qu'en est-il de mettre les résultats de la requête dans un tableau, où vous pouvez faire un compte ($array) et utiliser les lignes résultantes de la requête après? Exemple:

$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'];}
0
répondu XaviQV 2015-10-12 22:01:09

C'est encore une autre question, qui, étant mal posée, engendre beaucoup de solutions terribles, toutes rendant les choses terriblement compliquées pour résoudre un problème inexistant.

La règle extrêmement simple et évidente pour toute interaction de base de données est

, sélectionnez Toujours les seules données dont vous avez besoin.

De ce point de vue, la question est fausse et la réponse acceptée est juste. Mais d'autres solutions proposées sont tout simplement terribles.

La question Est "comment obtenir le compte mal façon". On ne devrait jamais y répondre directement, mais à la place, la seule réponse appropriée est "on ne devrait jamais sélectionner les lignes pour les Compter. Au lieu de cela, demandez toujours à la base de données de compter les lignes pour vous."Cette règle est si évidente, qu'il est tout simplement improbable de voir autant de tentatives de la briser.

Après avoir appris cette règle, nous verrions qu'il s'agit d'une question SQL , même pas liée au PDO. Et, s'il était demandé correctement, du point de vue SQL, la réponse apparaîtrait en un instant - DISTINCT.

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

Est la bonne réponse à cette question particulière.

La solution de l'affiche d'ouverture est également acceptable du point de vue de la règle susmentionnée, mais serait moins efficace en termes généraux.

0
répondu Your Common Sense 2015-10-13 05:54:56

Il y a deux façons de compter le nombre de lignes.

$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.

Ou la deuxième voie est

$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.
-1
répondu php-coder 2015-10-13 06:16:12