usar una tabla resultados registros mysqli_num_rows mostrar español contar contador consultas consulta como avanzadas sql mysql count performance

sql - registros - mostrar resultados de una consulta en php en una tabla



¿Cómo ajustar una consulta de conteo MySQL de 7 tablas donde las tablas contienen más de 30,000 filas? (6)

Los recuentos son a menudo lentos, ya que requieren recuperar todos los datos devueltos por el cursor para calcular cuántas filas se recuperarán en realidad.

¿Cuánto tiempo lleva contar en cada una de las tablas individuales? Sume el total de veces: si es más de 0.1 milisegundos, no creo que pueda hacer que la consulta se ejecute tan rápido como desee. En lo que respecta a las formas de acelerarlo, podría intentar presionar algunos de los criterios de la cláusula WHERE en una sub selección, como en

select count(distinct this_.id) as y0_ from (select * from gallery where published=?) this_ inner join site site3_ on this_.site_id=site3_.id inner join site_to_tag list7_ on site3_.id=list7_.site_id inner join tag sitetag4_ on list7_.tag_id=sitetag4_.id inner join gallery_to_name names9_ on this_.id=names9_.gallery_id inner join name name2_ on names9_.name_id=name2_.id inner join gallery_to_tag list11_ on this_.id=list11_.gallery_id inner join tag tag1_ on list11_.tag_id=tag1_.id where lower(name2_.value) like ? or tag1_.term=? or lower(site3_.name) like ? or lower(this_.description) like ? or sitetag4_.term=?

¿Cuántos campos hay en cada una de estas tablas? ¿Puedes usar sub selecciones para reducir la cantidad de datos que la base de datos tiene que unir, o realmente necesitas todas las columnas?

La presencia de tres predicados LIKE va a ralentizar las cosas, al igual que el uso de la función LOWER en la cláusula WHERE. Si necesita poder comparar casos que no distingan entre mayúsculas y minúsculas, podría ser mejor tener dos campos, uno en el caso "normal" (como se escribe) y otro en el inferior (o MAYÚSCULAS) para realizar búsquedas insensibles. Puede usar un disparador para mantener el inferior / SUPERIOR sincronizado con la versión de caso ''normal''.

Espero que esto ayude.

EDITAR:

Al observar el resultado de EXPLAIN PLAN, no parece que los campos utilizados en su cláusula WHERE estén indexados, o al menos parece que los índices no se están utilizando. Esto podría ser un subproducto de todos los predicados O en el WHERE. Si estos campos no están indexados, puede intentar indexarlos.

Tengo una consulta sql que cuenta el número de resultados para una consulta compleja. La consulta de selección real es muy rápida cuando se limitan a 20 resultados, pero la versión de recuento demora aproximadamente 4,5 segundos en mis tablas actuales después de muchas optimizaciones.

Si elimino las dos cláusulas join y where de las etiquetas del sitio y las etiquetas de la galería, la consulta se realizará en 1,5 segundos. Si creo 3 consultas separadas, una para seleccionar los sitios de pago, una para seleccionar los nombres y otra para juntar todo, puedo hacer que la consulta baje a .6 segundos, lo que todavía no es lo suficientemente bueno. Esto también me obligaría a utilizar un procedimiento almacenado ya que tendré que hacer un total de 4 consultas en Hibernate.

Para la consulta "tal cual", aquí hay información:

La Handler_read_key es 1746669
El Handler_read_next es 1546324

La mesa de la galería tiene 40,000 filas
La tabla del sitio tiene 900 filas
La tabla de nombres tiene 800 filas
La tabla de etiquetas tiene 3560 filas

Soy bastante nuevo en MySQL y afinación, y tengo índices en:

  • columna ''término'' en la tabla de etiquetas
  • columna ''publicada'' en la mesa de la galería
  • ''valor'' para la tabla de nombres

Estoy buscando que esta consulta llegue a 0.1 milisegundos.

