Aller au contenu | Aller au menu | Aller à la recherche


SQL "matching found rows" vs "affected rows"

Lors de modifications (UPDATE, DELETE) comment différencier le nombre d'enregistrement qui matche au critère WHERE et le nombre d'enregistrement réellement modifiés par la requête ?

  • "found (matched) rows" le nombre d'enregistrements trouvés (potentiellement modifiable)
  • "affected (changed) rows" le nombre d'enregistrements véritablement changés parmi les trouvés

En introduction, la conclusion

  • client MariaDB/MySQL : "affected (changed) rows" par défaut.
  • client SQLite : "found (matched) rows" uniquement ! pas moyen de le changer ?...
  • PHP + MariaDB/MySQL : "affected (changed) rows" par défaut, possibilité de passer en "found (matched) rows" avec l'option PDO::MYSQL_ATTR_FOUND_ROWS => true dans le connecteur
  • PHP + SQLite : "found (matched) rows" uniquement ! pas moyen de le changer ?..
  • Connecteur/J MySQL +.MariaDB/MySQL : "found (matched) rows" par défaut, possibilité de passer en "affected (changed) rows" avec useAffectedRows=true
  • Connecteur/J MariaDB+.MariaDB/MySQL : "found (matched) rows" par défaut, mais impossible de le changer.

MySQL client (mysql Ver 15.1 Distrib 10.0.27-MariaDB)

MariaDB [test]> SHOW CREATE TABLE table_update_count;
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table              | Create Table                                                                                                                                                                                                                                  |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| table_update_count | CREATE TABLE `table_update_count` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `val` varchar(3) COLLATE latin1_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

MariaDB [test]> TRUNCATE TABLE table_update_count;
Query OK, 0 rows affected (0.07 sec)

MariaDB [test]> INSERT INTO table_update_count (val) VALUES ('AA');
Query OK, 1 row affected (0.01 sec)

MariaDB [test]> INSERT INTO table_update_count (val) VALUES ('AB');
Query OK, 1 row affected (0.01 sec)

MariaDB [test]> UPDATE table_update_count SET val = 'AB';
Query OK, 1 row affected (0.01 sec)
Rows matched: 2  Changed: 1  Warnings: 0

MariaDB [test]> SELECT ROW_COUNT();
+-------------+
| ROW_COUNT() |
+-------------+
|           1 |
+-------------+
1 row in set (0.00 sec)

MariaDB [test]> UPDATE table_update_count SET val = 'AB';
Query OK, 0 rows affected (0.02 sec)
Rows matched: 2  Changed: 0  Warnings: 0

MariaDB [test]> SELECT ROW_COUNT();
+-------------+
| ROW_COUNT() |
+-------------+
|           0 |
+-------------+
1 row in set (0.00 sec)

On constate que MariaDB/MySQL fonctionne en "found rows" :

  • le premier UPDATE matche 2 rows, et change 1 row, puisque l'autre vaut déjà 'AB'
  • le seconde UPDATE matche 2 rows, et ne change rien puisque toutes les valeurs valent déjà 'AB'
  • ROW_COUNT()
    • MariaDB - ROW_COUNT() returns the number of rows updated, inserted or deleted by the preceding statement. This is the same as the row count that the mysql client displays and the value from the mysql_affected_rows() C API function. (Returns the number of affected rows by the last operation associated with mysql).
    • MySQL ROW_COUNT() The number of rows updated.

SQLite

