2010-09-29 4 views
2

J'écris une application sur CodeIgniter pour mieux organiser ma collection de livres électroniques. J'ai presque terminé, mais je me rends compte que ma page "parcourir" contient beaucoup trop de requêtes - deux par livre - pour obtenir leurs informations. Évidemment pas du tout idéal, d'autant plus que j'ai environ 1000 livres à mettre dans ce système.Comment réduire le nombre de requêtes nécessaires pour obtenir ce résultat

J'ai actuellement une fonction de modèle qui obtient tous les livres (sera finalement modifié pour prendre des paramètres - c'est l'étape suivante) et un autre qui obtient les méta-informations pour chaque livre retourné. La deuxième fonction est celle qui fait deux requêtes pour chaque livre - une pour obtenir l'information dans la table du livre et une autre pour obtenir les étiquettes associées au livre. Voici les deux fonctions du modèle:

obtenir la liste des livres:

function get_books() { 
    $this->db->select('isbn')->order_by('title'); 
    $query = $this->db->get('books'); 
    $result = $query->result(); 
    return $result; 
} 

Obtenir le livre méta-informations:

function get_book_info($isbn) { 
    // Grab the book from Amazon 
    $amazon = $this->amazon->get_amazon_item($isbn); 

    // Get the book info 
    $this->db->select('title, publisher, date, thumb, filename, pages'); 
    $query = $this->db->get_where('books', array('isbn' => $isbn)); 
    $bookResult = $query->row(); 

    // Get the book's tags 
    $this->db->select('tag'); 
    $this->db->from('tags AS t'); 
    $this->db->join('books_tags AS bt', 'bt.tag_id = t.id', 'left'); 
    $this->db->where('bt.book_id', $isbn); 
    $this->db->order_by('t.tag'); 
    $tagQuery = $this->db->get(); 
    foreach ($tagQuery->result() as $row) { 
     $tagResult[] = $row->tag; 
    } 
    $tagResult = implode(', ', $tagResult); 

    // Send data 
    $data = array(
     'isbn' => $isbn, 
     'thumb' => $bookResult->thumb, 
     'title' => strip_slashes($bookResult->title), 
     'file' => $bookResult->filename, 
     'publisher' => strip_slashes($bookResult->publisher), 
     'date' => date('F j, Y', strtotime($bookResult->date)), 
     'pages' => $bookResult->pages, 
     'tags' => $tagResult, 
     'rating' => $amazon->Items->Item->CustomerReviews->AverageRating, 
     'raters' => $amazon->Items->Item->CustomerReviews->TotalReviews 
    ); 
    return $data; 
} 

Je suis certain qu'il ya un moyen d'écrire un ou deux requêtes Cela va rassembler tous les enregistrements dans des objets que je peux ensuite filtrer, plutôt que d'avoir à écrire deux requêtes pour chacun d'eux, mais je n'ai aucune idée de l'endroit où même commencer à essayer d'écrire cela. Toutes les suggestions sont les bienvenues.

Merci beaucoup, Marcus

Répondre

1

Avec l'aide de ce sujet et d'autres dans la création d'une meilleure requête, j'ai pu résoudre ce avec le code suivant:

function get_book_info() { 

    /* 
    * SELECT b.isbn, b.title, b.publisher, b.date, b.thumb, b.filename, b.pages, t.tag 
    * FROM books AS b 
    * INNER JOIN books_tags AS bt ON b.isbn = bt.book_id 
    * INNER JOIN tags AS t ON bt.tag_id = t.id 
    * ORDER BY b.title, t.tag 
    */ 

    $this->db->select('b.isbn, b.title, b.publisher, b.date, b.thumb, b.filename, b.pages, t.tag'); 
    $this->db->from('books AS b'); 
    $this->db->join('books_tags AS bt', 'b.isbn = bt.book_id', 'inner'); 
    $this->db->join('tags AS t', 'bt.tag_id = t.id', 'inner'); 
    $this->db->order_by('b.title, t.tag'); 
    $query = $this->db->get(); 
    $result = $query->result(); 

    $counter = ''; 
    $record = $meta = $tags = array(); 
    $count = count($result); 
    $i = 1; 

    foreach ($result as $book) { 
     // If this is not the last row 
     if ($i < $count) { 
      // If this is the first appearance of this book 
      if ($counter != $book->isbn) { 
       // If the meta array already exists 
       if ($meta) { 
        // Add the combined tag string to the meta array 
        $meta['tags'] = implode(', ', $tags); 
        // Add the meta array 
        $record[] = $meta; 
        // Empty the tags array 
        $tags = array(); 
       } 
       // Reset the counter 
       $counter = $book->isbn; 
       // Grab the book from Amazon 
       $amazon = $this->amazon->get_amazon_item($book->isbn); 
       // Collect the book information 
       $meta = array(
        'isbn' => $book->isbn, 
        'title' => strip_slashes($book->title), 
        'publisher' => strip_slashes($book->publisher), 
        'date' => date('F j, Y', strtotime($book->date)), 
        'thumb' => $book->thumb, 
        'file' => $book->filename, 
        'pages' => $book->pages, 
        'rating' => $amazon->Items->Item->CustomerReviews->AverageRating, 
        'raters' => $amazon->Items->Item->CustomerReviews->TotalReviews 
       ); 
       // Add the tag to the tags array 
       $tags[] = $book->tag; 
      } else { 
       // All we need is the tag 
       $tags[] = $book->tag; 
      } 
     // If this is the last row 
     } else { 
      // If this is the first appearance of this book 
      if ($counter != $book->isbn) { 
       // Grab the book from Amazon 
       $amazon = $this->amazon->get_amazon_item($book->isbn); 
       // Collect the book information 
       $meta = array(
        'isbn' => $book->isbn, 
        'title' => strip_slashes($book->title), 
        'publisher' => strip_slashes($book->publisher), 
        'date' => date('F j, Y', strtotime($book->date)), 
        'thumb' => $book->thumb, 
        'file' => $book->filename, 
        'pages' => $book->pages, 
        'rating' => $amazon->Items->Item->CustomerReviews->AverageRating, 
        'raters' => $amazon->Items->Item->CustomerReviews->TotalReviews 
       ); 
       // Add the tag to the tags array 
       $tags[] = $book->tag; 
       // Add the combined tag string to the meta array 
       $meta['tags'] = implode(', ', $tags); 
       // Add the meta array 
       $record[] = $meta; 
      } else { 
       // All we need is the tag 
       $tags[] = $book->tag; 
       // Add the combined tag string to the meta array 
       $meta['tags'] = implode(', ', $tags); 
       // Add the meta array 
       $record[] = $meta; 
      } 
     } 
     $i++; 
    } 

    return $record; 
} 

Il peut très bien être une meilleure façon de gérer cela, mais c'est ainsi que ma logique l'a vu. Et une seule requête, totale.

+0

En regardant bien, d'après ce que je peux dire. Bien joué! –

0

Si je vous a raison: dans la table livres il y a toutes les données sur les livres: ce faisant ceci:

$this->db->select('*')->order_by('title'); 
$query = $this->db->get('books'); 
$result = $query->result(); 
return $result; 

devrait vous retourner tous les données sur vos livres et vous ne devriez pas avoir besoin de cycle à nouveau pour obtenir des données.

+0

Je suis d'accord avec la première requête. Je préférerais avoir une requête de plus que de devoir garder en mémoire tout le contenu de ma table de livres chaque fois que quelqu'un voudrait voir un ensemble spécifique. Ma question concerne davantage la fonction du second modèle. Le premier a été fourni pour référence. – Marcus

0

Je ne connais pas du tout CodeIgniter, mais je pense qu'il y a des pratiques générales que vous pouvez intégrer.

  • S'il s'agit d'une page de navigation, n'y a-t-il pas une pagination? Paginer les résultats devrait considérablement réduire le nombre de requêtes que vous devez exécuter par chargement de page.
  • Vous avez une fonction (par exemple, get_books_info()) que vous appelez qui récupère toutes les balises & meta info pour tous les livres retournés par votre fonction get_books(). Puis référence ce tableau à partir de votre get_book_info(). Vous pouvez même déclencher get_books_info() depuis get_book_info() - vous n'avez donc besoin de faire le travail que si vous avez besoin des données. Genre de chargement paresseux je pense.
+0

Il y aura éventuellement une pagination, mais je dois d'abord obtenir les requêtes optimisées, puis leur appliquer les limites et les décalages. – Marcus

2

Qu'est-ce que vous voulez faire est:

  1. Prenez une liste triée de tous vos livres et leurs étiquettes,
  2. leur style comme HTML, avec des étiquettes et notes.

Prenez vos livres et étiquettes ensemble, ont une variable pour garder la trace du dernier ISBN que vous avez écrit sur, et de ne construire que votre entrée lorsque les changements ISBN.Alors, tirez un ensemble comme celui-ci:

Book | Tag 
------ | ---------------- 
Book A | Fiction 
Book A | Fantasy 
Book B | Mystery 
Book C | Science Fiction 

Ensuite, écrivez le « info du livre de base » pour chaque changement de livre dans votre boucle. Évidemment, vous aurez besoin de plus de champs que juste Book et Tag (par exemple, ISBN). Si vos informations Amazon proviennent d'Amazon, vous n'aurez probablement pas le choix d'effectuer des appels répétitifs vers leur API (sauf si elles ont un mode "batch" ou quelque chose, dans lequel vous pouvez soumettre un tableau d'ISBN?) .