SELECT count(distinct gallery.id) from gallery gallery inner join site site on gallery.site_id = site.id inner join site_to_tag p2t on site.id = p2t.site_id inner join tag site_tag on p2t.tag_id = site_tag.id inner join gallery_to_name g2mn on gallery.id = g2mn.gallery_id inner join name name on g2mn.name_id = name.id inner join gallery_to_tag g2t on gallery.id = g2t.gallery_id inner join tag tag on g2t.tag_id = tag.id where gallery.published = true and ( name.value LIKE ''sometext%'' or tag.term = ''sometext'' or site.`name` like ''sometext%'' or site_tag.term = ''sometext'' )

Explicar datos:

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------------+--------+-------------------------------------------------------------------+--------------------+---------+-------------------------------------------+------+------------------------------------+ | 1 | SIMPLE | site | index | PRIMARY,nameIndex | nameIndex | 258 | NULL | 950 | Using index; Using temporary | | 1 | SIMPLE | gallery | ref | PRIMARY,publishedIndex,FKF44C775296EECE37,publishedSiteIdIndex | FKF44C775296EECE37 | 9 | production.site.id | 20 | Using where | | 1 | SIMPLE | g2mn | ref | PRIMARY,FK3EFFD7F8AFAD7A5E,FK3EFFD7F832C04188 | FK3EFFD7F8AFAD7A5E | 8 | production.gallery.id | 1 | Using index; Distinct | | 1 | SIMPLE | name | eq_ref | PRIMARY,valueIndex | PRIMARY | 8 | production.g2mn.name_id | 1 | Distinct | | 1 | SIMPLE | g2t | ref | PRIMARY,FK3DDB4D63AFAD7A5E,FK3DDB4D63E210FBA6 | FK3DDB4D63AFAD7A5E | 8 | production.g2mn.gallery_id | 2 | Using where; Using index; Distinct | | 1 | SIMPLE | tag | eq_ref | PRIMARY,termIndex | PRIMARY | 8 | production.g2t.tag_id | 1 | Distinct | | 1 | SIMPLE | p2t | ref | PRIMARY,FK29424AB796EECE37,FK29424AB7E210FBA6 | PRIMARY | 8 | production.gallery.site_id | 3 | Using where; Using index; Distinct | | 1 | SIMPLE | site_tag | eq_ref | PRIMARY,termIndex | PRIMARY | 8 | production.p2t.tag_id | 1 | Using where; Distinct | +----+-------------+--------------+--------+-------------------------------------------------------------------+--------------------+---------+-------------------------------------------+------+------------------------------------+

Velocidades de conteo individual:

[SQL] select count(*) from gallery; Affected rows: 0 Time: 0.014ms Results: 40385 [SQL] select count(*) from gallery_to_name; Affected rows: 0 Time: 0.012ms Results: 35615 [SQL] select count(*) from gallery_to_tag; Affected rows: 0 Time: 0.055ms Results: 165104 [SQL] select count(*) from tag; Affected rows: 0 Time: 0.002ms Results: 3560 [SQL] select count(*) from site; Affected rows: 0 Time: 0.001ms Results: 901 [SQL] select count(*) from site_to_tag; Affected rows: 0 Time: 0.003ms Results: 7026


Parece que su cláusula WHERE puede ser el delincuente, especialmente lo siguiente:

lower(name2_.value) like ?

De acuerdo con la documentación de MySQL :

El conjunto de caracteres predeterminado y la intercalación son latin1 y latin1_swedish_ci, por lo que las comparaciones de cadenas no binarias son insensibles a las mayúsculas y minúsculas de forma predeterminada.

Es posible que no necesite la función LOWER () en su cláusula WHERE. Las funciones en el lado izquierdo de la comparación evitan el uso de índices.

¿Cómo son tus valores LIKE ? Si está utilizando un comodín en el lado izquierdo del valor, impide el uso de índices.

Intente reemplazar sus declaraciones OR con UNION .

Intente ejecutar la consulta sin DISTINCT solo para ver cuánto está afectando su consulta.


