【问题标题】:SQL Query to pull records from 2 tables related to each other - Table 2 depending upon Table 1SQL Query 从相互关联的 2 个表中提取记录 - 表 2 取决于表 1
【发布时间】:2009-12-23 01:38:36
【问题描述】:

我有 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 表中的值是重复的,并且它还能够获取所有类别(它是最后一列,以防格式混乱)。我只想从文章表中获取一次值并在循环中获取 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 中的值只获取一次(我确实意识到从表 1 中获取的值是相同的,但循环它们没有意义)。为了实现这一点,我想获得您关于我应该使用哪种 Join 来以最高效率执行查询并使用最少资源的输入,所有这些都在一个查询中以最大限度地减少对数据库的命中。我希望这是有道理的。

提前致谢。

【问题讨论】:

    标签: php arrays mysql join


    【解决方案1】:

    编辑

    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;
    

    编辑: 为组连接添加列别名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 的值。

    编辑: 同样不使用组 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'];             
        }   
    
    ?>
    

    这样你只查询一次,然后你必须循环遍历文章才能对文章的数据进行操作。

    【讨论】:

    • 您好,感谢您的回复和解决方案。到目前为止,根据您的解决方案进行了分析,似乎它将为我提供我的答案。当我在 phpmyadmin 中测试 SQL 时,它给了我列名 GROUP_CONCAT(article_categories.category_id SEPARATOR ','),字面意思。当我打印数组时,它给了我以下值:数组( [0] => 1 [article_id] => 1 [1] => 我的文章 1 [article_title] => 我的文章 1 [2] => 1,2 ,3,4,5,36,71 [GROUP_CONCAT(article_categories.category_id SEPARATOR ',')] => 1,2,3,4,5,36,71 )
    • $categories_id = explode(',', $rows['category_id']);似乎没有帮助。 print_r($categories_id); 也没有难道我做错了什么? print_r($rows);确实告诉我这些值以逗号分隔格式存在,但我无法输出它。这是我遇到过的第一次。感谢您在这方面的帮助。谢谢。
    • 嗨,是的,如果我们确实应用了别名,那么它就可以工作。谢谢你!!!我读到连接的限制是 1024。这是否意味着它是 1MB 限制或 1KB 限制?此外,我还没有运行您的第二个解决方案,但到目前为止,它似乎是另一种解决方案,但是,是的,存在循环 article_id 和 article_title 的问题。没有办法避免吗?如果您碰巧手头有解决方案,请告诉我。我也做了一个编辑,不知道你是否注意到它。我只是想在开始处理之前确认 article_id 是否存在。希望您的第一个解决方案能做到这一点。
    • 你知道我真的不确定这些值是对应于字节还是千字节。您必须在手册中查找。至于检查文章 id 是否存在......当您使用该语法中的连接时,它只会返回具有匹配 article_id 值的行......所以为了返回一个类别,文章 id 必须存在。因此,如果mysql_num_rows 重新运行除 0 以外的任何内容,则它存在。
    • LEFT JOIN 可以。正如我所说,我们之前使用的语法就像运行正常的 JOIN,其中必须满足条件的双方才能返回记录。已更新左连接。
    【解决方案2】:

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

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

    $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。

    【讨论】:

    • 您好,感谢您抽出宝贵时间回复。尽管我确实很欣赏您的解决方案,但我的要求略有不同,并且缺少我要实现的主要目标,即在单个查询中处理所有内容。我确实想到了与您类似的解决方案,事实上,如果我要使用 2 个单独的查询,我什至不必在第一个查询的 while 循环中获取行,也不必使用复杂的语法。我只需要获取文章 ID 并以此遍历类别。如果是 2 个查询操作,问题可以轻松解决。但我想一枪搞定。再次感谢。
    猜你喜欢
    • 2018-08-27
    • 1970-01-01
    • 2017-04-25
    • 1970-01-01
    • 2020-11-22
    • 1970-01-01
    • 2013-04-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多