背景

在msyql查询中经常会出现一对多查询,只查询多表中的其中一条最新的数据或者达成某个条件的数据。如果只用GROUP BY已经不能满足查询结果,这时查询就会变得复杂。

举个例子,学校表、课程表、课程排序表,我需要每个学校下面课程排序最靠前的一个课程。

在有些案列中我们可以看到被人使用的方法是:

``SELECT c.Id FROMt_course_rank AS ocrLEFT JOIN ( SELECT * FROM t_course WHERE coursetype = 2 ORDER BY Visit DESC ) AS c ON c.Id = ocr.courseidLEFT JOIN t_school AS s ON c.LoginId = s.LoginIdWHEREc.LoginId IN (19) GROUP BY c.LoginId

通过临时表关联完成某中条件中最新数据,但是这种查询不准确,无法满足条件。

解决方案

1.先查出所有数据,然后遍历去查询(php中foreach中查询),简单,但是效率很低,过程可能复杂。

2. 先排序然后在调用子查询在已经排序好的里面取,麻烦,效率也不高。

3.使用GROUP_CONCAT配合SUBSTRING_INDEX

介绍下这两个函数

GROUP_CONCAT
很多人都知道他是分组合并,但是可能对于他第二个排序参数应用不太多。列出详细的使用
group_concat( [distinct] 要连接的字段 [order by 排序字段 asc/desc ] [separator ‘分隔符’] )
这里就是先根据时间排序,把所有时间从大到小拼接起来,用指定分割符隔开,默认为逗号隔开,如果你要合并的字符串中你确定了不会有特殊字符,你就可以指定某个特殊字符隔开。
SUBSTRING_INDEX
按关键字截取字符串,用法如下:
substring_index(被截取字段,关键字,关键字出现的次数),可能对于第三个参数不太清楚,这里详细说一下用count代表第三个参数:
返回一个 str 的子字符串,在 delimiter 出现 count 次的位置截取。
如果 count > 0,从则左边数起,且返回位置前的子串;
如果 count < 0,从则右边数起,且返回位置后的子串
举例: 如果字符串为 aaaaa,bbbbbb,cccccc 如果count=1,那么返回的值为aaaaa,相当于分割后的第一个元素,如果为2,就是aaaaa,bbbbbb。同理就可以参照php中的substr中-1索引就可以理解count<0的情况了。
实现思路
先分组拼接,把大的放在第一个,然后用指定字符串分割,取第一个,特别注意分割的字符串一定是保证不会出现在需要分组拼接的字符串中,否则会导致数据不准确。
最终效果

``SELECTSUBSTRING_INDEX( GROUP_CONCAT( c.Id ORDER BY c.Visit DESC ), “,”, 1 ) AS IdFROM t__course_rank AS ocrLEFT JOIN 
( SELECT * FROM t_course WHERE coursetype = 2 ORDER BY Visit DESC ) AS c ON c.Id = ocr.courseidLEFT JOIN t_school AS s ON c.LoginId 
= s.LoginIdWHERE
c.LoginId IN ( 19 ) GROUP BY c.LoginId