分组 根据某一列进行排序,根据shopid分组,用createTime排序,返回row_number()序号 select no =row_number() over (partition by s...

本文介绍了SQL中使用over函数配合row_number()等函数进行分组排序的方法,并通过实例展示了如何实现不同场景下的Top N查询,包括利用row_number()、crossapply、count及游标等多种方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

over不能单独使用,要和分析函数:rank(),dense_rank(),row_number()等一起使用。
其参数:over(partition by columnname1 order by columnname2)
含义:按columname1指定的字段进行分组排序,或者说按字段columnname1的值进行分组排序。
例如:employees表中,有两个部门的记录:department_id =10和20
select department_id,rank() over(partition by department_id order by salary) from employees就是指在部门10中进行薪水的排名,在部门20中进行薪水排名。如果是partition by org_id,则是在整个公司内进行排名。

以下是个人见解:

sql中的over函数和row_numbert()函数配合使用,可生成行号。可对某一列的值进行排序,对于相同值的数据行进行分组排序。

执行语句:select row_number() over(order by AID DESC) as rowid,* from bb

SELECT
House.HouseId,
House.HouseName,
House.iconFlag,
House.orderId,
House.HouseJingYingFW,
House.HouseTel,
House.HouseCelPhone,
dbo.fnGetDistance(
118.328213, 35.081728, House.longitude,
House.latitude
) as jl,
Goods.originalPrice as levelCount,
Goods.presentPrice as levelAmount
FROM
House
LEFT JOIN (
select shopId,originalPrice,presentPrice
from (select no =row_number() over (partition by shopId order by createTime desc), * from Goods_info WHERE IsClear = 1)t
where no=1
) Goods on shopId = House.HouseId
where
(
House.isdel is null
or House.isdel = 0
)
and House.status = 1
and House.houseStatus <> 0
and House.housetype like '%1%'
order by
House.orderId desc,
jl ASC

 

 

 

IF OBJECT_ID ('dbo.fnGetDistance') IS NOT NULL
DROP FUNCTION dbo.fnGetDistance
GO

--计算地球上两个坐标点(经度,纬度)之间距离sql函数
CREATE FUNCTION [dbo].[fnGetDistance](@LatBegin REAL, @LngBegin REAL, @LatEnd REAL, @LngEnd REAL) RETURNS FLOAT
AS
BEGIN
--距离(千米)
DECLARE @Distance REAL
DECLARE @EARTH_RADIUS REAL
SET @EARTH_RADIUS = 6378.137
DECLARE @RadLatBegin REAL,@RadLatEnd REAL,@RadLatDiff REAL,@RadLngDiff REAL
SET @RadLatBegin = @LatBegin *PI()/180.0
SET @RadLatEnd = @LatEnd *PI()/180.0
SET @RadLatDiff = @RadLatBegin - @RadLatEnd
SET @RadLngDiff = @LngBegin *PI()/180.0 - @LngEnd *PI()/180.0
SET @Distance = 2 *ASIN(SQRT(POWER(SIN(@RadLatDiff/2), 2)+COS(@RadLatBegin)*COS(@RadLatEnd)*POWER(SIN(@RadLngDiff/2), 2)))
SET @Distance = @Distance * @EARTH_RADIUS
--SET @Distance = Round(@Distance * 10000) / 10000
RETURN @Distance
END
GO

 SQL中Group分组获取Top N方法实现

 
有产品表,包含id,name,city,addtime四个字段,因报表需要按城市分组,统计每个城市的最新10个产品,便向该表中插入了100万数据,做了如下系列测试:
  www.2cto.com  
CREATE TABLE [dbo].[products](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [name] [nvarchar](50) NULL,
    [addtime] [datetime] NULL,
    [city] [nvarchar](10) NULL,
 CONSTRAINT [PK_products] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
 
1、采用row_number方法,执行5次,平均下来8秒左右,速度最快。
  www.2cto.com  
select no, id,name,city 
from  (select  no =row_number() over (partition by city order by addtime desc), * from products)t
where no< 11 order by city asc,addtime desc
2、采用cross apply方法,执行了3次,基本都在3分5秒以上,已经很慢了。
 
select distinct b.id,b.name,b.city from products a 
cross apply (select top 10 * from products where city = a.city order by  addtime desc) b
3、采用Count查询,只执行了两次,第一次执行到5分钟时,取消任务执行了;第二次执行到13分钟时,没有hold住又直接停止了,实在无法忍受。
 
select id,name,city from products a 
where (  select count(city) from products where a.city = city and addtime>a.addtime) < 10
order by city asc,addtime desc
4、采用游标方法,这个最后测试的,执行了5次,每次都是10秒完成,感觉还不错。
 
 
declare @city nvarchar(10)
create table #Top(id int,name nvarchar(50),city nvarchar(10),addtime datetime)
declare mycursor cursor for
select  distinct city from products order by city asc
open mycursor
fetch next from mycursor into @city
while @@fetch_status =0
begin
    insert into #Top 
    select top 10 id,name,city,addtime from products where city = @city 
    fetch next from mycursor into @city
end 
close mycursor
deallocate mycursor
Select * from #Top order by city asc,addtime desc
drop table #Top
 
通过上述对比不难发现,在面临Group获取Top N场景时,可以首选row_number,游标cursor其次,另外两个就基本不考虑了,数据量大的时候根本没法使用。

转载于:https://www.cnblogs.com/blogzys/p/7039466.html

<think>好的,我现在需要解决用户的问题:在使用ROW_NUMBER()之后是否可以直接使用WHERE子句进行过滤,以及如何正确过滤数据。用户之前已经得到了关于使用CTE或子查询的答案,现在他们进一步提问,可能想确认是否还有其他方法或者深入理解背后的原因。 首先,我要回顾之前的回答,确保这次回复保持一致性。用户之前提到ROW_NUMBER()不能直接在WHERE中使用,需要通过CTE或子查询。用户现在可能希望更详细地了解为什么不能直接使用WHERE,或者是否有例外情况。 根据用户提供的引用,引用[1]指出在分页时,OVER子句中的ORDER BY需要与最终排序一致,否则序号可能有问题。引用[2]提到ROW_NUMBER()在并列情况下的局限性,可能影响过滤结果。引用[3]展示了在视图中使用ROW_NUMBER()的示例,需要注意的地方。 接下来,我需要明确回答用户的问题:ROW_NUMBER()生成的列不能在同一个查询层级中使用WHERE过滤,必须通过子查询或CTE。这是因为窗口函数在SQL中的执行顺序是在WHERE之后,所以直接引用ROW_NUMBER()的别名会无效。需要将ROW_NUMBER()的结果放在子查询或CTE中,然后在外部查询中使用WHERE。 然后,我需要提供具体的示例,如使用子查询和CTE的方法,并说明两者的区别和适用场景。同时,结合用户的引用,比如引用[1]中的分页注意事项,提醒用户排序一致的重要性。 还要注意用户可能存在的误区,比如试图在同一个SELECT语句中直接使用WHERE过滤ROW_NUMBER()的结果,这会导致错误,需要解释执行顺序的问题。同时,引用[2]提到的RANK()和DENSE_RANK()可能作为补充,但用户的问题集中在WHERE的使用,所以可能不需要展开,但可以作为相关问题提及。 最后,生成相关问题时要围绕ROW_NUMBER()的过滤、性能、与其他函数的比较,确保问题有针对性,帮助用户深入理解。</think>### 使用 `ROW_NUMBER()` 后正确应用 `WHERE` 过滤数据的方法 #### 核心规则与原因 **`ROW_NUMBER()` 生成的序号列无法直接在原查询层级中使用 `WHERE` 过滤**,因为窗口函数(如 `ROW_NUMBER()`)的执行顺序在 `WHERE` 子句之后。若强行尝试,会因逻辑顺序冲突导致语法错误或结果异常。以下是具体实现方法: --- ### 方法一:通过子查询或 CTE 分层处理 #### 1. 子查询分层 将 `ROW_NUMBER()` 的计算结果包装在子查询中,再通过外层 `WHERE` 过滤: ```sql SELECT * FROM ( SELECT ROW_NUMBER() OVER (ORDER BY CreateTime DESC) AS RowNum, OrderID, CustomerID, TotalAmount FROM Orders ) AS SubQuery WHERE RowNum BETWEEN 11 AND 20; -- 分页:取第2页数据(每页10条) ``` **逻辑顺序解析**: 1. 内层子查询生成 `RowNum` 2. 外层查询根据 `RowNum` 过滤 **关键优势**:明确分层逻辑,兼容所有 SQL 数据库[^1]。 --- #### 2. CTE(公用表表达式) 使用 CTE 增强可读性,适用于复杂逻辑: ```sql WITH NumberedOrders AS ( SELECT ROW_NUMBER() OVER (PARTITION BY CategoryID ORDER BY Price DESC) AS RowNum, ProductName, CategoryID, Price FROM Products ) SELECT * FROM NumberedOrders WHERE RowNum <= 3; -- 筛选每个分类下价格最高的前3个商品 ``` **适用场景**:需要多次引用窗口函数结果或多步骤处理时更高效[^3]。 --- ### 方法二:避免直接过滤的误区 #### 错误示例 ```sql -- 错误写法:直接在同级查询中使用 WHERE 过滤 RowNum SELECT ROW_NUMBER() OVER (ORDER BY SalesDate) AS RowNum, SalesID, Amount FROM Sales WHERE RowNum > 10; -- 报错:RowNum 列不存在于当前作用域 ``` **根本原因**:SQL 执行顺序为 `FROM` → `WHERE` → `SELECT`(含窗口函数),因此 `WHERE` 阶段无法访问 `SELECT` 中定义的 `RowNum`[^1]。 --- ### 进阶注意事项 #### 1. 排序一致性要求 若用 `ROW_NUMBER()` 分页,必须保证窗口函数的 `ORDER BY` 与最终结果的排序一致。例如: ```sql -- 分页查询需内外层排序一致 SELECT * FROM ( SELECT ROW_NUMBER() OVER (ORDER BY CreateTime DESC) AS RowNum, * FROM Articles ) AS SubQuery WHERE RowNum BETWEEN 21 AND 30 ORDER BY CreateTime DESC; -- 外层需重复排序声明 ``` **违反后果**:若外层未声明 `ORDER BY`,数据库可能返回无序结果;若排序字段不一致,可能导致分页数据错乱[^1]。 --- #### 2. 并列排名场景的替代方案 当需要处理并列排名(如分数相同均算第一名)时,`ROW_NUMBER()` 不适用,需改用 `RANK()` 或 `DENSE_RANK()`: ```sql SELECT StudentID, Score, RANK() OVER (ORDER BY Score DESC) AS Rank -- 允许并列排名(如:1,1,3) FROM ExamResults; ``` **对比特性**: - `RANK()`:允许并列,后续序号跳跃(如 1,1,3) - `DENSE_RANK()`:允许并列,后续序号连续(如 1,1,2)[^2] --- ### 性能优化建议 在大数据场景下,可通过以下方式优化 `ROW_NUMBER()` 效率: 1. **索引匹配**:为窗口函数中 `PARTITION BY` 和 `ORDER BY` 涉及的列创建索引 2. **分区剪枝**:通过 `WHERE` 提前过滤无关分区数据,减少计算量 3. **分页缓存**:对高频访问的固定分页结果(如首页数据)使用临时表存储 --- ### 示例扩展:动态分区过滤 ```sql -- 按部门分区,筛选每个部门工龄前5的员工 WITH DeptSeniority AS ( SELECT ROW_NUMBER() OVER ( PARTITION BY DepartmentID ORDER BY HireDate ASC -- 入职越早工龄越长 ) AS SeniorityRank, EmployeeID, Name, DepartmentID FROM Employees WHERE Status = 'Active' -- 提前过滤非活跃员工 ) SELECT * FROM DeptSeniority WHERE SeniorityRank <= 5; ``` --- ### 相关问题 1. 当 `ROW_NUMBER()` 的 `PARTITION BY` 字段包含 NULL 值时,分区逻辑会如何处理? 2. 如何通过 `ROW_NUMBER()` 实现「随机抽样」或「按比例抽取数据」? 3. 在 MySQL 8.0 以下版本中,如何模拟 `ROW_NUMBER()` 的功能?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值