【问题标题】:How do I select distinct values from multiple mysql columns and put them in one PHP array?如何从多个 mysql 列中选择不同的值并将它们放入一个 PHP 数组中?
【发布时间】:2011-05-18 00:50:43
【问题描述】:

我有一张歌曲表,每首歌曲最多可以有 3 种不同的流派。所以在我的表中,对于每首歌曲,我都有列genre1、genre2 和genre3。我正在尝试在列表中显示所有可用的流派。

这是一个随机示例集:

genre1    genre2    genre3
metal     jazz
metal     country   pop
oldies    metal
rap
jazz      hip-hop   choir
choir     metal     jazz

我希望在 php 中完成的列表按字母顺序显示可供选择的不同类型。所以它应该列出这个:

  • 合唱团
  • 国家
  • 嘻哈
  • 爵士乐
  • 金属
  • 老歌
  • 流行音乐
  • 说唱

感谢所有帮助。也许我不是最聪明的方法,但我想不出更好的方法。

【问题讨论】:

  • 从MySql进来的数据是什么样子的?是数组吗?

标签: php mysql


【解决方案1】:

所以单独的列没有区别吗?如果是这种情况,您可以使用UNION

SELECT genre1 AS g FROM t UNION SELECT genre2 AS g FROM t UNION SELECT genre3 AS g FROM t

如果你有一个WHERE 子句,那么你需要复制它3次,或者使用一个中间临时表

祝你好运!


表:

mysql> SELECT genre1, genre2, genre3 FROM music;
+--------+---------+--------+
| genre1 | genre2  | genre3 |
+--------+---------+--------+
| metal  | jazz    |        | 
| metal  | country | pop    | 
| oldies | metal   |        | 
| rap    |         |        | 
| jazz   | hip-hop | choir  | 
| choir  | metal   | jazz   | 
+--------+---------+--------+
6 rows in set (0.00 sec)

分组:

mysql> SELECT genre1 AS g FROM music UNION ALL
          SELECT genre2 AS g FROM music UNION ALL
          SELECT genre3 AS g FROM music
+---------+
| g       |
+---------+
| metal   | 
| metal   | 
| oldies  | 
| rap     | 
| jazz    | 
| choir   | 
| jazz    | 
| country | 
| metal   | 
|         | 
| hip-hop | 
| metal   | 
|         | 
| pop     | 
|         | 
|         | 
| choir   | 
| jazz    | 
+---------+
18 rows in set (0.00 sec)

计数:

mysql> SELECT g, COUNT(*) AS c FROM
             (SELECT genre1 AS g FROM music UNION ALL
              SELECT genre2 AS g FROM music UNION ALL
              SELECT genre3 AS g FROM music)
       AS tg GROUP BY g;
+---------+---+
| g       | c |
+---------+---+
|         | 4 | 
| choir   | 2 | 
| country | 1 | 
| hip-hop | 1 | 
| jazz    | 3 | 
| metal   | 4 | 
| oldies  | 1 | 
| pop     | 1 | 
| rap     | 1 | 
+---------+---+
9 rows in set (0.01 sec)

