[MSSQL]NTILE另类分页有么有?!

本文介绍NTILE函数在SQL中的应用,演示如何利用NTILE实现数据分片及分页,同时展示NTILE与PARTITION BY结合使用的案例。

NTILE这个关键字干啥妮?ORACLE管它叫分片,大概意思是把查询出来的结果集分成尽量均等的组(片),有点不直观,来看代码

先看测试用的数据表:

--DROP TABLE T
CREATE TABLE T(GRP_A VARCHAR(20),GRP_B VARCHAR(20),GRP_C VARCHAR(20),VAL INT)
INSERT INTO T(GRP_A,GRP_B,GRP_C,VAL)
SELECT 'a1','b1','c1',10 union all
SELECT 'a1','b1','c2',10 union all
SELECT 'a1','b2','c2',40 union all
SELECT 'a1','b2','c3',40 union all
SELECT 'a1','b2','c3',50 union all
 
SELECT 'a2','b3','c3',12 union all
SELECT 'a2','b3','c3',22 union all
SELECT 'a2','b3','c3',32 
 
SELECT * FROM T
 
--------------------------------------------------------------------------
GRP_A                GRP_B                GRP_C                VAL
-------------------- -------------------- -------------------- -----------
a1                   b1                   c1                   10
a1                   b1                   c2                   10
a1                   b2                   c2                   40
a1                   b2                   c3                   40
a1                   b2                   c3                   50
a2                   b3                   c3                   12
a2                   b3                   c3                   22
a2                   b3                   c3                   32
 
(8 行受影响)

 

试下这个分片功能,SQL脚本比较简单:

SELECT 
    *,
    NTILE(3)OVER(ORDER BY VAL) AS NUM
FROM T
 
GRP_A                GRP_B                GRP_C                VAL         NUM
-------------------- -------------------- -------------------- ----------- --------------------
a1                   b1                   c1                   10          1
a1                   b1                   c2                   10          1
a2                   b3                   c3                   12          1
a2                   b3                   c3                   22          2
a2                   b3                   c3                   32          2
a1                   b2                   c2                   40          2
a1                   b2                   c3                   40          3
a1                   b2                   c3                   50          3
 
(8 行受影响)

 

结果集一共八条,分成了111,222,33一共三片,如果是九条刚好分配好,真不巧,八条,最后一片少一个,这没关系啊,

它就这么个功能,灰常简单,再来分个四片看看,分四片完美均分!

SELECT 
    *,
    NTILE(4)OVER(ORDER BY VAL) AS NUM
FROM T
 
 
GRP_A                GRP_B                GRP_C                VAL         NUM
-------------------- -------------------- -------------------- ----------- --------------------
a1                   b1                   c1                   10          1
a1                   b1                   c2                   10          1
a2                   b3                   c3                   12          2
a2                   b3                   c3                   22          2
a2                   b3                   c3                   32          3
a1                   b2                   c2                   40          3
a1                   b2                   c3                   40          4
a1                   b2                   c3                   50          4
 
(8 行受影响)

 

如上所示,刚好11,22,33,44分成四片

分页的又一方法对啵?比较不常见哈,因为你不知道总记录有多少

假设我们总想分成100页,不考虑每页多少条,这个场景下真可以用NTILE关键字!传100进去即可!

 

因为这里一共八条,所以传100进行,一行记录一页都不够,于是产生了ROW_NUMBER的效果,示例

SELECT 
    *,
    NTILE(100)OVER(ORDER BY VAL) AS NUM
FROM T
 
 
 
GRP_A                GRP_B                GRP_C                VAL         NUM
-------------------- -------------------- -------------------- ----------- --------------------
a1                   b1                   c1                   10          1
a1                   b1                   c2                   10          2
a2                   b3                   c3                   12          3
a2                   b3                   c3                   22          4
a2                   b3                   c3                   32          5
a1                   b2                   c2                   40          6
a1                   b2                   c3                   40          7
a1                   b2                   c3                   50          8
 
(8 行受影响)

 

 

NTILE的另类分页

头脑风暴一下,总记录数可以COUNT查询得知,每页大小已知,是不是可以计算得出多少页来?

DECLARE @TOTAL_COUNT INT,@PAGE_COUNT INT
SET @TOTAL_COUNT = 8
SET @PAGE_COUNT = 3
总页数 = @TOTAL_COUNT/@PAGE_COUNT

