2010-12-07 18 views
7

J'ai ce qui suit lorsqu'il est exécuté seul très rapidement, mais lorsque j'effectue cela pour beaucoup de entity_id les requêtes commencent à prendre de plus en plus de temps (la boucle est un foreach PHP) par exemple cette requête ne prend que 0,078 mais la même requête sur une entité différente dans la boucle dure jusqu'à 2,1 secondes, les requêtes semblent ralentir et ralentir le nombre d'entités que je mets dans la boucle. Pourquoi est-ce? et comment puis-je améliorer/optimiser la requête?optimisation de la vitesse de requête mysql

foreach($entity_ids as $entity_id) { 
    SELECT COUNT(*) as prev, DATE_FORMAT(`created`, '%Y%m%d') AS date_group 
    FROM articles_entities 
    WHERE entity_id = '$entity_id' 
    AND `created` >= DATE_SUB(CURDATE(), INTERVAL 10 DAY) 
    GROUP BY date_group 

    // store result 
} 

je la structure de tableau suivant:

CREATE TABLE `articles_entities` (
    `id` CHAR(36) NOT NULL, 
    `article_id` CHAR(36) NOT NULL, 
    `entity_id` CHAR(36) NOT NULL, 
    `created` DATETIME DEFAULT NULL, 
    `modified` DATETIME DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `article_id` (`article_id`), 
    KEY `entity_id` (`entity_id`), 
    KEY `created` (`created`) 
) ENGINE=MYISAM DEFAULT CHARSET=utf8; 

Répondre

7

Il me semble que vous avez un tableau d'ID, puis que vous voulez tirer des enregistrements de votre table (d'une manière contrôlée par votre déclaration) où le champ ID correspond à l'une des valeurs de tableau. Au lieu d'utiliser un LOOP en PHP pour exécuter plusieurs instructions SQL, la meilleure chose à faire est de construire une instruction principale, puis utiliser PHP pour gérer les résultats. Vous pouvez accomplir cela en utilisant l'instruction SQL IN:

// where $entity_ids is an array eg 1,2,3,4,5 

    $sql="SELECT entity_id AS 'alt_entity_id', COUNT(entity_id) as prev, DATE_FORMAT(`created`, '%Y%m%d') AS 'date_group' 
    FROM articles_entities 
    WHERE entity_id IN ".implode(",",$entity_ids)." 
    AND `created` >= DATE_SUB(CURDATE(), INTERVAL 10 DAY) 
    GROUP BY date_group, entity_id"; 
    // you may wish to revese the group fields, as you require, you may also wish to change the count field to date_group, depending on what you wish to be counted 

Cela exécutera la requête initiale une fois, pour toutes les valeurs id que vous avez, regroupés à la fois par jour et l'identifiant passé. Vous pouvez ensuite utiliser PHP pour filtrer les résultats de l'ID spécifique du jeu de résultats renvoyé.

Ceci est bien plus efficace que le temps système produit en bouclant l'exécution d'une requête.

Votre retour sera resultset ressemblera:

entity_id | count(entity_id) | date_group 
----------|------------------|------------ 
    1  |  3   | 2010-04-01 
    1  |  3   | 2010-03-01 
    1  |  3   | 2010-02-01 
    2  |  2   | 2010-01-01 
    2  |  2   | 2010-02-01 
    3  |  1   | 2010-06-01 
    4  |  2   | 2010-06-01 
    4  |  2   | 2010-02-01 
+0

La seule chose qui manquait à la réponse était la sélection, entity_id AS alt_entity_id car je pouvais déterminer quelle entité appartenait à chacun (notez que je ne pouvais pas sélectionner simplement entity_id car cela donnait des résultats incorrects. Je dois également permuter les groupes, si vous mettez à jour votre réponse, je l'accepterai – Lizard

+0

Heureux que ça aide, j'ai mis à jour ré :) – SW4

2

Je ne sais pas où vous obtenez les valeurs de l'entité pour votre boucle, mais l'exécution de cette requête dans une boucle sera toujours un gros frais généraux de performance. Si vous obtenez le entity_ids d'une requête SQL précédente, il serait plus logique de refactoriser votre SQL pour rejoindre la requête initiale avec la requête de boucle afin que vous retourniez toutes les données dont vous avez besoin dans une seule requête SQL.

+0

j'ai ajouté la boucle php – Lizard

+0

D'où vient entity_ids $ viennent? –

+0

J'ai d'abord sélectionné des articles de la même table qui correspondent à un critère spécifique (plage de dates différente), et renvoyé de ses entités – Lizard

0

À peu près combien d'entités avez-vous affaire?

Pourriez-vous insérer les entites requises dans une table séparée et faire une jointure, plutôt que d'avoir plusieurs requêtes?

0

Prenez tous ids dans un tableau, joignez-vous pour former une chaîne et utiliser "où dans" pour obtenir les détails de manière optimisée

$enitityIDS = array(); 
    foreach($entity_ids as $entity_id) { 
     $enitityIDS[]=$entity_id; 
    } 
    $entityIDString = join(",",$enitityIDS); 

puis faire

SELECT COUNT(*) as prev, DATE_FORMAT(`created`, '%Y%m%d') AS date_group 
    FROM articles_entities 
    WHERE entity_id in (".$entityIDString.") 
    AND `created` >= DATE_SUB(CURDATE(), INTERVAL 10 DAY) 
    GROUP BY date_group, entity_id 

Optimal Way

+1

ne oubliez pas que vous devez également regrouper la requête par le – SW4