【问题标题】:How to reduce the number of queries required to get this result如何减少获得此结果所需的查询次数
【发布时间】:2010-09-29 16:03:08
【问题描述】:

我正在 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;
}

我确信有一种方法可以编写一两个查询,将所有记录收集到我可以过滤的对象中,而不必为每个查询编写两个查询,但我什至不知道从哪里开始试着写那个。欢迎任何建议。

非常感谢, 马库斯

【问题讨论】:

    标签: php mysql activerecord codeigniter


    【解决方案1】:

    你想做的是:

    1. 获取所有书籍及其标签的排序列表,
    2. 将它们设置为 HTML,带有标签和评级。

    将您的图书标签放在一起,有一个变量来跟踪您写出的最后一个 ISBN,并且仅在 ISBN 更改时才建立您的条目。所以,拉一个这样的集合:

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

    然后,每次图书在您的循环中发生变化时,写出“基本图书信息”。显然,您需要的字段不仅仅是 BookTag(例如 ISBN)。

    如果您的亚马逊信息来自亚马逊,您可能无法选择重复调用他们的 API(除非他们有“批处理”模式或其他方式,您可以在其中提交一系列 ISBN?)。

    【讨论】:

    • 同意。我对多次致电亚马逊没有意见 - 这都是“很高兴拥有”数据。我已将核心书籍元数据保存到数据库中。我喜欢您展示的示例结果集,但我不知道如何编写这种查询。有什么提示可以帮助我入门吗?
    • 受您的方法启发,我问了另一个关于如何生成上述结果的问题,并基于它实现了我的解决方案。代码如下。
    • 很抱歉在你自己解决的时候睡着了……但很高兴你明白了!
    【解决方案2】:

    在本主题的一些帮助下以及在创建更好查询的其他帮助下,我能够使用以下代码解决此问题:

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

    很可能有更好的方法来处理这个问题,但这就是我的逻辑所见。总共只有一个查询。

    【讨论】:

      【解决方案3】:

      如果我没看错的话:在 books 表中包含有关书籍的所有数据: 这样做:

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

      应该向您返回有关您图书的所有数据,并且您不需要再次循环获取数据。

      【讨论】:

      • 第一个查询没问题。我宁愿有一个额外的查询,也不愿每次有人想要查看特定集合时都将我的书籍表的全部内容保存在内存中。我的问题更多关于第二个模型函数。第一个供参考。
      【解决方案4】:

      我对 CodeIgniter 完全不熟悉,但我认为您可以结合一些通用实践。

      • 如果这是一个浏览页面 - 不是有分页吗?对结果进行分页应该会大大减少每次页面加载必须运行的查询数量。
      • 有一个您调用的函数(例如,get_books_info())检索您的get_books() 函数返回的所有书籍的所有标签和元信息。然后从您的get_book_info() 引用该数组。您甚至可以从get_book_info() 触发get_books_info() - 因此您只需要在需要数据时进行工作。我认为有点延迟加载。

      【讨论】:

      • 最终会有分页,但我需要先获得优化的查询,然后再对它们应用限制和偏移量。
      【解决方案5】:
      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;
      }
      

      【讨论】:

      • 仅代码答案不是好的答案 - 请修剪您的答案,添加一些评论,仅描述相关部分。
      猜你喜欢
      • 2020-11-19
      • 1970-01-01
      • 2016-08-02
      • 2020-12-28
      • 1970-01-01
      • 1970-01-01
      • 2011-03-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多