【讨论】:

  • 为方便起见,您可以使用UNION ALL(更快)然后在此处使用SELECT g, COUNT(*) as c FROM ( union ) AS threeg GROUP BY g。这也会给你一个计数,你可以向用户展示你拥有更多的流派。
  • 所以当我获取数组时,所有的值都会在 $row['g] 中,对吗?谢谢!
  • 我不确定我是否完全理解 UNION ALL 版本。我应该把 UNION ALL 放在哪里?我不确定订单。感谢您的宝贵时间。
  • 如果您有很多行,则值得检查以确保每列都有索引。我相信在genre1上单独索引,在genre2上一个,在genre3上一个索引将是最有效的方法。
  • 我也不肯定,但是如果您可以为 union all 中的每个内部选择添加 distinct 或 group by g,那么以后合并的工作会更少。如果您不想要计数,请从选择中删除 ', count(*) as c'。那么你将只有“g”列。
【解决方案2】:
SELECT genre1, genre2, genre3 FROM table

假设返回的是一个数组数组,那么:

function coalesce_into_array($aggregate, $row) {
    foreach ($row as $genre) {
        $aggregate[] = $genre;
    }

    return $aggregate;
}

$data = array_unique(array_reduce($data, 'coalesce_into_array', array()));
sort($data);

但是,我不建议在严肃的应用程序中这样做。数据库设计很糟糕。阅读数据库规范化以了解如何改进它。

【讨论】:

    【解决方案3】:

    除非您出于性能原因将denormalized (2) 流派分为三列,否则应该有一个单独的表格来关联歌曲和流派:

    CREATE TABLE SongGenres (
        song INT NOT NULL REFERENCES Songs (id) ON DELETE CASCADE,
        genre VARCHAR(32) NOT NULL,
        UNIQUE INDEX (song, genre),
        INDEX genres (genre) -- improves performance for getting genre names
    ) Engine=InnoDB;
    

    这取消了(“Cross Road Blues”可以在“Blues”和“Delta Blues”下归档,但仅此而已)和(想到 A3 的乡村酸屋福音)三种类型的人为限制每首歌。如果您的类型有限,您可能希望创建类型列enumerated。 SongGenres 表简化了获取所有流派:

    SELECT UNIQUE genre FROM SongGenres;
    

    或者,您可以进一步规范化并为流派创建一个单独的表:

    CREATE TABLE Genres (
        id INT PRIMARY KEY AUTO_INCREMENT,
        name VARCHAR(32) NOT NULL,
        UNIQUE INDEX (name)
    ) Engine=InnoDB;
    
    CREATE TABLE SongGenres (
        song INT NOT NULL REFERENCES Songs (id) ON DELETE CASCADE,
        genre INT NOT NULL REFERENCES Genres (id) ON DELETE RESTRICT,
        UNIQUE INDEX (song, genre)
    ) Engine=InnoDB;
    

    这更加简化了获取所有类型名称的过程(尽管这只是次要优势):

    SELECT name FROM Genres;
    

    流派表的主要优势在于数据的正确性:如果有人拼错了流派,则不会在流派表中找到该流派。一个潜在的缺点是它将有效类型限制为表中的类型。当然,给在 SongGenres 上具有 INSERT 权限的用户帐户是有意义的,所以这个限制并不严重。一旦您开始添加新的流派,您将面临与没有流派表时相同的问题:错别字。不要添加在 Genres 表中找不到的新流派,而是寻找类似的流派(例如使用 Levenshtein distanceSOUNDS LIKE),如果找到,询问用户是否想用一个流派替换该流派找到的内容或保留原始流派(并将其添加到流派列表中)。

    这是第一种情况下的数据(两个表,SongsSongGenres):

    mysql> SELECT * FROM Songs;
    +----+---------+--------+----
    |编号 |标题 |艺术家 | ...
    +----+---------+--------+----
    | 1 |十字路口蓝调 | ...
    | 2 |山谷中的和平| ...
    +----+---------+--------+----
    2 行(0.00 秒)
    
    mysql> SELECT * FROM SongGenres;
    +--------+-------------+
    |歌曲 |流派 |
    +--------+-------------+
    | 2 |酸 |
    | 1 |蓝调 |
    | 2 |国家 |
    | 1 |三角洲蓝调 |
    | 2 |福音 |
    | 2 |房子|
    | 2 |技术 |
    +--------+-------------+
    一组 7 行(0.00 秒)
    
    mysql> SELECT s.title, sg.genre FROM Songs AS s JOIN SongGenres AS sg ON s.id=sg.song;
    +---------+-------------+
    |标题 |流派 |
    +---------+-------------+
    |十字路口蓝调 |蓝调 |
    |十字路口蓝调 |三角洲蓝调 |
    |山谷中的和平|酸 |
    |山谷中的和平|国家 |
    |山谷中的和平|福音 |
    |山谷中的和平|房子|
    |山谷中的和平|技术 |
    +---------+-------------+
    一组 7 行(0.00 秒)
    

    使用单独的 Genres 表,Songs 中的数据看起来是一样的,但在其他表中我们会有类似的内容:

    mysql> SELECT * FROM Genres;
    +----+--------------+
    |编号 |姓名 |
    +----+--------------+
    | 1 |酸 |
    | 2 |蓝调 |
    | 3 |经典|
    | 4 |国家 |
    | 5 |三角洲蓝调 |
    | 6 |民间|
    | 7 |福音 |
    | 8 |嘻哈 |
    | 9 |房子|
    ...
    | 18 |技术 |
    +----+--------------+
    18 行一组(0.00 秒)
    
    mysql> SELECT * FROM SongGenres;
    +--------+-------+
    |歌曲 |流派 |
    +--------+-------+
    | 1 | 2 |
    | 1 | 5 |
    | 2 | 1 |
    | 2 | 4 |
    | 2 | 7 |
    | 2 | 9 |
    | 2 | 18 |
    +--------+-------+
    一组 7 行(0.00 秒)
    
    mysql> SELECT s.title, g.name 作为流派
        -> FROM Songs AS s
        -> JOIN SongGenres AS sg ON s.id=sg.song
        -> 加入流派作为 g ON sg.genre=g.id;
    +----------+-------------+
    |标题 |流派 |
    +---------+-------------+
    |十字路口蓝调 |蓝调 |
    |十字路口蓝调 |三角洲蓝调 |
    |山谷中的和平|酸 |
    |山谷中的和平|国家 |
    |山谷中的和平|福音 |
    |山谷中的和平|房子|
    |山谷中的和平|技术 |
    +---------+-------------+
    一组 7 行(0.00 秒)
    

    【讨论】:

    • 感谢您的替代方案!在您使用两个单独的表格的替代方法中,我如何将多种流派分配给一首歌曲?另外,如果您不介意,能否解释一下“ON DELETE CASCADE”是什么意思?
    • 分两张表的方法,你是指一张两张表SongsSongGenres(一共2张),还是一张加两张表SongGenresGenres(总共3个)?无论哪种情况,答案都是相同的:分配给一首歌曲的每个流派都将是SongGenres 表中的一行。
    • 至于ON DELETE CASCADEdocumentation 解释说:“InnoDB 对尝试更新或删除父表中具有某些匹配的候选键值的任何 UPDATE 或 DELETE 操作采取的操作子表中的行是[...] [d]删除或更新父表中的行并自动删除或更新子表中的匹配行。”
    【解决方案4】:

    从表中选择不同的流派1、流派2、流派3

    Description

    也许您需要更好的数据库设计... songs | genres | [song_id|genre_id]

    【讨论】:

    • 在您的示例中,我将如何获取数组并将其放入列表中?按照您的做法,当我获取数组时,每行将有三个字段,例如 $row['genre1']、$row['genre2'] 和 $row['genre3']。就像我说的,我需要一份清单中的所有内容。有没有办法将它们结合起来?谢谢。
    • 这是数据库设计的问题 ;) 只是糟糕的数据库设计。创建一个名为流派的表,然后将流派添加到另一个表中。您可以将它们放在一个数组中,然后对这个数组进行排序。
    • 啊,我明白了。那么在歌曲表中,我将通过歌曲表中的 ID 来识别流派?所以在“流派”列中,例如,我会有 4|6|10?这些数字将对应于流派表中的 ID。
    • 类似的东西...或者可能是第三张表,只有歌曲 ID 和流派 ID...Who voted my answer down and why?
    • @dododedodoni 我没有投反对票,但我想我可以告诉你为什么......它根本没有回答这个问题。你是对的,数据库不太适合做问题,但这并不意味着根本不起作用的答案会有帮助。
    猜你喜欢
    • 1970-01-01
    • 2011-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-28
    • 2019-06-20
    相关资源
    最近更新 更多