Existe-t-il un moyen d'obtenir le nombre de lignes dans toutes les tables d'une base de données MySQL sans exécuter un SELECT count()
sur chaque table?
Existe-t-il un moyen d'obtenir le nombre de lignes dans toutes les tables d'une base de données MySQL sans exécuter un SELECT count()
sur chaque table?
Réponses:
SELECT SUM(TABLE_ROWS)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = '{your_db}';
Remarque dans la documentation: pour les tables InnoDB, le nombre de lignes n'est qu'une estimation approximative utilisée dans l'optimisation SQL. Vous devrez utiliser COUNT (*) pour les nombres exacts (ce qui est plus cher).
Vous pouvez probablement mettre quelque chose ensemble avec la table Tables . Je ne l'ai jamais fait, mais il semble qu'il y ait une colonne pour TABLE_ROWS et une pour TABLE NAME .
Pour obtenir des lignes par table, vous pouvez utiliser une requête comme celle-ci:
SELECT table_name, table_rows
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = '**YOUR SCHEMA**';
Comme @Venkatramanan et d'autres, j'ai trouvé INFORMATION_SCHEMA.TABLES peu fiable (en utilisant InnoDB, MySQL 5.1.44), donnant des nombres de lignes différents chaque fois que je l'exécute, même sur des tables au repos. Voici un moyen relativement hacky (mais flexible / adaptable) de générer une grosse instruction SQL que vous pouvez coller dans une nouvelle requête, sans installer de gemmes Ruby ni d'autres choses.
SELECT CONCAT(
'SELECT "',
table_name,
'" AS table_name, COUNT(*) AS exact_row_count FROM `',
table_schema,
'`.`',
table_name,
'` UNION '
)
FROM INFORMATION_SCHEMA.TABLES
WHERE table_schema = '**my_schema**';
Il produit une sortie comme celle-ci:
SELECT "func" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.func UNION
SELECT "general_log" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.general_log UNION
SELECT "help_category" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.help_category UNION
SELECT "help_keyword" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.help_keyword UNION
SELECT "help_relation" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.help_relation UNION
SELECT "help_topic" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.help_topic UNION
SELECT "host" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.host UNION
SELECT "ndb_binlog_index" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.ndb_binlog_index UNION
Copiez et collez à l'exception de la dernière UNION pour obtenir une sortie agréable comme,
+------------------+-----------------+
| table_name | exact_row_count |
+------------------+-----------------+
| func | 0 |
| general_log | 0 |
| help_category | 37 |
| help_keyword | 450 |
| help_relation | 990 |
| help_topic | 504 |
| host | 0 |
| ndb_binlog_index | 0 |
+------------------+-----------------+
8 rows in set (0.01 sec)
Je viens de courir:
show table status;
Cela vous donnera le nombre de lignes pour CHAQUE table plus un tas d'autres informations. J'avais l'habitude d'utiliser la réponse sélectionnée ci-dessus, mais c'est beaucoup plus facile.
Je ne sais pas si cela fonctionne avec toutes les versions, mais j'utilise 5.5 avec le moteur InnoDB.
SELECT TABLE_NAME,SUM(TABLE_ROWS)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'your_db'
GROUP BY TABLE_NAME;
C'est tout ce dont vous avez besoin.
mysql> SELECT TABLE_NAME,SUM(TABLE_ROWS) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'ngramsdb' GROUP BY TABLE_NAME;
Cette procédure stockée répertorie les tables, compte les enregistrements et produit un nombre total d'enregistrements à la fin.
Pour l'exécuter après avoir ajouté cette procédure:
CALL `COUNT_ALL_RECORDS_BY_TABLE` ();
-
La procédure:
DELIMITER $$
CREATE DEFINER=`root`@`127.0.0.1` PROCEDURE `COUNT_ALL_RECORDS_BY_TABLE`()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE TNAME CHAR(255);
DECLARE table_names CURSOR for
SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE();
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN table_names;
DROP TABLE IF EXISTS TCOUNTS;
CREATE TEMPORARY TABLE TCOUNTS
(
TABLE_NAME CHAR(255),
RECORD_COUNT INT
) ENGINE = MEMORY;
WHILE done = 0 DO
FETCH NEXT FROM table_names INTO TNAME;
IF done = 0 THEN
SET @SQL_TXT = CONCAT("INSERT INTO TCOUNTS(SELECT '" , TNAME , "' AS TABLE_NAME, COUNT(*) AS RECORD_COUNT FROM ", TNAME, ")");
PREPARE stmt_name FROM @SQL_TXT;
EXECUTE stmt_name;
DEALLOCATE PREPARE stmt_name;
END IF;
END WHILE;
CLOSE table_names;
SELECT * FROM TCOUNTS;
SELECT SUM(RECORD_COUNT) AS TOTAL_DATABASE_RECORD_CT FROM TCOUNTS;
END
Manière simple:
SELECT
TABLE_NAME, SUM(TABLE_ROWS)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = '{Your_DB}'
GROUP BY TABLE_NAME;
Exemple de résultat:
+----------------+-----------------+
| TABLE_NAME | SUM(TABLE_ROWS) |
+----------------+-----------------+
| calls | 7533 |
| courses | 179 |
| course_modules | 298 |
| departments | 58 |
| faculties | 236 |
| modules | 169 |
| searches | 25423 |
| sections | 532 |
| universities | 57 |
| users | 10293 |
+----------------+-----------------+
Il y a un peu de hack / contournement à ce problème d'estimation.
Auto_Increment - pour une raison quelconque, cela renvoie un nombre de lignes beaucoup plus précis pour votre base de données si l'incrémentation automatique est configurée sur les tables.
J'ai trouvé cela en explorant pourquoi les informations de la table d'affichage ne correspondaient pas aux données réelles.
SELECT
table_schema 'Database',
SUM(data_length + index_length) AS 'DBSize',
SUM(TABLE_ROWS) AS DBRows,
SUM(AUTO_INCREMENT) AS DBAutoIncCount
FROM information_schema.tables
GROUP BY table_schema;
+--------------------+-----------+---------+----------------+
| Database | DBSize | DBRows | DBAutoIncCount |
+--------------------+-----------+---------+----------------+
| Core | 35241984 | 76057 | 8341 |
| information_schema | 163840 | NULL | NULL |
| jspServ | 49152 | 11 | 856 |
| mysql | 7069265 | 30023 | 1 |
| net_snmp | 47415296 | 95123 | 324 |
| performance_schema | 0 | 1395326 | NULL |
| sys | 16384 | 6 | NULL |
| WebCal | 655360 | 2809 | NULL |
| WxObs | 494256128 | 530533 | 3066752 |
+--------------------+-----------+---------+----------------+
9 rows in set (0.40 sec)
Vous pouvez ensuite facilement utiliser PHP ou autre pour renvoyer le maximum des 2 colonnes de données pour donner la "meilleure estimation" pour le nombre de lignes.
c'est à dire
SELECT
table_schema 'Database',
SUM(data_length + index_length) AS 'DBSize',
GREATEST(SUM(TABLE_ROWS), SUM(AUTO_INCREMENT)) AS DBRows
FROM information_schema.tables
GROUP BY table_schema;
L'incrémentation automatique sera toujours +1 * (nombre de tables), mais même avec 4000 tables et 3 millions de lignes, c'est une précision de 99,9%. Beaucoup mieux que les lignes estimées.
La beauté de ceci est que le nombre de lignes renvoyé dans performance_schema est également effacé pour vous, car le plus grand ne fonctionne pas sur les valeurs nulles. Cependant, cela peut être un problème si vous n'avez pas de tables avec incrémentation automatique.
Vous pouvez essayer ça. Cela fonctionne bien pour moi.
SELECT IFNULL(table_schema,'Total') "Database",TableCount
FROM (SELECT COUNT(1) TableCount,table_schema
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema','mysql')
GROUP BY table_schema WITH ROLLUP) A;
Si vous utilisez la base de données information_schema, vous pouvez utiliser ce code mysql (la partie where fait que la requête n'affiche pas les tables qui ont une valeur nulle pour les lignes):
SELECT TABLE_NAME, TABLE_ROWS
FROM `TABLES`
WHERE `TABLE_ROWS` >=0
La requête suivante produit une requête (nother) qui obtiendra la valeur de count (*) pour chaque table, à partir de chaque schéma, répertoriée dans information_schema.tables. Le résultat complet de la requête présentée ici - toutes les lignes prises ensemble - comprend une instruction SQL valide se terminant par un point-virgule - sans «union» pendante. L'union pendante est évitée en utilisant une union dans la requête ci-dessous.
select concat('select "', table_schema, '.', table_name, '" as `schema.table`,
count(*)
from ', table_schema, '.', table_name, ' union ') as 'Query Row'
from information_schema.tables
union
select '(select null, null limit 0);';
Voici ce que je fais pour obtenir le nombre réel (pas d'utilisation du schéma)
C'est plus lent mais plus précis.
C'est un processus en deux étapes à
Obtenez la liste des tables pour votre base de données. Vous pouvez l'obtenir en utilisant
mysql -uroot -p mydb -e "show tables"
Créez et assignez la liste des tables à la variable de tableau dans ce script bash (séparées par un seul espace comme dans le code ci-dessous)
array=( table1 table2 table3 )
for i in "${array[@]}"
do
echo $i
mysql -uroot mydb -e "select count(*) from $i"
done
Exécuter:
chmod +x script.sh; ./script.sh
Une autre option: pour les non InnoDB, il utilise les données de information_schema.TABLES (car c'est plus rapide), pour InnoDB - sélectionnez count (*) pour obtenir le décompte précis. Il ignore également les vues.
SET @table_schema = DATABASE();
-- or SET @table_schema = 'my_db_name';
SET GROUP_CONCAT_MAX_LEN=131072;
SET @selects = NULL;
SELECT GROUP_CONCAT(
'SELECT "', table_name,'" as TABLE_NAME, COUNT(*) as TABLE_ROWS FROM `', table_name, '`'
SEPARATOR '\nUNION\n') INTO @selects
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = @table_schema
AND ENGINE = 'InnoDB'
AND TABLE_TYPE = "BASE TABLE";
SELECT CONCAT_WS('\nUNION\n',
CONCAT('SELECT TABLE_NAME, TABLE_ROWS FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND ENGINE <> "InnoDB" AND TABLE_TYPE = "BASE TABLE"'),
@selects) INTO @selects;
PREPARE stmt FROM @selects;
EXECUTE stmt USING @table_schema;
DEALLOCATE PREPARE stmt;
Si votre base de données contient beaucoup de grandes tables InnoDB, le comptage de toutes les lignes peut prendre plus de temps.
Voici comment je compte les TABLES et TOUS LES ENREGISTREMENTS en utilisant PHP:
$dtb = mysql_query("SHOW TABLES") or die (mysql_error());
$jmltbl = 0;
$jml_record = 0;
$jml_record = 0;
while ($row = mysql_fetch_array($dtb)) {
$sql1 = mysql_query("SELECT * FROM " . $row[0]);
$jml_record = mysql_num_rows($sql1);
echo "Table: " . $row[0] . ": " . $jml_record record . "<br>";
$jmltbl++;
$jml_record += $jml_record;
}
echo "--------------------------------<br>$jmltbl Tables, $jml_record > records.";
L'affiche voulait le nombre de lignes sans compter, mais n'a pas précisé quel moteur de table. Avec InnoDB, je ne connais qu'une seule façon, c'est de compter.
Voici comment je cueille mes pommes de terre:
# Put this function in your bash and call with:
# rowpicker DBUSER DBPASS DBNAME [TABLEPATTERN]
function rowpicker() {
UN=$1
PW=$2
DB=$3
if [ ! -z "$4" ]; then
PAT="LIKE '$4'"
tot=-2
else
PAT=""
tot=-1
fi
for t in `mysql -u "$UN" -p"$PW" "$DB" -e "SHOW TABLES $PAT"`;do
if [ $tot -lt 0 ]; then
echo "Skipping $t";
let "tot += 1";
else
c=`mysql -u "$UN" -p"$PW" "$DB" -e "SELECT count(*) FROM $t"`;
c=`echo $c | cut -d " " -f 2`;
echo "$t: $c";
let "tot += c";
fi;
done;
echo "total rows: $tot"
}
Je ne fais aucune affirmation à ce sujet autre que le fait que c'est un moyen vraiment laid mais efficace pour obtenir le nombre de lignes dans chaque table de la base de données, quel que soit le moteur de table et sans avoir à avoir la permission d'installer des procédures stockées, et sans avoir besoin d'installer rubis ou php. Oui, c'est rouillé. Oui ça compte. count (*) est précis.
Sur la base de la réponse de @ Nathan ci-dessus, mais sans avoir besoin de "supprimer l'union finale" et avec l'option de trier la sortie, j'utilise le SQL suivant. Il génère une autre instruction SQL qui s'exécute ensuite:
select CONCAT( 'select * from (\n', group_concat( single_select SEPARATOR ' UNION\n'), '\n ) Q order by Q.exact_row_count desc') as sql_query
from (
SELECT CONCAT(
'SELECT "',
table_name,
'" AS table_name, COUNT(1) AS exact_row_count
FROM `',
table_schema,
'`.`',
table_name,
'`'
) as single_select
FROM INFORMATION_SCHEMA.TABLES
WHERE table_schema = 'YOUR_SCHEMA_NAME'
and table_type = 'BASE TABLE'
) Q
Vous avez besoin d'une valeur de group_concat_max_len
variable de serveur suffisamment grande, mais à partir de MariaDb 10.2.4, elle devrait par défaut être 1M.
Le code ci-dessous génère la requête de sélection pour tous les contes. Il suffit de supprimer le dernier "UNION ALL", de sélectionner tous les résultats et de coller une nouvelle fenêtre de requête à exécuter.
SELECT
concat('select ''', table_name ,''' as TableName, COUNT(*) as RowCount from ' , table_name , ' UNION ALL ') as TR FROM
information_schema.tables where
table_schema = 'Database Name'
Si vous voulez les nombres exacts, utilisez le script ruby suivant. Vous avez besoin de Ruby et RubyGems.
Installez les gemmes suivantes:
$> gem install dbi
$> gem install dbd-mysql
Fichier: count_table_records.rb
require 'rubygems'
require 'dbi'
db_handler = DBI.connect('DBI:Mysql:database_name:localhost', 'username', 'password')
# Collect all Tables
sql_1 = db_handler.prepare('SHOW tables;')
sql_1.execute
tables = sql_1.map { |row| row[0]}
sql_1.finish
tables.each do |table_name|
sql_2 = db_handler.prepare("SELECT count(*) FROM #{table_name};")
sql_2.execute
sql_2.each do |row|
puts "Table #{table_name} has #{row[0]} rows."
end
sql_2.finish
end
db_handler.disconnect
Revenez à la ligne de commande:
$> ruby count_table_records.rb
Production:
Table users has 7328974 rows.
Si vous connaissez le nombre de tables et leurs noms, et en supposant qu'elles ont chacune des clés primaires, vous pouvez utiliser une jointure croisée en combinaison avec COUNT(distinct [column])
pour obtenir les lignes provenant de chaque table:
SELECT
COUNT(distinct t1.id) +
COUNT(distinct t2.id) +
COUNT(distinct t3.id) AS totalRows
FROM firstTable t1, secondTable t2, thirdTable t3;
Voici un exemple SQL Fiddle .