前言:

今天在生产环境中发现取价接口,每次调用得到值都不同,原因是因为分组后有多条数据,根据order by排序不生效。
原SQL

select
	T.query_id as queryId,
	price
from
	(
	select
		query_param.query_id,
		c.price
	from
		(
		select
			1 as query_id ,
			null as organization_id,
			70795 as goods_id,
			null as skc_id ,
			null as sku_id ,
			null as query_date,
			1 as price_id
	UNION ALL
		select
			2 as query_id ,
			null as organization_id,
			111000 as goods_id,
			null as skc_id ,
			null as sku_id ,
			null as query_date,
			1 as price_id
	) query_param
	left join mkt_price_control c on
		query_param.price_id = c.price_id
		and c.status = '09'
		and c.price_id in (1)
		and c.goods_id in ( )
		and c.organization_id is null
		and (query_param.goods_id is null
			or (query_param.goods_id is not null
				and query_param.goods_id = c.goods_id))
		and (query_param.skc_id is null
			or (query_param.skc_id is not null
				and query_param.skc_id = c.skc_id))
		and (query_param.sku_id is null
			or (query_param.sku_id is not null
				and query_param.goods_id = c.sku_id))
		and (query_param.organization_id is null
			or (query_param.organization_id is not null
				and query_param.organization_id = c.organization_id))
		and (query_param.query_date is null
			or (query_param.query_date is not null
				and query_param.query_date >= c.enable_time
				and query_param.query_date <= c.disable_time))
	order by
		c.enable_time desc,
		c.create_time desc,
		c.id ) T
group by
	T.query_id;

更改后SQL:

SELECT
    T.query_id AS queryId,
    T.price AS price
FROM
    (
    SELECT
        query_param.query_id,
        c.price,
        ROW_NUMBER() OVER (PARTITION BY query_param.query_id ORDER BY c.enable_time DESC, c.create_time DESC, c.id) AS row_num
    FROM
        () query_param
    LEFT JOIN mkt_price_control c ON
        query_param.price_id = c.price_id
        AND c.status = '09'
        AND c.price_id IN (1)
        AND c.goods_id IN ()
        AND c.organization_id IS NULL
        AND (query_param.goods_id IS NULL OR (query_param.goods_id IS NOT NULL AND query_param.goods_id = c.goods_id))
        AND (query_param.skc_id IS NULL OR (query_param.skc_id IS NOT NULL AND query_param.skc_id = c.skc_id))
        AND (query_param.sku_id IS NULL OR (query_param.sku_id IS NOT NULL AND query_param.goods_id = c.sku_id))
        AND (query_param.organization_id IS NULL OR (query_param.organization_id IS NOT NULL AND query_param.organization_id = c.organization_id))
        AND (query_param.query_date IS NULL OR (query_param.query_date IS NOT NULL AND query_param.query_date >= c.enable_time AND query_param.query_date <= c.disable_time))
    ) T
WHERE
    T.row_num = 1
GROUP BY
    T.query_id;

描述

partition by关键字是分析性函数的一部分,它和聚合函数(如group by)不同的地方在于它能返回一个分组中的多条记录,而聚合函数一般只有一条反映统计值的记录。
partition by用于给结果集分组,如果没有指定那么它把整个结果集作为一个分组。
partition by与group by不同之处在于前者返回的是分组里的每一条数据,并且可以对分组数据进行排序操作。后者只能返回聚合之后的组的数据统计值的记录。

常用函数

row_number() over(partition by ... order by ...)
rank() over(partition by ... order by ...)
dense_rank() over(partition by ... order by ...)
count() over(partition by ... order by ...) 求分组后的总数
max() over(partition by ... order by ...) 求分组后的最大值
min() over(partition by ... order by ...) 求分组后的最小值
sum() over(partition by ... order by ...) 求分组后的总和
avg() over(partition by ... order by ...) 求分组后的平均值
first_value() over(partition by ... order by ...) 求分组后的第一个值
last_value() over(partition by ... order by ...) 求分组后的最后一个值
lag() over(partition by ... order by ...) 取出分组后前n行数据
lead() over(partition by ... order by ...) 取出分组后后n行数据

案例

rank()

rank() over(partition by A order by B)
按照A进行分组,然后根据B进行排序,over即在什么之上,rank()即跳跃排序
(比如存在两个第一名,接下来就是第三名) 举例:
select 课程, 学生ID, 分数, rank() over (partition by 课程 order by 分数 desc) as 排名 from 成绩表
查询结果:

课程 学生ID 分数 排名
语文 1 99 1
语文 5 99 1
语文 5 87 3

row_number()

row_number() over (partition by A order by B)
row_number(): 如果有两个第一名时,只返回一个结果。 举例:
select 课程, 学生ID, 分数, row_number() over (partition by 课程 order by 分数 desc) as 排名 from 成绩表
查询结果:

课程 学生ID 分数 排名
语文 1 99 1
语文 5 99 2
语文 5 87 3

dense_rank()

dense_rank() over(partition by A order by B)
dense_rank(): 连续排序(如果有两个第一名时,接下来仍然是第二名) 举例:
select 课程, 学生ID, 分数, rank() over (partition by 课程 order by 分数 desc) as 排名 from 成绩表
查询结果:

课程 学生ID 分数 排名
语文 1 99 1
语文 5 99 1
语文 5 87 2