是不是这样,总记录数/每页大小=总页数,然后妮?你想干啥?想分页...继续

知道总页数了吧,分片呀,呵呵

SELECT 
    *,
    NTILE(@TOTAL_COUNT/@PAGE_COUNT)OVER(ORDER BY VAL) AS NUM
FROM T

刚试了把,NTILE参数可以是表达式滴,于是全新的分页出来啦~,以上代码执行后结果如下
GRP_A                GRP_B                GRP_C                VAL         NUM
-------------------- -------------------- -------------------- ----------- --------------------
a1                   b1                   c1                   10          1
a1                   b1                   c2                   10          1
a2                   b3                   c3                   12          1
a2                   b3                   c3                   22          1
a2                   b3                   c3                   32          2
a1                   b2                   c2                   40          2
a1                   b2                   c3                   40          2
a1                   b2                   c3                   50          2
 
(8 行受影响)

然后对外边直接传第几页,就返回NUM等于几的数据,如NUM=2表示第二片的数据,爽不爽?不用再计算@PageIndex * @PageSize然后BETWEEN算(@PageIndex+1) * @PageSize谁用谁知道..

传统分页代码,也不传统啊,ROW_NUMBER分页脚本:

DECLARE @PageSize INT,@PageIndex INT
SELECT @PageIndex = 0,@PageSize = 2
SELECT * FROM 
(
    SELECT
        *,
        ROW_NUMBER()OVER(ORDER BY VAL) AS NUM
    FROM T
) AS T
WHERE NUM BETWEEN @PageIndex * @PageSize AND (@PageIndex + 1) * @PageSize

NTILE另类分页:
DECLARE @TOTAL_COUNT INT,@PAGE_SIZE INT
SET @TOTAL_COUNT = 8
SET @PAGE_SIZE = 2
----总页数 = @TOTAL_COUNT/@PAGE_COUNT
 
SELECT * FROM 
(
    SELECT 
        *,
        NTILE(@TOTAL_COUNT/@PAGE_SIZE)OVER(ORDER BY VAL) AS NUM
    FROM T
)AS T
WHERE NUM = 3

功能是一样滴啊,除了分页还能揍啥?它的功能就是这么个功能,能揍啥,自己发挥吧

 

NTILE与PARTITION的配合使用:

SELECT 
    *,
    NTILE(4)OVER(PARTITION BY GRP_A ORDER BY VAL ASC) AS NUM
FROM T
 
 
 
GRP_A                GRP_B                GRP_C                VAL         NUM
-------------------- -------------------- -------------------- ----------- --------------------
a1                   b1                   c1                   10          1
a1                   b1                   c2                   10          1
a1                   b2                   c2                   40          2
a1                   b2                   c3                   40          3
a1                   b2                   c3                   50          4
a2                   b3                   c3                   12          1
a2                   b3                   c3                   22          2
a2                   b3                   c3                   32          3
 
(8 行受影响)

 

也没什么特别之处,就是先分组,再排序,最后分片,

这里是先按GRP_A分组,分成了a1,a2两组,在每组内再按VAL排序,最后在每组内再分片,a1组分成了11,2,3,4四片,a2组分成了1,2,3片

 

见笑了

 

 

猜测您可能对下边的文章感兴趣

SQL SERVER 2008 函数大全 - 字符串函数

SQL2008系统统计函数

[MSSQL]GROUPING SETS,ROLLUP,CUBE初体验

[MSSQL]ROW_NUMBER函数

[MSQL]RANK函数

[MSSQL]NTILE另类分页有么有?!

[MSQL]也说SQL中显示星期几函数

[MSSQL]COALESCE与ISNULL函数

[MSSQL]PIVOT函数

[MSSQL]FOR XML AUTO I

[MSSQL]FOR XML AUTO II

[MSSQL]TRY…CATCH…通用格式

如果您喜欢该博客请点击右下角推荐按钮,您的推荐是作者创作的动力!

