2010-09-29 85 views
2

我正在编写CodeIgniter之上的应用程序,以更好地组织我的电子书收藏。我差不多完成了,但是我意识到我的'浏览'页面正在运行太多的查询 - 每本书两个 - 来获取他们的信息。显然不是理想的,特别是因为我有大约1000本书投入这个系统。如何减少获得此结果所需的查询数

我目前有一个模型函数可以获取所有书籍(最终将被修改为接受参数 - 这是下一步),另一个获取每个返回书籍的元信息。第二个功能是对每本书进行两次查询 - 一个用于获取书本表中的信息,另一个用于获取与该书籍相关的标签。下面是这两个型号的功能:

获取图书列表:

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

拿到书元信息:

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; 
} 

我敢肯定有写一个或两个查询方式它会将所有记录收集到可以过滤的对象中,而不必为每个对象编写两个查询,但我不知道应该在哪里开始尝试写入。欢迎任何建议。

谢谢了, 马库斯

回答

1

从这个话题和别人创造一个更好的查询一些帮助,我能够用下面的代码来解决此问题:

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

看起来不错,从我可以告诉。做得好! – 2010-09-30 10:33:56

0

如果我给你的权利:在表有所有关于书的数据: 这样做的:

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

应该回到你的所有有关您的书籍的数据和您不需要再次循环获取数据。

+0

我很好,第一个查询。我宁愿有一个额外的查询,而不是每次有人想要查看特定集时都要将我的书籍表的全部内容保存在内存中。我的问题是关于第二个模型函数。第一个供参考。 – Marcus 2010-09-29 19:42:30

0

我对CodeIgniter根本不熟悉,但我认为有一些可以加入的一般做法。

  • 如果这是一个浏览页面 - 是不是有分页?对结果进行分页应大幅度减少每页加载运行的查询数量。
  • 有一个函数(比如说,get_books_info()),您可以调用该函数检索get_books()函数返回的所有书籍的所有标记&元信息。然后从你的get_book_info()引用该数组。你甚至可以从get_book_info()触发get_books_info() - 所以你只需要做这项工作,如果你需要的数据。我认为这种懒惰的加载。
+0

最终会分页,但我需要首先获得优化的查询,然后将限制和偏移量应用于它们。 – Marcus 2010-09-29 20:16:48

2

你想要做的是:

  1. 抓住所有的书,他们的标签排序列表,
  2. 风格出来为HTML,用标签和评级。

抓住你的书标签一起,有一个变量来跟踪你写了最后的ISBN,只有建立在你的条目中的ISBN变化。所以,拉一组这样的:

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

然后,写出来的“基本信息书”为您循环内的书的每一次变化。很明显,你需要更多的字段,而不仅仅是BookTag(例如,ISBN)。

如果你的亚马逊信息来自亚马逊,你可能会没有选择重复调用他们的API(除非他们有一个“批量”模式或其他你可以提交一系列的ISBN)? 。

+0

同意。我对与亚马逊的多次通话没问题 - 这就是“很高兴有”数据。我已将核心书籍元数据保存到数据库。 我喜欢你展示的样本结果集,但我没有线索如何编写这种查询。任何提示让我开始? – Marcus 2010-09-29 19:44:22

+0

受到你方法的启发,我问了另一个问题,关于如何生成上述结果并基于它实现我的解决方案。代码如下。 – Marcus 2010-09-30 01:13:04

+0

对不起,当你自己解决它时已经睡着了......但很高兴你明白了! – 2010-09-30 10:34:43

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

代码只有答案不是很好的答案 - 请修改您的答案,添加一些评论,只是描述相关的位。 – slugster 2012-10-14 10:16:25