【问题标题】:PHP & mySQL: Repeating entries problem - Need each entry to show up only once but they repeatPHP & mySQL:重复条目问题 - 需要每个条目只显示一次,但它们会重复
【发布时间】:2011-01-02 04:21:21
【问题描述】:

我的平台:

PHP 和 mySQL

我有什么:

我有 4 个表,即“books”、“book_type”、“book_categories”、“all_categories”。

我想做什么:

简单来说,我想显示所有有库存的书籍,即 in_stock = 'y',以及所有表格中的所有书籍相关信息,只显示一次而不重复条目。目前每本书都在重复,我只想展示一次。

当前问题:

在我的应用程序的前端,条目会重复显示,而实际上我希望它们只显示一次(如在 DISTINCT / UNIQUE 中)并且不会重复。

我的怀疑:

我怀疑重复数据是因为每本书所属的类别。每本书条目都会显示多次,因为它属于一个类别。令人困惑?我的意思是,如果 book1 属于 4 个类别,则 book1 显示 4 次。如果 book2 属于 2 个类别,则显示 2 次。

我需要什么:

我需要能够解决上述问题的 PHP 和 mySQL 代码。我希望我们可以在不使用 mySQL 中的 GROUP_CONCAT 的情况下解决问题,因为同样有一个限制(1024?)。一本书可以属于许多类别,我不想冒险使用 GROUP_CONCAT 丢失任何数据。我还想在单个查询中执行此操作,而无需在循环中重复访问数据库。感谢理解。

复制问题的所有表和对应数据如下:

CREATE TABLE IF NOT EXISTS `books` (
  `book_id` int(11) NOT NULL auto_increment,
  `book_type_id` int(11) NOT NULL,
  `book_title` varchar(50) NOT NULL,
  `book_price` smallint(4) NOT NULL,
  `in_stock` char(1) NOT NULL,
  PRIMARY KEY  (`book_id`),
  KEY `book_type_id` (`book_type_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

--
-- Dumping data for table `books`
--

INSERT INTO `books` (`book_id`, `book_type_id`, `book_title`, `book_price`, `in_stock`) VALUES
(1, 1, 'My Book 1', 10, 'y'),
(2, 1, 'My Book 2', 20, 'n'),
(3, 2, 'My Book 3', 30, 'y'),
(4, 3, 'My Book 4', 40, 'y'),
(5, 2, 'My Book 5', 50, 'n'),
(6, 1, 'My Book 6', 60, 'y'),
(7, 3, 'My Book 7', 70, 'n'),
(8, 2, 'My Book 8', 80, 'n'),
(9, 1, 'My Book 9', 90, 'y'),
(10, 3, 'My Book 10', 100, 'n');

--
-- Table structure for table `book_type`
--

CREATE TABLE IF NOT EXISTS `book_type` (
  `book_type_id` int(11) NOT NULL auto_increment,
  `book_type` varchar(50) NOT NULL,
  PRIMARY KEY  (`book_type_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

--
-- Dumping data for table `book_type`
--

INSERT INTO `book_type` (`book_type_id`, `book_type`) VALUES
(1, 'Good'),
(2, 'Better'),
(3, 'Best');


--
-- Table structure for table `book_categories`
--

CREATE TABLE IF NOT EXISTS `book_categories` (
  `book_id` int(11) NOT NULL,
  `cat_id` int(11) NOT NULL,
  PRIMARY KEY  (`book_id`,`cat_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Dumping data for table `book_categories`
--

INSERT INTO `book_categories` (`book_id`, `cat_id`) VALUES
(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5),
(2, 1),
(2, 2),
(3, 1),
(3, 2),
(3, 3);


--
-- Table structure for table `all_categories`
--

CREATE TABLE IF NOT EXISTS `all_categories` (
  `cat_id` int(11) NOT NULL auto_increment,
  `category` varchar(50) NOT NULL,
  PRIMARY KEY  (`cat_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

--
-- Dumping data for table `all_categories`
--

INSERT INTO `all_categories` (`cat_id`, `category`) VALUES
(1, 'Comedy'),
(2, 'Drama'),
(3, 'Romance'),
(4, 'Horror'),
(5, 'Trivia'),
(6, 'Puzzles'),
(7, 'Riddles'),
(8, 'Kids'),
(9, 'Gents'),
(10, 'Ladies');

我的目标:

//MY QUERY:
SELECT books.book_title,  books.book_price,
       book_type.book_type,
       all_categories.category
FROM books 
LEFT JOIN book_type       ON books.book_type_id = book_type.book_type_id
LEFT JOIN book_categories ON books.book_id = book_categories.book_id
LEFT JOIN all_categories  ON book_categories.cat_id = all_categories.cat_id
WHERE books.in_stock = 'y' 

当前输出:

book_title  book_price  book_type       category
My Book 1    10          Good            Comedy
My Book 1    10          Good            Drama
My Book 1    10          Good            Romance
My Book 1    10          Good            Horror
My Book 1    10          Good            Trivia
My Book 3    30          Better          Comedy
My Book 3    30          Better          Drama
My Book 3    30          Better          Romance
My Book 4    40          Best            NULL
My Book 6    60          Good            NULL
My Book 9    90          Good            NULL

需要以下输出:

book_title  book_price  book_type       category
My Book 1    10          Good            Comedy, Drama, Romance, Horror, Trivia
My Book 3    30          Better          Comedy, Drama, Romance
My Book 4    40          Best            NULL
My Book 6    60          Good            NULL
My Book 9    90          Good            NULL

在此先感谢大家。

【问题讨论】:

  • 请注意我对您的 100 查询备注所做的修改。
  • SELECT book.book_id, all_categories.category FROM book_category JOIN all_categories on book_categories.cat_id=all_categories.cat_id JOIN books on books.book_id=book_categories.book_id WHERE books.in_stock= 'y';
  • OR SELECT book_categories.book_id, all_categories.category FROM book_category JOIN all_categories on book_categories.cat_id=all_categories.cat_id WHERE book_id IN(SELECT book_id FROM books WHERE books.in_stock='y');
  • @MindStalker 谢谢。

标签: php mysql join


【解决方案1】:

确保您不会丢失任何数据的最佳方法是多次查询。分别查询表并在 PHP 中加入它们,可能你的查询看起来像这样

book_id book_title  book_price  book_type         
    1  My Book 1    10          Good           
    2  My Book 3    30          Better          
    3  My Book 4    40          Best           
    4  My Book 6    60          Good            
    5  My Book 9    90          Good           

    book_id, category
    1   Comedy
    1   Drama
    1   Romance
    2   Comedy

编辑:

不,您不应该在 DB 上需要 100 次点击,只需两次,一次用于获取书籍,另一次用于获取类别。循环将在 PHP 中完成,以循环第二个查询并将数据与第一个查询连接起来。第二个查询可能是

SELECT book.book_id, all_categories.category FROM book_category JOIN all_categories on book_categories.cat_id=all_categories.cat_id JOIN books on books.book_id=book_categories.book_id WHERE books.in_stock= 'y';

SELECT book_categories.book_id, all_categories.category FROM book_category 
JOIN   all_categories on book_categories.cat_id=all_categories.cat_id  
WHERE book_id IN   (SELECT book_id FROM books WHERE books.in_stock= 'y');

【讨论】:

  • 感谢您的回复。这意味着除了获取我的数据的查询之外,还有一个额外的循环来获取每个类别的类别。这意味着如果我有 100 本书的库存,那么只是为了获取类别,数据库上还有 100 次点击(1 次点击/查询/书籍)。我觉得来自许多用户的如此多的点击会严重影响该网站。那有意义吗?请让我知道您对此的看法。
  • @MindStalker 感谢您的回复。我正在尝试获取商店中所有书籍的结果,而不仅仅是特定书籍。所以在查询中,我们不能提供特定的 book_id。相反,我们只需要加入表并获取所有 in_store = 'y' 的书(无论是 100 本书还是 500 或 1000 本书)。考虑到这一点,我们如何修改您的查询以适应相同的要求?
【解决方案2】:

session 变量设置为较大的值,使用 GROUP_CONCAT 运行查询,然后将其重置回 global 值。

SET SESSION group_concat_max_len=@@max_allowed_packet;

SELECT books.book_title,  books.book_price,
       book_type.book_type,
       GROUP_CONCAT(all_categories.category)
FROM books 
LEFT JOIN book_type       ON books.book_type_id = book_type.book_type_id
LEFT JOIN book_categories ON books.book_id = book_categories.book_id
LEFT JOIN all_categories  ON book_categories.cat_id = all_categories.cat_id
WHERE books.in_stock = 'y'
GROUP BY books.book_id;

SET SESSION group_concat_max_len=@@group_concat_max_len;

【讨论】:

    【解决方案3】:

    非常简单。添加分组依据。所以它会是这样的:

    编辑:看起来最好的方法是使用组 concat(即使您担心它的上限)以绕过它的上限我建议在运行时更新它时间。所以它会是这样的:

    SET GLOBAL group_concat_max_len = SOME_BIG_VALUE; #SOME_BIG_VALUE >> 1024
    
    SELECT books.book_title,  books.book_price,
           book_type.book_type,
           GROUP_CONCAT(all_categories.category)
    FROM books 
    LEFT JOIN book_type       ON books.book_type_id = book_type.book_type_id
    LEFT JOIN book_categories ON books.book_id = book_categories.book_id
    LEFT JOIN all_categories  ON book_categories.cat_id = all_categories.cat_id
    WHERE books.in_stock = 'y'
    GROUP BY books.book_title
    

    【讨论】:

    • 您好,您提供的查询仅获取书名“我的书 1”和“我的书 3”的第一个类别。对于其余书籍(我的书 4、6 和 9),该类别为 NULL。简而言之,虽然行已停止重复,但并未检索到所有类别。只有第一类是。如果您可以修改查询以显示所有类别,我相信这将解决问题。谢谢。
    • 抱歉,我在阅读您的 cmets 关于必须通过托管公司之前编辑了此内容。
    【解决方案4】:

    您是否尝试为您的选择添加不同的?

    SELECT DISTINCT books.book_title,  books.book_price,
       book_type.book_type,
       all_categories.category
    FROM books 
    LEFT JOIN book_type       ON books.book_type_id = book_type.book_type_id
    LEFT JOIN book_categories ON books.book_id = book_categories.book_id
    LEFT JOIN all_categories  ON book_categories.cat_id = all_categories.cat_id
    WHERE books.in_stock = 'y'
    GROUP BY books.book_title
    

    【讨论】:

    • 感谢吉姆的评论。您的查询结果与 WillMatt 提供的查询结果相同。请参考我对他的回答的评论。再次感谢。
    【解决方案5】:

    您还可以将 CONCAT_WS 分隔符与子查询一起使用,例如:

    SELECT  books.book_title, 
        books.book_price, 
        book_type.book_type, 
        CONCAT_WS(', ', (   
                            SELECT  all_categories.category
                              FROM  all_categories 
                             WHERE  all_categories.cat_id = book_categories.cat_id
                         )
                 ) AS book_category_list
     FROM books 
    LEFT JOIN book_type   
           ON books.book_type_id = book_type.book_type_id                  
    LEFT JOIN book_categories 
           ON books.book_id = book_categories.book_id
        WHERE books.in_stock = 'y'
    

    抱歉,那里的格式有点奇怪。

    【讨论】:

    • 嗨斯科内。您是否有机会实际尝试您的查询?我做到了,它仍然给我与 CURRENT OUTPUT 相同的结果,但查询的结果不显示 NULL 值。它仍然重复我想避免的行。 CONCAT_WS 的限制是什么? GROUP_CONCAT 的限制是 1024 字节,那么你知道 CONCAT_WS 的限制吗?请告诉我。谢谢。
    • 我实际上并没有尝试过——很抱歉它没有按预期工作。我现在有点忙,但我会在今晚晚些时候尝试回复(没有承诺):P 干杯。
    • 感谢您的努力。
    【解决方案6】:

    哥们,你应该看看MySQL的GROUP_CONCAT()函数。 http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat

    这将是其中的一些东西:

    SELECT stuff, GROUP_CONCAT(DISTINCT category ORDER BY category DESC SEPARATOR ', ') 
    GROUP BY category, price, book_type
    

    【讨论】:

    • 嗨冲突。 GROUP_CONCAT 最多只允许 1024 BYTES。这意味着如果(类别 + 分隔符)长度 > 1024 字节,则查询将失败。我不正确吗?正如我所提到的,一本书可以属于许多类别。平均而言,类别的长度为 15 个字节。现在如果这本书属于 100 个类别,那么占用的空间是 1500 字节,大于 1024。这将导致错误的结果。我知道 GROUP_CONCAT 并且出于同样的原因想避免它。请让我知道你的想法。谢谢。
    • 有设置group_concat_max_len(默认1024),也许你可以把它设置得更高?
    • 嗨冲突,我不太确定是否可以通过脚本完成。我认为这可能涉及那些(据我所知)不愿意改变任何事情的网络托管人。大多数时候,我的同行报告说,对服务器设置的任何更改都经常被拒绝,因为网络主机倾向于通过根据用户需求更改设置来避免滥用。所以我想在不接受他们支持的情况下解决它。我认为,如果我们能找到一个完美的解决方案,那么我们就不必再担心托管人员遇到类似的问题了。希望这是有道理的。
    • SQL 命令是SET [GLOBAL | SESSION] group_concat_max_len = val; 但是你真的认为你会超过 1024 字节的限制吗?那是很多字符......虽然如果有这种可能性很好,你应该避免以后的问题
    • 嗨,Clash,正如我在之前给您的一篇文章中所指出的那样,可能会超出限制。也许我们可以用你展示的新方法做点什么。我还发现了一些有趣但很麻烦的事情。我有另一张桌子,它记录了为一本书下的所有订单。如果我尝试对所有下达的订单进行计数,则计数结果不正确。它正在打印存在的所有类别的计数,而不是下订单的计数。如果我使用 GROUP_CONCAT 获取类别,就会发生这种情况。我们有什么办法可以纠正这个问题吗?谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-09
    • 2017-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-15
    • 2014-12-09
    相关资源
    最近更新 更多