Creo que he encontrado la solución. Durante algún tiempo estuve buscando en el Servidor Percona para reemplazar mis servidores MySQL, y ahora creo que hay una buena razón para esto.
El servidor Percona presenta muchas nuevas tablas INFORMATION_SCHEMA como INNODB_TABLE_STATS, que no está disponible en el servidor MySQL estándar. Cuando tu lo hagas:
SELECT rows, modified FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table'
Obtiene el recuento real de filas y un contador. La documentación oficial dice lo siguiente sobre este campo:
Si el valor de la columna modificada excede "filas / 16" o 2000000000, el recálculo de estadísticas se realiza cuando innodb_stats_auto_update == 1. Podemos estimar la antigüedad de las estadísticas por este valor.
Por lo tanto, este contador se ajusta de vez en cuando, pero puede hacer una suma de verificación del número de filas y el contador, y luego con cada modificación de la tabla obtendrá una suma de verificación única. P.ej:
SELECT MD5(CONCAT(rows,'_',modified)) AS checksum FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table';
De todos modos, iba a actualizar mis servidores al servidor Percona, por lo que este límite no es un problema para mí. Administrar cientos de desencadenantes y agregar campos a las tablas es un gran problema para esta aplicación, porque es muy tarde en el desarrollo.
Esta es la función PHP que he creado para asegurarme de que las tablas se puedan sumar en cualquier motor y servidor que se use:
function checksum_table($input_tables){
if(!$input_tables) return false; // Sanity check
$tables = (is_array($input_tables)) ? $input_tables : array($input_tables); // Make $tables always an array
$where = "";
$checksum = "";
$found_tables = array();
$tables_indexed = array();
foreach($tables as $table_name){
$tables_indexed[$table_name] = true; // Indexed array for faster searching
if(strstr($table_name,".")){ // If we are passing db.table_name
$table_name_split = explode(".",$table_name);
$where .= "(table_schema='".$table_name_split[0]."' AND table_name='".$table_name_split[1]."') OR ";
}else{
$where .= "(table_schema=DATABASE() AND table_name='".$table_name."') OR ";
}
}
if($where != ""){ // Sanity check
$where = substr($where,0,-4); // Remove the last "OR"
$get_chksum = mysql_query("SELECT table_schema, table_name, rows, modified FROM information_schema.innodb_table_stats WHERE ".$where);
while($row = mysql_fetch_assoc($get_chksum)){
if($tables_indexed[$row[table_name]]){ // Not entirely foolproof, but saves some queries like "SELECT DATABASE()" to find out the current database
$found_tables[$row[table_name]] = true;
}elseif($tables_indexed[$row[table_schema].".".$row[table_name]]){
$found_tables[$row[table_schema].".".$row[table_name]] = true;
}
$checksum .= "_".$row[rows]."_".$row[modified]."_";
}
}
foreach($tables as $table_name){
if(!$found_tables[$table_name]){ // Table is not found in information_schema.innodb_table_stats (Probably not InnoDB table or not using Percona Server)
$get_chksum = mysql_query("CHECKSUM TABLE ".$table_name); // Checksuming the old-fashioned way
$chksum = mysql_fetch_assoc($get_chksum);
$checksum .= "_".$chksum[Checksum]."_";
}
}
$checksum = sprintf("%s",crc32($checksum)); // Using crc32 because it's faster than md5(). Must be returned as string to prevent PHPs signed integer problems.
return $checksum;
}
Puedes usarlo así:
// checksum a signle table in the current db
$checksum = checksum_table("test_table");
// checksum a signle table in db other than the current
$checksum = checksum_table("other_db.test_table");
// checksum multiple tables at once. It's faster when using Percona server, because all tables are checksummed via one select.
$checksum = checksum_table(array("test_table, "other_db.test_table"));
Espero que esto ahorre algunos problemas a otras personas que tienen el mismo problema.