Nombre de lignes avec AOP

192

Il existe de nombreuses déclarations contradictoires. Quelle est la meilleure façon de compter les lignes en utilisant PDO en PHP? Avant d'utiliser PDO, j'ai simplement utilisé mysql_num_rows.

fetchAll est quelque chose que je ne veux pas parce que je peux parfois avoir affaire à de grands ensembles de données, donc pas bon pour mon utilisation.

Avez-vous des suggestions?

James
la source

Réponses:

265
$sql = "SELECT count(*) FROM `table` WHERE foo = ?"; 
$result = $con->prepare($sql); 
$result->execute([$bar]); 
$number_of_rows = $result->fetchColumn(); 

Ce n'est pas la façon la plus élégante de le faire, en plus cela implique une requête supplémentaire.

AOP a PDOStatement::rowCount(), qui ne semble pas fonctionne dans MySql. Quelle douleur.

D'après le document AOP:

Pour la plupart des bases de données, PDOStatement :: rowCount () ne renvoie pas le nombre de lignes affectées par une instruction SELECT. À la place, utilisez PDO :: query () pour émettre une instruction SELECT COUNT (*) avec les mêmes prédicats que votre instruction SELECT prévue, puis utilisez PDOStatement :: fetchColumn () pour récupérer le nombre de lignes qui seront renvoyées. Votre application peut alors effectuer l'action correcte.

EDIT: L'exemple de code ci-dessus utilise une instruction préparée, ce qui est probablement inutile dans de nombreux cas pour compter les lignes, donc:

$nRows = $pdo->query('select count(*) from blah')->fetchColumn(); 
echo $nRows;
karim79
la source
cela signifierait faire une requête de base de données supplémentaire. Je suppose qu'il a déjà effectué une requête de sélection et souhaite maintenant savoir combien de lignes ont été renvoyées.
nickf
1
nickf est correct. mysql_num_rows () ne fonctionnera pas lors de l'utilisation de PDO.
James
Certes, apparemment, il y a PDOStatement :: rowCount () mais cela ne fonctionne pas dans MySql
karim79
11
En utilisant cette approche, fetchColumn()renvoie une chaîne "1234" ... votre EDIT a echo count($nRows);- count()est une fonction de tableau: P. Je recommanderais également de taper le résultat de fetchColumn()vers un entier. $count = (int) $stmt->fetchColumn()
Cobby
1
@ karim79 L'approche d'instruction non préparée renvoie 1 uniquement au lieu du nombre réel de lignes. La déclaration préparée fonctionne très bien. Quel peut être le problème ?
SilentAssassin
86

Comme je l'ai écrit précédemment dans une réponse à une question similaire , la seule raisonmysql_num_rows() fonctionné est qu'il récupérait en interne toutes les lignes pour vous donner cette information, même si cela ne vous paraissait pas.

Ainsi, en AOP, vos options sont:

  1. Utilisez la FOUND_ROWS()fonction de MySQL .
  2. Utilisez la fetchAll()fonction PDO pour récupérer toutes les lignes dans un tableau, puis utilisez- count()le.
  3. Faites une requête supplémentaire SELECT COUNT(*), comme l'a suggéré karim79.
Chad Birch
la source
8
Merci de m'avoir renseigné davantage sur mysql_num_rows (). Cela semble être un goulot d'étranglement important que je me suis donné. Merci encore.
James
2
fetch_all est en fait fetchAll () :)
dynamique
2
L'option 2 n'est pas recommandée si le résultat est important.
Edson Horacio Junior
Le FOUND_ROWS () sera supprimé de MySQL donc veuillez vérifier le lien vers FOUND_ROWS avant de l'utiliser si vous étiez déjà familier avec cela.
anatak
29

Comme cela arrive souvent, cette question est déroutante. Les gens viennent ici avec deux tâches différentes en tête:

  1. Ils ont besoin de savoir combien de lignes dans le tableau
  2. Ils ont besoin de savoir si une requête a renvoyé des lignes

Ce sont deux tâches absolument différentes qui n'ont rien en commun et ne peuvent pas être résolues par la même fonction. Ironiquement, pour aucun d'eux, la PDOStatement::rowCount()fonction réelle ne doit être utilisée.

Voyons pourquoi

Compter les lignes dans le tableau

Avant d'utiliser PDO, j'ai simplement utilisé mysql_num_rows().