OR asesina el rendimiento de las consultas, incluso con buenos índices. Se pone peor a medida que las tablas se hacen más grandes.

Esto es terriblemente feo, pero es probable que sea más rápido (a expensas de la legibilidad, obviamente). Si MySQL solo admite CTE, sería mucho, mucho mejor.

También podría buscar escribir un lote corto y seleccionar la parte común de la consulta repetida en una tabla temporal y luego hacer todo lo que corresponda a la tabla temporal. Puede o no tener que indexar la tabla temporal para que esto funcione bien, de hecho depende del recuento de filas.

(Tenga en cuenta que la union ya hace una distinct , por lo que no es necesario volver a hacer la count y obligar a otro tipo)

select count(id) from ( SELECT gallery.id from gallery gallery inner join site site on gallery.site_id = site.id inner join site_to_tag p2t on site.id = p2t.site_id inner join tag site_tag on p2t.tag_id = site_tag.id inner join gallery_to_name g2mn on gallery.id = g2mn.gallery_id inner join name name on g2mn.name_id = name.id inner join gallery_to_tag g2t on gallery.id = g2t.gallery_id inner join tag tag on g2t.tag_id = tag.id where gallery.published = true and name.value like ''sometext%'' UNION SELECT gallery.id from gallery gallery inner join site site on gallery.site_id = site.id inner join site_to_tag p2t on site.id = p2t.site_id inner join tag site_tag on p2t.tag_id = site_tag.id inner join gallery_to_name g2mn on gallery.id = g2mn.gallery_id inner join name name on g2mn.name_id = name.id inner join gallery_to_tag g2t on gallery.id = g2t.gallery_id inner join tag tag on g2t.tag_id = tag.id where gallery.published = true and tag.term = ''sometext'' UNION SELECT gallery.id from gallery gallery inner join site site on gallery.site_id = site.id inner join site_to_tag p2t on site.id = p2t.site_id inner join tag site_tag on p2t.tag_id = site_tag.id inner join gallery_to_name g2mn on gallery.id = g2mn.gallery_id inner join name name on g2mn.name_id = name.id inner join gallery_to_tag g2t on gallery.id = g2t.gallery_id inner join tag tag on g2t.tag_id = tag.id where gallery.published = true and site.`name` like ''sometext%'' UNION SELECT gallery.id from gallery gallery inner join site site on gallery.site_id = site.id inner join site_to_tag p2t on site.id = p2t.site_id inner join tag site_tag on p2t.tag_id = site_tag.id inner join gallery_to_name g2mn on gallery.id = g2mn.gallery_id inner join name name on g2mn.name_id = name.id inner join gallery_to_tag g2t on gallery.id = g2t.gallery_id inner join tag tag on g2t.tag_id = tag.id where gallery.published = true and site_tag.term = ''sometext'' ) as x


Admito que no me tomé el tiempo para entender completamente sus tablas y consultas. Sin embargo, por el tipo de tiempo de respuesta que está solicitando y por la aparente complejidad de las sugerencias actuales, diría que esta es una de esas situaciones en las que (en lugar de pedirle a SQL que cuente todos los registros que deseo contar) mantendría una tabla separada de recuentos siempre actualizados, y siempre actualizará cualquier recuento apropiado con código desencadenado en cualquier registro agregado / cambio / eliminación.

Por ejemplo, imagina un archivo de transacción con un millón de filas, y quiero el total del campo 2. Puedo pedirle a db que SUM () el campo, o puedo mantener un total separado para el campo 2 en una tabla en algún lugar que se ajusta Hora en que se agrega, elimina o se edita el campo 2. Es redundante, pero muy rápido cuando quiero saber el total. Y siempre puedo SUM () si quiero auditar mi total calculado por separado.


Incluí mi esquema de prueba y una secuencia de comandos para producir datos de prueba al final de esta publicación. He utilizado la opción SQL_NO_CACHE para evitar que MySQL SQL_NO_CACHE en caché los resultados de la consulta; esto es solo para probar y, en última instancia, debe eliminarse.

