SQL Query从两个相互关联的表中提取记录 - 表2取决于表1

时间:2009-12-23 01:38:36

标签: php arrays mysql join

我有2张桌子。表1是“文章”,表2是“article_categories”。当用户创建文章时,它将存储在“文章”中。用户在创建文章时可以选择显示本文的各种类别。目前,可以选择一篇文章属于10-25个类别的任何地方(将来可能会增加)。提交文章的这些类别存储在“article_categories”中。因此,这意味着单个文章ID可以在表'article_categories'中具有多个相关值。从两个表中检索所有值时,我需要提取“article_categories”中的所有值,并将值存储在数组中。

我的问题是要使用什么SQL查询才能这样做?我应该使用内连接,左连接,外连接......?最好的方法是什么?我确实在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_categories表中的article_id和category_id键上有一个复合键。我使用的示例查询如下:

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

可以看出,articles表中的值是重复的,它也能够获取所有类别(如果格式化了,它就是最后一列)。我只想从articles表中获取一次值并在循环中获取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?       
    }   

?>

显然,我不能在上面做LIMIT 1因为这会限制我检索所有类别ID的能力。

所以我的问题是如何从第二个表中获取所有category_id(在循环中)并将它们添加到数组中,同时确保表1中的值只被提取一次(I确实意识到从表1中获取的值是相同的,但循环它们没有意义)。为了实现这一点,我想了解我应该使用什么样的Join以最高效率执行查询并使用最少的资源,所有这些都在一个查询中以最小化数据库上的命中。我希望有道理。

提前致谢。

2 个答案:

答案 0 :(得分:3)

修改

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;

修改: 为组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']);               
    }   

?>

请注意群组连续,因为它确实有限制:

  

结果被截断为最大长度由。给出   group_concat_max_len系统   变量,其默认值为   1024。值可以设置得更高,   虽然有效最大长度   返回值的约束受到约束   max_allowed_packet的价值。

修改: 也没有使用组concat我会继续按照你的方式进行...只需使类别ID成为你的主要循环结构:

<?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'];             
    }   

?>

这样你只需要查询一次,然后你就必须遍历文章以对文章的数据进行操作。

答案 1 :(得分:0)

在您描述的多对多场景中,您无法避免在任何单个结果集中出现重复数据。

这是一个想法。执行单独的查询以构建类别名称数组,并将其数据库键作为数组索引。

$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。您可以使用二维数组来获取文章ID列表及其关联类别

$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'是文章ID,而13,9,5和7是属于他们在其下找到的文章ID的类别ID。