Cela signifie que vous avez déjà fait une erreur. Utiliser mysql_num_rows()ou rowCount()pour compter le nombre de lignes dans le tableau est un vrai désastre en termes de consommation des ressources du serveur. Une base de données doit lire toutes les lignes du disque, consommer la mémoire du serveur de base de données, puis envoyer tout ce tas de données à PHP, consommant également la mémoire du processus PHP, alourdissant votre serveur sans aucune raison.
En outre, sélectionner des lignes uniquement pour les compter n'a tout simplement aucun sens. Une count(*)requête doit être exécutée à la place. La base de données comptera les enregistrements hors de l'index, sans lire les lignes réelles , puis une seule ligne retournée.

À cette fin, le code suggéré dans la réponse acceptée est équitable.

Comptage du nombre de lignes renvoyées.

Le deuxième cas d'utilisation n'est pas aussi désastreux qu'inutile: au cas où vous auriez besoin de savoir si votre requête a renvoyé des données, vous avez toujours les données elles-mêmes!

Dites, si vous ne sélectionnez qu'une seule ligne. Très bien, vous pouvez utiliser la ligne récupérée comme indicateur:

$stmt->execute();
$row = $stmt->fetch();
if (!$row) { // here! as simple as that
    echo 'No data found';
}

Au cas où vous auriez besoin d'obtenir plusieurs lignes, vous pouvez utiliser fetchAll().

fetchAll() est quelque chose que je ne veux pas car je peux parfois avoir affaire à de grands ensembles de données

Oui bien sûr, pour le premier cas d'utilisation, ce serait deux fois plus mauvais. Mais comme nous l'avons déjà appris, ne sélectionnez pas les lignes uniquement pour les compter, ni avec rowCount()nifetchAll() .

Mais au cas où vous utiliseriez réellement les lignes sélectionnées, il n'y a rien de mal à utiliser fetchAll(). N'oubliez pas que dans une application Web, vous ne devez jamais sélectionner un grand nombre de lignes. Seules les lignes qui seront effectivement utilisées sur une page Web doivent être sélectionnés, d' où vous avez à utiliser LIMIT, WHEREou une clause similaire dans votre SQL. Et pour une quantité aussi modérée de données, tout est correct fetchAll(). Et encore une fois, utilisez simplement le résultat de cette fonction dans la condition:

$stmt->execute();
$data = $stmt->fetchAll();
if (!$data) { // again, no rowCount() is needed!
    echo 'No data found';
}

Et bien sûr, ce sera une folie absolue d'exécuter une requête supplémentaire uniquement pour dire si votre autre requête a renvoyé des lignes, comme cela a été suggéré dans les deux premières réponses.

Compter le nombre de lignes dans un grand jeu de résultats

Dans un cas aussi rare où vous avez besoin de sélectionner une très grande quantité de lignes (dans une application console par exemple), vous devez utiliser une requête sans tampon , afin de réduire la quantité de mémoire utilisée. Mais c'est le cas réel lorsqu'il rowCount() ne sera pas disponible , donc cette fonction ne sert pas non plus.

C'est donc le seul cas d'utilisation où vous devrez peut-être exécuter une requête supplémentaire, au cas où vous auriez besoin de connaître une estimation proche du nombre de lignes sélectionnées.

Votre bon sens
la source
il est utile si l'API a besoin d'imprimer le total des résultats d'une requête de recherche. il ne vous donnera que 10 ou 15 lignes en retour, mais il devrait également vous indiquer qu'il y a 284 résultats au total.
Andres SK
4
@andufo Ce n'est pas le cas. N'oubliez pas: un développeur ne devrait jamais le faire de cette façon. La requête de recherche ne doit jamais renvoyer les 284 lignes. 15 doivent être renvoyés pour afficher et une ligne d'une requête distincte pour indiquer que 284 ont été trouvés.
Your Common Sense
2
C'est un très bon point - peu intuitif au début, mais valable. La plupart des gens oublient que deux requêtes SQL simples sont bien plus rapides qu'une requête légèrement plus grande. Pour justifier tout comptage, vous devez avoir une requête très lente qui ne peut pas être optimisée et qui donnera probablement peu de résultats. Merci d'avoir fait remarquer cela!
PeerBr
@Your Common Sense: Assurez-vous simplement que fetchAll () ne sera pas une mauvaise idée si le jeu de résultats est très grand? Ne serait-il pas préférable d'utiliser fetch () pour obtenir des données successives.
timmornYE
@timmornYE c'est exactement ce qui est dit dans le dernier paragraphe de ma réponse
Votre bon sens
21

C'est super tard, mais j'ai rencontré le problème et je fais ceci:

function countAll($table){
   $dbh = dbConnect();
   $sql = "select * from `$table`";

   $stmt = $dbh->prepare($sql);
    try { $stmt->execute();}
    catch(PDOException $e){echo $e->getMessage();}

return $stmt->rowCount();

C'est vraiment simple et facile. :)

Dan
la source
19
Sélectionner toutes les données uniquement pour les compter est contraire aux règles les plus élémentaires de l'interaction avec la base de données.
Votre bon sens
Vous souhaitez peut-être avoir une barre de progression pour toutes les valeurs renvoyées, vous devez donc connaître le nombre de lignes à l'avance.
Jonny
18

J'ai fini par utiliser ceci:

$result = $db->query($query)->fetchAll();

if (count($result) > 0) {
    foreach ($result as $row) {
        echo $row['blah'] . '<br />';
    }
} else {
    echo "<p>Nothing matched your query.</p>";
}
Eric Warnke
la source
12

Cet article est ancien mais obtenir le nombre de lignes en php avec PDO est simple

$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
TenTen Peter
la source
3
Voir la documentation citée dans la réponse de karim79. Cela fonctionne parfois mais n'est pas fiable.
octern
5

Ceci est un vieil article, mais frustré à la recherche d'alternatives. Il est très regrettable que PDO ne dispose pas de cette fonctionnalité, d'autant plus que PHP et MySQL ont tendance à aller de pair.

Il y a une faille malheureuse dans l'utilisation de fetchColumn () car vous ne pouvez plus utiliser ce jeu de résultats (effectivement) lorsque fetchColumn () déplace l'aiguille vers la ligne suivante. Par exemple, si vous obtenez un résultat similaire à

  1. Fruit-> Banane
  2. Fruit-> Pomme
  3. Fruits-> Orange

Si vous utilisez fetchColumn (), vous pouvez découvrir qu'il y a 3 fruits retournés, mais si vous parcourez maintenant le résultat, vous n'avez que deux colonnes, Le prix de fetchColumn () est la perte de la première colonne de résultats juste pour trouver sur le nombre de lignes renvoyées. Cela conduit à un codage bâclé et à des résultats totalement erronés s'il est mis en œuvre.

Alors maintenant, en utilisant fetchColumn (), vous devez implémenter un appel et une requête MySQL entièrement nouveaux juste pour obtenir un nouvel ensemble de résultats de travail. (qui, espérons-le, n'a pas changé depuis votre dernière requête), je sais, peu probable, mais cela peut arriver. En outre, la surcharge des requêtes doubles lors de la validation du nombre de lignes. Ce qui pour cet exemple est petit, mais l'analyse de 2 millions de lignes sur une requête jointe, ce n'est pas un prix agréable à payer.

J'adore PHP et je soutiens toutes les personnes impliquées dans son développement ainsi que la communauté en général utilisant PHP au quotidien, mais j'espère vraiment que cela sera abordé dans les prochaines versions. C'est «vraiment» mon seul reproche avec PHP PDO, qui autrement est une excellente classe.

Eric
la source
2

Répondre à cela parce que je me suis pris au piège en le sachant maintenant et peut-être que ce sera utile.

Gardez à l'esprit que vous ne pouvez pas récupérer les résultats deux fois. Vous devez enregistrer le résultat de l'extraction dans un tableau, obtenir le nombre de lignes par count($array)et afficher les résultats avec foreach. Par exemple:

$query = "your_query_here";
$STH = $DBH->prepare($query);
$STH->execute();
$rows = $STH->fetchAll();
//all your results is in $rows array
$STH->setFetchMode(PDO::FETCH_ASSOC);           
if (count($rows) > 0) {             
    foreach ($rows as $row) {
        //output your rows
    }                       
}
débutant csharp
la source
1

Si vous voulez juste obtenir un nombre de lignes (pas les données) ie. en utilisant COUNT (*) dans une instruction préparée, tout ce que vous avez à faire est de récupérer le résultat et de lire la valeur:

$sql = "SELECT count(*) FROM `table` WHERE foo = bar";
$statement = $con->prepare($sql); 
$statement->execute(); 
$count = $statement->fetch(PDO::FETCH_NUM); // Return array indexed by column number
return reset($count); // Resets array cursor and returns first value (the count)

En fait, récupérer toutes les lignes (données) pour effectuer un comptage simple est un gaspillage de ressources. Si le jeu de résultats est volumineux, votre serveur risque de s'étouffer.

poisson fou
la source
1

Pour utiliser des variables dans une requête, vous devez utiliser bindValue()ou bindParam(). Et ne concaténez pas les variables avec" . $variable . "

$statement = "SELECT count(account_id) FROM account
                  WHERE email = ? AND is_email_confirmed;";
$preparedStatement = $this->postgreSqlHandler->prepare($statement);
$preparedStatement->bindValue(1, $account->getEmail());
$preparedStatement->execute();
$numberRows= $preparedStatement->fetchColumn();

GL

Braian Coronel
la source
0

Quand il s'agit de mysql comment compter ou obtenir combien de lignes dans une table avec PHP PDO j'utilise ceci

// count total number of rows
$query = "SELECT COUNT(*) as total_rows FROM sometable";
$stmt = $con->prepare($query);

// execute query
$stmt->execute();

// get total rows
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$total_rows = $row['total_rows'];

les crédits vont à Mike @ codeofaninja.com

Robert
la source
-1

Une doublure rapide pour récupérer la première entrée. C'est bien pour les requêtes très basiques.

<?php
$count = current($db->query("select count(*) from table")->fetch());
?>

Référence

itsazzad
la source
-1

J'ai essayé $count = $stmt->rowCount();avec Oracle 11.2 et cela n'a pas fonctionné. J'ai décidé d'utiliser une boucle for comme indiqué ci-dessous.

   $count =  "";
    $stmt =  $conn->prepare($sql);
    $stmt->execute();
   echo "<table border='1'>\n";
   while($row = $stmt->fetch(PDO::FETCH_OBJ)) {
        $count++;
        echo "<tr>\n";
    foreach ($row as $item) {
    echo "<td class='td2'>".($item !== null ? htmlentities($item, ENT_QUOTES):"&nbsp;")."</td>\n";
        } //foreach ends
        }// while ends
        echo "</table>\n";
       //echo " no of rows : ". oci_num_rows($stmt);
       //equivalent in pdo::prepare statement
       echo "no.of rows :".$count;
manas mukherjee
la source
-1

Pour les requêtes simples où je veux une ligne spécifique et que je veux savoir si elle a été trouvée, j'utilise quelque chose comme:

function fetchSpecificRow(&$myRecord) {
    $myRecord = array();
    $myQuery = "some sql...";
    $stmt = $this->prepare($myQuery);
    $stmt->execute(array($parm1, $parm2, ...));
    if ($myRecord = $stmt->fetch(PDO::FETCH_ASSOC)) return 0;
    return $myErrNum;
}
user5862537
la source
-1

Il y a une solution simple. Si vous utilisez PDO, connectez-vous à votre base de données comme ceci:

try {
    $handler = new PDO('mysql:host=localhost;dbname=name_of_your_db', 'your_login', 'your_password'); 
    $handler -> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} 
    catch (PDOException $e) {   
    echo $e->getMessage();
}

Ensuite, la requête à DB sera:

$query = $handler->query("SELECT id FROM your_table WHERE ...");

Et enfin, pour compter les lignes correspondant à votre requête, écrivez comme ceci

$amountOfRows = $query->rowcount();
Vlad
la source
Pourquoi cela me renvoie -1 la plupart du temps? Même s'il y a des données. .
Ingus
-1

fetchColumn ()

utilisé si vous voulez obtenir le décompte des enregistrements [effisien]

$sql   = "SELECT COUNT(*) FROM fruit WHERE calories > 100";
$res   = $conn->query($sql);
$count = $res->fetchColumn(); // ex = 2

requete()

utilisé si vous souhaitez récupérer des données et le nombre d'enregistrements [options]

$sql = "SELECT * FROM fruit WHERE calories > 100";
$res = $conn->query($sql);

if ( $res->rowCount() > 0) {

    foreach ( $res as $row ) {
        print "Name: {$row['NAME']} <br />";
    }

}
else {
    print "No rows matched the query.";
}

PDOStatement :: rowCount

antelove
la source
Vous ne devriez jamais faire ça. Sur une table plus ou moins grande, il consommera toute la mémoire du serveur et plantera le serveur
Votre bon sens
Maintenant, votre réponse ne fait que dupliquer une douzaine de réponses existantes.
Votre bon sens
-2

lorsque vous faites un COUNT (*) dans votre instruction mysql comme dans

$q = $db->query("SELECT COUNT(*) FROM ...");

votre requête mysql compte déjà le nombre de résultat pourquoi compter à nouveau en php? pour obtenir le résultat de votre mysql

$q = $db->query("SELECT COUNT(*) as counted FROM ...");
$nb = $q->fetch(PDO::FETCH_OBJ);
$nb = $nb->counted;

et $nbcontiendra l'entier que vous avez compté avec votre instruction mysql un peu longue à écrire mais rapide à exécuter

Edit: désolé pour le mauvais post, mais comme un exemple montre une requête avec count in, je suggérais d'utiliser le résultat mysql, mais si vous n'utilisez pas le count dans sql fetchAll () est efficace, si vous enregistrez le résultat dans une variable vous ne perdrez pas une ligne.

$data = $dbh->query("SELECT * FROM ...");
$table = $data->fetchAll(PDO::FETCH_OBJ);

count($table)renverra le nombre de lignes et vous pourrez toujours utiliser le résultat après comme $row = $table[0] ou en utilisant unforeach

foreach($table as $row){
  print $row->id;
}
Surpris
la source
1
La question est de savoir comment obtenir le compte lorsque vous NE faites PAS de COUNT (*) dans votre déclaration mysql
Votre bon sens
-2

Voici une extension personnalisée de la classe PDO, avec une fonction d'assistance pour récupérer le nombre de lignes incluses par le critère "WHERE" de la dernière requête.

Cependant, vous devrez peut-être ajouter plus de «gestionnaires», selon les commandes que vous utilisez. Pour le moment, cela ne fonctionne que pour les requêtes qui utilisent "FROM" ou "UPDATE".

class PDO_V extends PDO
{
    private $lastQuery = null;

    public function query($query)
    {
        $this->lastQuery = $query;    
        return parent::query($query);
    }
    public function getLastQueryRowCount()
    {
        $lastQuery = $this->lastQuery;
        $commandBeforeTableName = null;
        if (strpos($lastQuery, 'FROM') !== false)
            $commandBeforeTableName = 'FROM';
        if (strpos($lastQuery, 'UPDATE') !== false)
            $commandBeforeTableName = 'UPDATE';

        $after = substr($lastQuery, strpos($lastQuery, $commandBeforeTableName) + (strlen($commandBeforeTableName) + 1));
        $table = substr($after, 0, strpos($after, ' '));

        $wherePart = substr($lastQuery, strpos($lastQuery, 'WHERE'));

        $result = parent::query("SELECT COUNT(*) FROM $table " . $wherePart);
        if ($result == null)
            return 0;
        return $result->fetchColumn();
    }
}
Venryx
la source
Le problème ne vaut pas la peine. Un tel numéro étant si rarement nécessaire qu'on n'a pas besoin d'une extension dédiée. Sans oublier qu'il ne prend pas en charge les déclarations préparées - la seule raison d'utiliser PDO.
Your Common Sense
-2

Vous pouvez combiner la meilleure méthode en une seule ligne ou fonction et faire en sorte que la nouvelle requête soit générée automatiquement pour vous:

function getRowCount($q){ 
    global $db;
    return $db->query(preg_replace('/SELECT [A-Za-z,]+ FROM /i','SELECT count(*) FROM ',$q))->fetchColumn();
}

$numRows = getRowCount($query);
Bryan
la source
Rien de mieux dans cette méthode. Exécuter une requête supplémentaire uniquement pour savoir combien de lignes ont été renvoyées n'a absolument aucun sens.
Votre bon sens
-2
<table>
      <thead>
           <tr>
                <th>Sn.</th>
                <th>Name</th>
           </tr>
      </thead>
      <tbody>
<?php
     $i=0;
     $statement = $db->prepare("SELECT * FROM tbl_user ORDER BY name ASC");
     $statement->execute();
     $result = $statement->fetchColumn();
     foreach($result as $row) {
        $i++;
    ?>  
      <tr>
         <td><?php echo $i; ?></td>
         <td><?php echo $row['name']; ?></td>
      </tr>
     <?php
          }
     ?>
     </tbody>
</table>
Amranur Rahman
la source
1
C'est une très mauvaise idée. Sélectionner toutes les lignes, c'est un énorme gaspillage de bande passante.
Nafees
-2
function count_x($connect) {  
 $query = "  SELECT * FROM tbl WHERE id = '0' ";  
 $statement = $connect->prepare($query);  $statement->execute();  
 $total_rows = $statement->rowCount();  
 return $total_rows; 
}
alpazull
la source
-4

Utilisez le paramètre array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL), sinon affichez -1:

Usen Parametro array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL), Sin ello Vente -1

exemple:

$res1 = $mdb2->prepare("SELECT clave FROM $tb WHERE id_usuario='$username' AND activo=1 and id_tipo_usuario='4'", array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
$res1->execute();

$count=$res1->rowCount();
echo $count;
Victor Idrogo Aliaga
la source