Esta es una idea similar a la propuesta por Donnie, pero la he arreglado un poco. Si he entendido las uniones correctamente, no hay necesidad de repetir todas las uniones en cada selección, ya que cada una es efectivamente independiente de las demás. La cláusula WHERE original estipula que gallery.published debe ser verdadero y luego sigue con una serie de 4 condiciones unidas por OR . Por lo tanto, cada consulta puede ejecutarse por separado. Aquí están las cuatro uniones:

gallery <--> gallery_to_name <--> name gallery <--> gallery_to_tag <--> tag gallery <--> site gallery <--> site <--> site_to_tag <--> tag

Debido a que gallery contiene site_id , en este caso, no hay necesidad de la unión intermedia a través de la tabla del site . La última unión se puede reducir a esto:

gallery <--> site_to_tag <--> tag

Ejecutar cada SELECT separado y usar UNION para combinar los resultados es muy rápido. Los resultados aquí suponen las estructuras de tabla y los índices que se muestran al final de esta publicación:

SELECT SQL_NO_CACHE COUNT(id) AS matches FROM ( (SELECT g.id FROM gallery AS g INNER JOIN site AS s ON s.id = g.site_id WHERE g.published = TRUE AND s.name LIKE ''3GRD%'') UNION (SELECT g.id FROM gallery AS g INNER JOIN gallery_to_name AS g2n ON g2n.gallery_id = g.id INNER JOIN name AS n ON n.id = g2n.name_id WHERE g.published = TRUE AND n.value LIKE ''3GRD%'') UNION (SELECT g.id FROM gallery AS g INNER JOIN gallery_to_tag AS g2t ON g2t.gallery_id = g.id INNER JOIN tag AS gt ON gt.id = g2t.tag_id WHERE g.published = TRUE AND gt.term = ''3GRD'') UNION (SELECT g.id FROM gallery AS g INNER JOIN site_to_tag AS s2t ON s2t.site_id = g.site_id INNER JOIN tag AS st ON st.id = s2t.tag_id WHERE g.published = TRUE AND st.term = ''3GRD'') ) AS totals; +---------+ | matches | +---------+ | 99 | +---------+ 1 row in set (0.00 sec)

La velocidad varía según los criterios de búsqueda. En el siguiente ejemplo, se usa un valor de búsqueda diferente para cada tabla, y el operador LIKE tiene que hacer un poco más de trabajo, ya que ahora hay más coincidencias potenciales para cada uno:

SELECT SQL_NO_CACHE COUNT(id) AS matches FROM ( (SELECT g.id FROM gallery AS g INNER JOIN site AS s ON s.id = g.site_id WHERE g.published = TRUE AND s.name LIKE ''3H%'') UNION (SELECT g.id FROM gallery AS g INNER JOIN gallery_to_name AS g2n ON g2n.gallery_id = g.id INNER JOIN name AS n ON n.id = g2n.name_id WHERE g.published = TRUE AND n.value LIKE ''3G%'') UNION (SELECT g.id FROM gallery AS g INNER JOIN gallery_to_tag AS g2t ON g2t.gallery_id = g.id INNER JOIN tag AS gt ON gt.id = g2t.tag_id WHERE g.published = TRUE AND gt.term = ''3IDP'') UNION (SELECT g.id FROM gallery AS g INNER JOIN site_to_tag AS s2t ON s2t.site_id = g.site_id INNER JOIN tag AS st ON st.id = s2t.tag_id WHERE g.published = TRUE AND st.term = ''3OJX'') ) AS totals; +---------+ | matches | +---------+ | 12505 | +---------+ 1 row in set (0.24 sec)

Estos resultados se comparan favorablemente con una consulta que utiliza varias combinaciones:

SELECT SQL_NO_CACHE COUNT(DISTINCT g.id) AS matches FROM gallery AS g INNER JOIN gallery_to_name AS g2n ON g2n.gallery_id = g.id INNER JOIN name AS n ON n.id = g2n.name_id INNER JOIN gallery_to_tag AS g2t ON g2t.gallery_id = g.id INNER JOIN tag AS gt ON gt.id = g2t.tag_id INNER JOIN site AS s ON s.id = g.site_id INNER JOIN site_to_tag AS s2t ON s2t.site_id = s.id INNER JOIN tag AS st ON st.id = s2t.tag_id WHERE g.published = TRUE AND ( gt.term = ''3GRD'' OR st.term = ''3GRD'' OR n.value LIKE ''3GRD%'' OR s.name LIKE ''3GRD%''); +---------+ | matches | +---------+ | 99 | +---------+ 1 row in set (2.62 sec) SELECT SQL_NO_CACHE COUNT(DISTINCT g.id) AS matches FROM gallery AS g INNER JOIN gallery_to_name AS g2n ON g2n.gallery_id = g.id INNER JOIN name AS n ON n.id = g2n.name_id INNER JOIN gallery_to_tag AS g2t ON g2t.gallery_id = g.id INNER JOIN tag AS gt ON gt.id = g2t.tag_id INNER JOIN site AS s ON s.id = g.site_id INNER JOIN site_to_tag AS s2t ON s2t.site_id = s.id INNER JOIN tag AS st ON st.id = s2t.tag_id WHERE g.published = TRUE AND ( gt.term = ''3IDP'' OR st.term = ''3OJX'' OR n.value LIKE ''3G%'' OR s.name LIKE ''3H%''); +---------+ | matches | +---------+ | 12505 | +---------+ 1 row in set (3.17 sec)

ESQUEMA
Los índices en las columnas id más site.name , name.value y tag.term son importantes:

DROP SCHEMA IF EXISTS `egervari`; CREATE SCHEMA IF NOT EXISTS `egervari`; USE `egervari`; -- ----------------------------------------------------- -- Table `site` -- ----------------------------------------------------- DROP TABLE IF EXISTS `site` ; CREATE TABLE IF NOT EXISTS `site` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT , `name` VARCHAR(255) NOT NULL , INDEX `name` (`name` ASC) , PRIMARY KEY (`id`) ) ENGINE = InnoDB; -- ----------------------------------------------------- -- Table `gallery` -- ----------------------------------------------------- DROP TABLE IF EXISTS `gallery` ; CREATE TABLE IF NOT EXISTS `gallery` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT , `site_id` INT UNSIGNED NOT NULL , `published` TINYINT(1) NOT NULL DEFAULT 0 , PRIMARY KEY (`id`) , INDEX `fk_gallery_site` (`site_id` ASC) , CONSTRAINT `fk_gallery_site` FOREIGN KEY (`site_id` ) REFERENCES `site` (`id` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB; -- ----------------------------------------------------- -- Table `name` -- ----------------------------------------------------- DROP TABLE IF EXISTS `name` ; CREATE TABLE IF NOT EXISTS `name` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT , `value` VARCHAR(255) NOT NULL , INDEX `value` (`value` ASC) , PRIMARY KEY (`id`) ) ENGINE = InnoDB; -- ----------------------------------------------------- -- Table `tag` -- ----------------------------------------------------- DROP TABLE IF EXISTS `tag` ; CREATE TABLE IF NOT EXISTS `tag` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT , `term` VARCHAR(255) NOT NULL , INDEX `term` (`term` ASC) , PRIMARY KEY (`id`) ) ENGINE = InnoDB; -- ----------------------------------------------------- -- Table `gallery_to_name` -- ----------------------------------------------------- DROP TABLE IF EXISTS `gallery_to_name` ; CREATE TABLE IF NOT EXISTS `gallery_to_name` ( `gallery_id` INT UNSIGNED NOT NULL , `name_id` INT UNSIGNED NOT NULL , PRIMARY KEY (`gallery_id`, `name_id`) , INDEX `fk_gallery_to_name_gallery` (`gallery_id` ASC) , INDEX `fk_gallery_to_name_name` (`name_id` ASC) , CONSTRAINT `fk_gallery_to_name_gallery` FOREIGN KEY (`gallery_id` ) REFERENCES `gallery` (`id` ) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_gallery_to_name_name` FOREIGN KEY (`name_id` ) REFERENCES `name` (`id` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB; -- ----------------------------------------------------- -- Table `gallery_to_tag` -- ----------------------------------------------------- DROP TABLE IF EXISTS `gallery_to_tag` ; CREATE TABLE IF NOT EXISTS `gallery_to_tag` ( `gallery_id` INT UNSIGNED NOT NULL , `tag_id` INT UNSIGNED NOT NULL , PRIMARY KEY (`gallery_id`, `tag_id`) , INDEX `fk_gallery_to_tag_gallery` (`gallery_id` ASC) , INDEX `fk_gallery_to_tag_tag` (`tag_id` ASC) , CONSTRAINT `fk_gallery_to_tag_gallery` FOREIGN KEY (`gallery_id` ) REFERENCES `gallery` (`id` ) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_gallery_to_tag_tag` FOREIGN KEY (`tag_id` ) REFERENCES `tag` (`id` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB; -- ----------------------------------------------------- -- Table `site_to_tag` -- ----------------------------------------------------- DROP TABLE IF EXISTS `site_to_tag` ; CREATE TABLE IF NOT EXISTS `site_to_tag` ( `site_id` INT UNSIGNED NOT NULL , `tag_id` INT UNSIGNED NOT NULL , PRIMARY KEY (`site_id`, `tag_id`) , INDEX `fk_site_to_tag_site` (`site_id` ASC) , INDEX `fk_site_to_tag_tag` (`tag_id` ASC) , CONSTRAINT `fk_site_to_tag_site` FOREIGN KEY (`site_id` ) REFERENCES `site` (`id` ) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_site_to_tag_tag` FOREIGN KEY (`tag_id` ) REFERENCES `tag` (`id` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB;

DATOS DE PRUEBA
Esto completa el site con 900 filas, tag con 3560 filas, name con 800 filas y gallery con 40,000 filas, e inserta entradas en las tablas de enlaces:

DELIMITER // DROP PROCEDURE IF EXISTS populate// CREATE PROCEDURE populate() BEGIN DECLARE i INT DEFAULT 0; WHILE i < 900 DO INSERT INTO site (name) VALUES (CONV(i + 1 * 10000, 20, 36)); SET i = i + 1; END WHILE; SET i = 0; WHILE i < 3560 DO INSERT INTO tag (term) VALUES (CONV(i + 1 * 10000, 20, 36)); INSERT INTO site_to_tag (site_id, tag_id) VALUES ( (i MOD 900) + 1, i + 1 ); SET i = i + 1; END WHILE; SET i = 0; WHILE i < 800 DO INSERT INTO name (value) VALUES (CONV(i + 1 * 10000, 20, 36)); SET i = i + 1; END WHILE; SET i = 0; WHILE i < 40000 DO INSERT INTO gallery (site_id, published) VALUES ( (i MOD 900) + 1, i MOD 2 ); INSERT INTO gallery_to_name (gallery_id, name_id) VALUES ( i + 1, (i MOD 800) + 1 ); INSERT INTO gallery_to_tag (gallery_id, tag_id) VALUES ( i + 1, (i MOD 3560) + 1 ); SET i = i + 1; END WHILE; END; // DELIMITER ; CALL populate();


Hm ... solo miro tu publicación durante dos minutos, por lo que mi respuesta podría no ser perfecta ... ¿pero has pensado en introducir una tabla de índices que vincule con las otras entidades?

me gusta

CREATE TABLE `references` `text` VARC>HAR(...) NOT NULL, `name` VARCHAR(255) NOT NULL, `reference_type` WHATEVER, // enum or what suits your needs `reference_id` INTEGER NOT NULL );

Luego solo consulta esta tabla:

SELECT COUNT(*) FROM references WHERE sometext LIKE ...;

Tendría que manejar los casos con ''%text%'' aunque ...

Además, ¿es realmente importante el número de galerías o su consulta solo pretende verificar si existe una sola?