+0

D'accord. Je suis d'accord avec les multiples appels à Amazon - c'est tout "agréable d'avoir" des données. J'ai enregistré les métadonnées du livre principal dans la base de données. J'aime l'exemple de résultats que vous montrez, mais je n'ai aucune idée de comment écrire ce genre de requête. Des conseils pour me lancer? – Marcus

+0

Inspiré par votre approche, j'ai posé une autre question sur la façon de générer le résultat ci-dessus et mis en œuvre ma solution basée sur elle. Le code est ci-dessous. – Marcus

+0

Désolé d'avoir été endormi pendant que vous l'avez résolu par vous-même ... mais heureux que vous l'ayez! –

0
function get_book_info() { 

    /* 
    * SELECT b.isbn, b.title, b.publisher, b.date, b.thumb, b.filename, b.pages, t.tag 
    * FROM books AS b 
    * INNER JOIN books_tags AS bt ON b.isbn = bt.book_id 
    * INNER JOIN tags AS t ON bt.tag_id = t.id 
    * ORDER BY b.title, t.tag 
    */ 

    $this->db->select('b.isbn, b.title, b.publisher, b.date, b.thumb, b.filename, b.pages, t.tag'); 
    $this->db->from('books AS b'); 
    $this->db->join('books_tags AS bt', 'b.isbn = bt.book_id', 'inner'); 
    $this->db->join('tags AS t', 'bt.tag_id = t.id', 'inner'); 
    $this->db->order_by('b.title, t.tag'); 
    $query = $this->db->get(); 
    $result = $query->result(); 

    $counter = ''; 
    $record = $meta = $tags = array(); 
    $count = count($result); 
    $i = 1; 

    foreach ($result as $book) { 
     // If this is not the last row 
     if ($i < $count) { 
      // If this is the first appearance of this book 
      if ($counter != $book->isbn) { 
       // If the meta array already exists 
       if ($meta) { 
        // Add the combined tag string to the meta array 
        $meta['tags'] = implode(', ', $tags); 
        // Add the meta array 
        $record[] = $meta; 
        // Empty the tags array 
        $tags = array(); 
       } 
       // Reset the counter 
       $counter = $book->isbn; 
       // Grab the book from Amazon 
       $amazon = $this->amazon->get_amazon_item($book->isbn); 
       // Collect the book information 
       $meta = array(
        'isbn' => $book->isbn, 
        'title' => strip_slashes($book->title), 
        'publisher' => strip_slashes($book->publisher), 
        'date' => date('F j, Y', strtotime($book->date)), 
        'thumb' => $book->thumb, 
        'file' => $book->filename, 
        'pages' => $book->pages, 
        'rating' => $amazon->Items->Item->CustomerReviews->AverageRating, 
        'raters' => $amazon->Items->Item->CustomerReviews->TotalReviews 
       ); 
       // Add the tag to the tags array 
       $tags[] = $book->tag; 
      } else { 
       // All we need is the tag 
       $tags[] = $book->tag; 
      } 
     // If this is the last row 
     } else { 
      // If this is the first appearance of this book 
      if ($counter != $book->isbn) { 
       // Grab the book from Amazon 
       $amazon = $this->amazon->get_amazon_item($book->isbn); 
       // Collect the book information 
       $meta = array(
        'isbn' => $book->isbn, 
        'title' => strip_slashes($book->title), 
        'publisher' => strip_slashes($book->publisher), 
        'date' => date('F j, Y', strtotime($book->date)), 
        'thumb' => $book->thumb, 
        'file' => $book->filename, 
        'pages' => $book->pages, 
        'rating' => $amazon->Items->Item->CustomerReviews->AverageRating, 
        'raters' => $amazon->Items->Item->CustomerReviews->TotalReviews 
       ); 
       // Add the tag to the tags array 
       $tags[] = $book->tag; 
       // Add the combined tag string to the meta array 
       $meta['tags'] = implode(', ', $tags); 
       // Add the meta array 
       $record[] = $meta; 
      } else { 
       // All we need is the tag 
       $tags[] = $book->tag; 
       // Add the combined tag string to the meta array 
       $meta['tags'] = implode(', ', $tags); 
       // Add the meta array 
       $record[] = $meta; 
      } 
     } 
     $i++; 
    } 

    return $record; 
} 
+0

Les réponses au code seulement ne sont pas de bonnes réponses - veuillez rogner votre réponse, ajouter quelques commentaires, juste décrire les bits pertinents. – slugster

Questions connexes