本次文章目的:
MySQL并没有专门的中位数算法,而对于SQL不熟悉的人,书写中位数,只能通过Java等语言实现。并非推荐使用MySQL完成中位数计算,以下实现,仅为了通过算法解析的过程中,了解一些MySQL常用与不常用的功能、函数,并开拓思维。
当然,对于一些临时性的要求,需要制作一些临时性的算法测试、校验、导出, 能使用MySQL完成这类算法,就凸显出其效率。
说到中位数,我们就需要一批数据来进行测试和实现,创建如下表:
DROP TABLE IF EXISTS CaseRent;
CREATE TABLE CaseRent(
ID int(6) NOT NULL AUTO_INCREMENT,
ResidentialAreaID int(6) DEFAULT NULL,
CaseFrom varchar(30) DEFAULT NULL,
Price int(6) DEFAULT NULL,
PRIMARY KEY (ID)
);
称之为出租案例表,关键字段有:小区ID、案例来源及价格。
接下来通过随机数来给出租案例表赋值:
INSERT INTO CaseRent (ResidentialAreaID, CaseFrom ,Price)
SELECT ROUND((RAND()*100)+1),'链家在线',ROUND((RAND()*8000)+1000)
该语句包含知识点如下:
1.通过 INSERT INTO ... SELECT 进行赋值(用途广泛,创建表亦可以使用)
2.运用Rand() 随机数函数,ROUND() 四舍五入函数,完成小区ID从0~100 ,价格从1000~9000的随机录入。
一条数据当然不够,我们可以使劲的多点几下执行,使数据增加到近10条。这时候我们修改一下赋值语句
INSERT INTO CaseRent (ResidentialAreaID, CaseFrom ,Price)
SELECT ROUND((RAND()*100)+1),'链家在线',ROUND((RAND()*8000)+1000) FROM CaseRent
继续反复来N下,之后将来源“链家在线”修改为“房天下”,进行一次赋值。
INSERT INTO CaseRent (ResidentialAreaID, CaseFrom ,Price)
SELECT ROUND((RAND()*100)+1),'房天下',ROUND((RAND()*8000)+1000) FROM CaseRent
模拟数据到此完成!示例如下:
实际上,网上的中位数花式百出,但无一不是:代码篇幅长、需要自我关联 或者 使用上临时变量。
当然也有类似我们接下来要讲的方式。无论哪种方式,都需要更多的了解和扩展自己所知。
接下来以刚才我们自定义的模拟数据为例子,安排第一个问题:
1.查找小区ID = 99 的价格中位数
这类的中位数,可以说是最简单的,而且网上大部分中位数,均针对此类中位数(单条件),从上述网站就可以看到,其问题与我们的类似,但其代码量可谓不少。
我们来分析问题:其获取价格中位数,就必须使用ORDER BY 来实现排序,排序后,统计总条数,来获取中间一条的价格作为结果(如果为偶数,可以取2条均值,亦可以取前一条 例如 6条数据,可以取第3、4 条进行均值计算,这里以取前一条为算法模拟)
那么第一步,无疑是要进行价格从小到大的排序:
SELECT * FROM CaseRent WHERE ResidentialAreaID = 99 ORDER BY Price
排序之后,ID显的杂乱无章,关如此,我们人为的话,只能去手动数条数进行查找, 因此我们需要拥有一个新的自增ID,以此来更快的得知其对应的排名。
如何得到新的自增ID呢? 我们可以新建一张表, 通过INSERT INTO ...SELECT 来完成新数据的录入,以此达到数据的ID自增:例如:
INSERT INTO NewCaseRent(ResidentialAreaID,CaseFrom,Price)
SELECT ResidentialAreaID,CaseFrom,Price FROM CaseRent WHERE ResidentialAreaID = 99 ORDER BY Price
不过这样我们就需要建表了,这就显的很麻烦,因为一个自增,而新建一张表,入不敷出,
那么我们就需要一个变量,来实现自增功能。
同JAVA/Python等开发语言一样,Mysql也有变量,通常以@开头为用户自定义变量,以@@开头为系统变量。
那么我们怎么使用变量?很简单,通过SET创建并赋值变量值, 再通过SELECT查询结果,例如:
SET @ID = 0;
SELECT @ID;
有了变量,我们可以将变量作为新的自增ID,来代替创建一张新表的操作了,
通过变量自加操作,完成新的自增ID功能:
SET @ID = 0;
SELECT @ID:=@ID+1 AS ID,ResidentialAreaID,CaseFrom,Price FROM CaseRent WHERE ResidentialAreaID = 99 ORDER BY Price
注意几点:
1.在SELECT中,给临时变量赋值,使用 :=
2.每条语句,从底层讲,都是循环查询,因此在语句上直接自增,就可以实现逐条累加。
当然,上面的语句其实是2条语句,这样放到JAVA或者其他语言中执行,可能不方便,因此也可以修改成如下语句:
SELECT @ID:=@ID+1 AS ID,ResidentialAreaID,CaseFrom,Price FROM CaseRent,(SELECT @ID:=0) b
WHERE ResidentialAreaID = 99 ORDER BY Price
结果示例:
效果很好,接下来我们要做的,就是获取ID=总条数/2 的那条数据了。
思考一下,如何才能简单的得到结果?
SELECT * FROM (
SELECT @ID:=@ID+1 AS ID,ResidentialAreaID,CaseFrom,Price FROM CaseRent,(SELECT @ID:=0) b
WHERE ResidentialAreaID = 99 ORDER BY Price ) a WHERE ID = @ID/2
通过简单的中位数选取,深刻认知Mysql临时变量的用法。
接下来引入加深层次的中位数:
1.根据案例来源,分别统计不同来源,小区ID=99的中位数。
分析问题:比第一步多了一个条件,其结果也多了一条数据。
那么该怎么做呢?
我们知道,排序的时候,需要按照 案例来源、价格 2个条件进行排序了,如果直接自增ID, 会是什么样的呢?
SELECT @ID:=@ID+1 AS ID,ResidentialAreaID,CaseFrom,Price FROM CaseRent,(SELECT @ID:=0) b
WHERE ResidentialAreaID = 99 ORDER BY CaseFrom,Price
很明显,如果想要实现真确的自增ID, 到了链家在线这一步,ID需要重新从1开始计算。
那么难道我们分成2次统计? 如果案例来源有N个,这个方式明显不行。
接下来引入Mysql函数 IF
IF ( 条件 , 真 , 假 )