SQLite version 3.15.0 2016-10-14 10:20:30
Enter ".help" for usage hints.
sqlite> DROP TABLE "main"."table_update_count";
sqlite> SELECT CHANGES();
0
sqlite> CREATE  TABLE "main"."table_update_count" ("id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL  UNIQUE , "val" VARCHAR);
sqlite> SELECT CHANGES();
0
sqlite> INSERT INTO table_update_count (val) VALUES ('AA');
sqlite> SELECT CHANGES();
1
sqlite> INSERT INTO table_update_count (val) VALUES ('AB');
sqlite> SELECT CHANGES();
1
sqlite> UPDATE table_update_count SET val = 'AB';
sqlite> SELECT CHANGES();
2
sqlite> UPDATE table_update_count SET val = 'AA' WHERE id = 1;
sqlite> SELECT CHANGES();
1
sqlite> UPDATE table_update_count SET val = 'AB' WHERE id = 1 OR id = 2;
sqlite> SELECT CHANGES();
2
sqlite>

On constate que SQLite fonctionne en "affected rows" :

  • le premier UPDATE matche 2 rows, et bien qu'en seul row soit changé, puisque l'autre vaut déjà 'AB', il indique 2 changements !
  • le seconde UPDATE matche 2 rows, et bien qu'aucun row soit changé, puisque toutes les valeurs valent déjà 'AB', il indique 2 changements !

CHANGES

changes() The changes() function returns the number of database rows that were changed or inserted or deleted by the most recently completed INSERT, DELETE, or UPDATE statement, exclusive of statements in lower-level triggers. The changes() SQL function is a wrapper around the sqlite3_changes() C/C++ function and hence follows the same rules for counting changes.

SQLite C Interface - Count The Number Of Rows Modified int sqlite3_changes(sqlite3*); This function returns the number of rows modified, inserted or deleted by the most recently completed INSERT, UPDATE or DELETE statement on the database connection specified by the only parameter. Executing any other type of SQL statement does not modify the value returned by this function.

PRAGMA count_changes

By default, this deprecated pragma is false and these statements do not return anything. If set to true, each of the mentioned statement will return an one-column, one-row table consisting of a single integer value indicating impacted rows by the operation.

no way for SQLite ?

no way for SQLite !

It's technically impossible in the core of SQLite ? Maybe with a new option. Or just a feature you don't want implement ?
It is a feature I do not what to implement, because it would slow down the code for the overwhelming majority of users who do not want it. (D. Richard Hipp)

MariaDB/MySQL/SQLite avec PHP

PDOStatement::rowCount — Retourne le nombre de lignes affectées par le dernier appel à la fonction PDOStatement::execute()

  • avec MariaDB/MySQL, retourne le nombre véritable de changements
  • avec SQLite, retourne le nombre de rows correspondants au critère WHERE
$dbh = new PDO('sqlite:D:\\SQLite\\my_db_test.sqlite');
$query = "UPDATE table_update_count SET val = 'AB'";
echo $query . PHP_EOL;
$stmt = $dbh->prepare($query);
$stmt->execute();
echo $stmt->rowCount(); // 2 (au lieu d'un seul)

mysqli_result::$num_rows -- mysqli_num_rows — Retourne le nombre de lignes dans un résultat

   /* Détermine le nombre de lignes du jeu de résultats */
   $row_cnt = $result->num_rows;

mysqli::$affected_rows -- mysqli_affected_rows — Retourne le nombre de lignes affectées par la dernière opération MySQL

   $mysqli->affected_rows
   mysqli_affected_rows($link)

MYSQL_ATTR_FOUND_ROWS

MYSQLI_CLIENT_FOUND_ROWS

MariaDB/MySQL avec JAVA/JDBC

Lors d'UPDATE, par défaut, le connecteur JDBC retourne le "matching found rows".
Néanmoins, il est possible d'obtenir le nombre d'"affected rows" du moins avec le connecteur MySQL (mais actuellement pas avec le connecteur MariaDB).
Cela se fait avec l'option de connexion "useAffectedRows=true".

MySQL - Driver/Datasource Class Names, URL Syntax and Configuration Properties for Connector/J

useAffectedRows Default: false

Don't set the CLIENT_FOUND_ROWS flag when connecting to the server (not JDBC-compliant, will break most applications that rely on "found" rows vs. "affected rows" for DML statements), but does cause "correct" update counts from "INSERT ... ON DUPLICATE KEY UPDATE" statements to be returned by the server.

Since version: 5.1.7 (Oct 21, 2008) (Bug #39352 We've added a connection property "useAffectedRows" that allows the driver to connect without setting "CLIENT_FOUND_ROWS", which should be a work-around to this issue, however it can only be set at connect time due to mysqld design, so if your application requires "found rows" functionality in other places, you'll have to maintain two sets of connections.)

If you want the updated count, you can specify useAffectedRows=true as a non-standard URL option.

jdbc:mysql://mariadb.myhost:3306/my_database?useAffectedRows=true
useAffectedRows MariaDB ?

Alternative aux moteurs et connecteurs qui ne retourne pas le "count only changed rows"

  • UPDATE t SET val='AB' WHERE val<>'AB'; -- when updating
  • SELECT COUNT(*) FROM t WHERE val <> 'AB'; -- before updating

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.

Fil des commentaires de ce billet