<think>嗯,我现在需要在MySQL里计算分位数,但不确定有没有内置函数。让我想想,之前用过一些聚合函数,比如AVG、SUM,但分位数可能更复杂。首先,我应该查一下MySQL的文档,看看有没有像PERCENTILE_CONT这样的函数。记得在PostgreSQL和SQL Server里有类似的窗口函数,但MySQL可能不太一样。 查了一下,发现用户手册里提到MySQL 8.0增加了窗口函数,比如ROW_NUMBER和NTILE,但好像没有直接计算分位数的内置函数。比如引用[2]里提到,Oracle有专门的函数,但其他解决方案可能要用自连接或窗口函数。MySQL可能需要用其他方法模拟。 那怎么办呢?可能需要用子查询和变量来计算。例如,用ORDER BY排序数据,然后用变量给行编号,再根据总行数计算分位点的位置。比如中位数的话,如果是奇数行取中间值,偶数取平均。或者用NTILE函数将数据分成100组,但NTILE的参数是分桶数,可能不太灵活。 另外,引用[2]提到传统方法用自连接,但窗口函数更高效。比如用PERCENT_RANK()来获取百分比排名,然后结合条件筛选。不过MySQL的PERCENT_RANK()是否存在呢?查一下,MySQL 8.0确实有PERCENT_RANK()作为窗口函数,可以计算行的相对排名百分比。那或许可以用这个函数来找到分位点。 具体步骤可能如下:先为数据集排序,计算每行的百分比排名,然后找到对应百分比的位置,再取值。比如要计算75th分位数,可以用子查询筛选出PERCENT_RANK() >= 0.75的第一行。或者用线性插值法,像PERCENTILE_CONT那样。不过这可能比较复杂,需要用到CASE WHEN和聚合函数。 比如,用户可能需要这样的查询: SELECT AVG(val) AS percentile FROM ( SELECT val, PERCENT_RANK() OVER (ORDER BY val) AS p FROM data ) AS t WHERE p >= 0.75 ORDER BY p LIMIT 1; 不过这可能不完全准确,需要测试一下。或者结合COUNT和变量来计算位置。比如计算总行数,确定分位点的索引,然后取对应的值。如果索引不是整数,可能需要插值。例如,对于第p分位数,位置是(N-1)*p +1,然后取该位置的值或插值。 所以,总结来说,MySQL没有直接的内置分位数函数,但可以通过窗口函数和子查询组合实现。需要根据具体需求选择合适的方法,并注意性能问题,尤其是大数据集时。</think>### MySQL中计算分位数的实现方法 MySQL目前(截至8.0版本)**没有直接计算分位数的内置函数**(如`PERCENTILE_CONT`或`PERCENTILE_DISC`),但可以通过以下方法实现: --- #### **方法1:使用窗口函数(推荐,需MySQL 8.0+)** 通过`PERCENT_RANK()`或`ROW_NUMBER()`结合条件筛选实现: ```sql -- 计算75th分位数示例 SELECT AVG(val) AS 75th_percentile FROM ( SELECT val, PERCENT_RANK() OVER (ORDER BY val) AS p_rank FROM your_table ) AS ranked WHERE p_rank >= 0.75 ORDER BY p_rank LIMIT 1; ``` **说明**: - `PERCENT_RANK()`计算每行的百分比排名(范围$[0,1]$) - 筛选最接近目标分位数的值,并用`AVG`处理边界情况[^2] --- #### **方法2:使用变量和子查询** 适用于所有MySQL版本: ```sql -- 计算中位数示例(50th分位数) SET @row_index = -1; SELECT AVG(val) AS median FROM ( SELECT @row_index := @row_index +1 AS row_index, val FROM your_table ORDER BY val ) AS indexed WHERE row_index IN ( FLOOR(@row_index/2), CEIL(@row_index/2) ); ``` **说明**: - 使用变量`@row_index`生成行号 - 通过数学计算确定分位数位置 --- #### **方法3:使用`NTILE()`分桶(近似计算)** 将数据分为100组后取目标分位桶: ```sql SELECT MAX(val) AS 90th_percentile FROM ( SELECT val, NTILE(100) OVER (ORDER BY val) AS bucket FROM your_table ) AS buckets WHERE bucket = 90; ``` **注意**:此方法为近似值,数据分布不均匀时可能有误差 --- ### **性能与注意事项** 1. **窗口函数优化**:MySQL 8.0的窗口函数性能优于传统自连接方法,但仍需对排序字段建立索引。 2. **大数据集处理**:若数据量过大(如超过100万行),建议结合`WHERE`条件分段计算[^3]。 3. **精确性权衡**:`PERCENT_RANK`方法更精确,`NTILE`方法更高效但为近似值。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值