Как уменьшить количество запросов, необходимых для получения этого результата - PullRequest
2 голосов
/ 29 сентября 2010

Я пишу приложение поверх 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;
}

Я уверен, что есть способнаписать один или два запроса, которые соберут все записи в объекты, которые я затем смогу отфильтровать, вместо того, чтобы писать два запроса для каждого, но я понятия не имею, с чего начать пытаться написать это.Любые предложения приветствуются.

Большое спасибо, Маркус

Ответы [ 5 ]

2 голосов
/ 29 сентября 2010

То, что вы хотите сделать, это:

  1. Возьмите отсортированный список всех своих книг и их тегов,
  2. Разработайте их как HTML, с тегами и оценками.

Соберите все свои теги и в своих книгах, используйте переменную для отслеживания последнего записанного ISBN и создавайте свою запись только при изменении ISBN. Итак, вытащите набор так:

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

Затем запишите «базовую информацию о книге» каждый раз, когда книга изменяется в вашем цикле. Очевидно, вам понадобится больше полей, чем просто Книга и Метка (например, ISBN ).

Если ваша информация об Amazon поступает из Amazon, у вас, вероятно, не будет выбора в отношении повторяющихся вызовов их API (если у них нет «пакетного» режима или чего-то еще, где вы можете отправить массив номеров ISBN?).

1 голос
/ 30 сентября 2010

С некоторой помощью из этой темы и другими при создании лучшего запроса мне удалось решить эту проблему с помощью следующего кода:

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 голосов
/ 14 октября 2012
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 голосов
/ 29 сентября 2010

Я совсем не знаком с CodeIgniter, но думаю, что есть некоторые общие приемы, которые вы можете включить.

  • Если это страница просмотра - нет ли нумерации страниц? Разбивка на страницы результатов должна резко сократить количество запросов, которые вы должны выполнить на загрузку страницы.
  • У вас есть одна функция (скажем, get_books_info()), которую вы вызываете, которая извлекает все теги и метаинформацию для всех книг, возвращаемых вашей функцией get_books(). Затем обратитесь к этому массиву из вашего get_book_info(). Вы можете даже вызвать get_books_info() с get_book_info() - поэтому вам нужно выполнять работу, только если вам нужны данные. Вид ленивой загрузки, я думаю.
0 голосов
/ 29 сентября 2010

Если я вас правильно понял: в таблице книг есть все данные о книгах: при этом:

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

должен вернуть вам все данные о ваших книгах, и вам не нужно повторять цикл для получения данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...