Запрос SQL для извлечения записей из 2 таблиц, связанных друг с другом - Таблица 2 в зависимости от таблицы 1 - PullRequest
0 голосов
/ 23 декабря 2009

У меня есть 2 таблицы. Таблица 1 - «статьи», а Таблица 2 - «article_categories». Когда пользователь создает статью, она сохраняется в «статьях». Пользователь при создании статьи может выбирать различные категории, под которыми может появиться эта статья. В настоящее время статья может быть выбрана в 10-25 категориях (может быть увеличена в будущем). Эти категории, под которыми подана статья, хранятся в «article_categories». Таким образом, это означает, что один идентификатор статьи может иметь несколько связанных значений в таблице article_categories. При извлечении всех значений из обеих таблиц мне нужно было бы извлечь все значения из article_categories и сохранить их в массиве.

Мой вопрос о том, какой SQL-запрос использовать для этого? Должен ли я использовать Inner Join, Left Join, Outer Join ...? Каков был бы лучший способ сделать это? Я попробовал некоторые из этих объединений в phpmyadmin, и они дают мне повторяющиеся значения одной и той же статьи, когда на самом деле статья должна быть выбрана только один раз, и все связанные категории должны быть выбраны. Я хотел сделать все это в одном и том же запросе, не разбивая запрос на 2 разных, чтобы выполнить это. Я прикрепляю свои структуры таблиц, чтобы вам было легко:

CREATE TABLE IF NOT EXISTS `articles` (
  `article_id` int(11) unsigned NOT NULL auto_increment,
  `creator_id` int(11) unsigned NOT NULL,
  `article_title` varchar(150) NOT NULL,
  `article_status` varchar(10) NOT NULL,
  PRIMARY KEY  (`article_id`),
  KEY `buyer_id` (`creator_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

--
-- Dumping data for table `articles`
--

INSERT INTO `articles` (`article_id`, `creator_id`, `article_title`, `article_status`) VALUES
(1, 1, 'My article 1', 'Pending');


CREATE TABLE IF NOT EXISTS `article_categories` (
  `article_id` int(11) unsigned NOT NULL,
  `category_id` smallint(3) unsigned NOT NULL,
  PRIMARY KEY  (`article_id`,`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Dumping data for table `article_categories`
--

INSERT INTO `article_categories` (`article_id`, `category_id`) VALUES
(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5),
(1, 36),
(1, 71);

Также обратите внимание, что у меня есть составной ключ для ключей article_id и category_id в таблице article_categories. Пример запроса, который я использовал ниже:

SELECT *
FROM articles, article_categories 
WHERE articles.article_id = article_categories.article_id
AND articles.article_id = 1;

В результате:

article_id  creator_id  article_title   article_status  article_id  category_id
    1   1   My article 1    Pending 1   1
    1   1   My article 1    Pending 1   2
    1   1   My article 1    Pending 1   3
    1   1   My article 1    Pending 1   4
    1   1   My article 1    Pending 1   5
    1   1   My article 1    Pending 1   36
    1   1   My article 1    Pending 1   71

Как видно, значение из таблицы статей повторяется, и оно также может получить все категории (это последний столбец, если форматирование испорчено). Я хотел получить значения из таблицы статей только один раз и получить category_id в цикле, чтобы я мог добавить эти циклические значения в массив и продолжить свою обработку. Вот что я собираюсь сделать после получения значений сверху:

<?php
//i wanted to check if the article_id exists before i pull the related categories. 
//If I do it this way and output using mysql_num_rows, it gives me value 7,
//when in fact, the there's only 1 article with such Id. This means it also counts
//  the number of categories. Is there a way to uniquely identify only the number of
// articles (just to see if it exists or not, in the place)

$result = mysql_query("SELECT *
FROM articles, article_categories 
WHERE articles.article_id = article_categories.article_id
AND articles.article_id = 1");

while ( $rows = mysql_fetch_array($result) )
    {   //i don't think this the following 2 assignments should be done in the loop
        $article_id = $rows['article_id'];
        $article_title = $rows['article_title'];

        //(loop all the category_id from the 2nd table and add to the array below)
        $categories_id[] .= ??????? --> How do i do this?       
    }   

?>

Очевидно, что я не могу выполнить ПРЕДЕЛ 1 на вышеуказанном, поскольку это ограничит мою способность извлекать все идентификаторы категории.

Так что мой вопрос заключается в том, как мне получить все category_id из 2-й таблицы (в цикле) и добавить их в массив, и в то же время убедиться, что значения из таблицы 1 выбраны только один раз (я Вы понимаете, что значения, извлеченные из таблицы 1, одинаковы, но нет смысла зацикливаться на них). Чтобы добиться этого, я хотел бы получить ваше мнение о том, какой тип соединения я должен использовать для выполнения запроса с максимальной эффективностью и использования минимальных ресурсов, все в одном запросе, чтобы минимизировать попадания в БД. Я надеюсь, что это имеет смысл.

Заранее спасибо.

Ответы [ 2 ]

3 голосов
/ 23 декабря 2009

EDIT

SELECT articles.article_id, articles.article_title, GROUP_CONCAT(article_categories.category_id SEPARATOR ',') as category_id
FROM articles
LEFT JOIN article_categories ON  (articles.article_id = article_categories.article_id)
-- WHERE CLAUSE IF YOU WANT/NEED --
GROUP BY articles.article_id;

EDIT : Добавлен псевдоним столбца для группы concat GROUP_CONCAT(article_categories.category_id SEPARATOR ',') as category_id

<?php
$result = mysql_query("SELECT articles.article_id, articles.article_title, GROUP_CONCAT(article_categories.category_id SEPARATOR ',') as category_id
FROM articles, article_categories
WHERE articles.article_id = 1
GROUP BY articles.article_id;");

while ( $rows = mysql_fetch_array($result) )
    {   
        $article_id = $rows['article_id'];
        $article_title = $rows['article_title'];

        //(loop all the category_id from the 2nd table and add to the array below)
        $categories_id = explode(',', $rows['category_id']);               
    }   

?>

Остерегайтесь группы concat, хотя она имеет ограничение:

Результат усекается до максимальной длины , которая задается group_concat_max_len система переменная, которая имеет значение по умолчанию 1024. Значение может быть установлено выше, хотя эффективная максимальная длина возвращаемого значения ограничено значение max_allowed_packet.

EDIT : Кроме того, без использования concat группы, я бы продолжил и сделал так, как вы это сделали ... просто сделайте id категории вашей основной петлевой stcuture:

<?php
$result = mysql_query("SELECT articles.article_id, articles.article_title, article_categories.category_id
FROM articles, article_categories
WHERE articles.article_id = 1");

$articles = array();
while ( $rows = mysql_fetch_array($result) )
    {   
        if(!array_key_exists($rows['article_id'], $articles)
        {
           $articles[$rows['article_id']] = array(
               'article_id' => $rows['article_id'],
               'article_title' => $rows['article_title']
               'categories_id' => array()
            );
         }

         $articles[$rows['article_id']][] = $rows['categories_id'];             
    }   

?>

Таким образом, вы делаете запрос только один раз, но тогда вам придется циклически просматривать статью для операции с данными статей.

0 голосов
/ 23 декабря 2009

В этом сценарии «многие ко многим», который вы описываете, вы не можете избежать дублирования данных в любом наборе результатов, который вы получаете.

Вот идея. Выполните отдельный запрос для создания массива имен категорий с ключом базы данных в качестве индекса массива.

$sql = "SELECT category_id, category_name FROM Categories";
$result = mysql_query($sql);
$arrCategories = array();
while ( $row = mysql_fetch_assoc($result) {
    $arrCategories[$row['category_id']] = $row['category_name'];
}

Теперь у вас есть имена всех категорий в массиве.

Когда вы выбираете статьи, вам нужно выполнить отдельный запрос, который извлекает его category_ids из таблицы соединений. Вы можете использовать двумерный массив, чтобы иметь список идентификаторов статей и связанных с ними категорий

$arrArticleCategoryIds = array();

$result = mysql_query("SELECT *
FROM articles, article_categories 
WHERE articles.article_id = article_categories.article_id
AND articles.article_id = 1");

while ( $rows = mysql_fetch_array($result) )
    {   
        // why bother doing this when you've already hard-coded "1"?
        $article_id = $rows['article_id'];
        $article_title = $rows['article_title'];

        //(loop all the category_id from the 2nd table and add to the array below)
        // $categories_id[] .= ??????? --> How do i do this?               

        // here's how:
        $sql = "SELECT category_id 
                FROM `article_categories` 
                WHERE article_id = $article_id
        ";
        $category_id_results = mysql_query($sql);

        while ( $category_id_row = mysql_fetch_assoc($category_id_results) ) {
            $arrArticleCategoryIds[$article_id][] = $row['category_id'];
       }

    }   

Вы получите два массива:

$arrCategories 
Array
(
    [1] => apple
    [2] => banana
    ...
)

$arrArticleCategoryIds
Array
(
    [1] => Array
        (
            [1] => 13
            [2] => 9
         )
    [3] => Array
         (
            [1] => 5
            [2] => 7
         )
    )
)

Где '1' и '3' - идентификаторы статей, а 13, 9, 5 и 7 - идентификаторы категорий, которые относятся к идентификатору статьи, под которым они находятся.

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