[MSSQL]ROW_NUMBER函数

本文详细介绍了 SQL 中 ROW_NUMBER() 函数的使用方法及其应用场景,包括如何通过 ORDER BY 和 PARTITION BY 子句实现结果集的排序和分区,以及如何利用该函数进行分页查询。

ROW_NUMBER()在SQL2K5版本中新增,该函数返回结果集分区内行的序列号,每个分区的第一行从 1 开始,连续不间断,后跟OVER BY子句或者PARTITION BY子句

先构造一张表,放一些数据进行,SQL脚本如下

--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',20 union all
SELECT 'a1','b2','c2',30 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                   20
a1                   b2                   c2                   30
a1                   b2                   c3                   40
a1                   b2                   c3                   50
a2                   b3                   c3                   12
a2                   b3                   c3                   22
a2                   b3                   c3                   32
 
(8 行受影响)

ORDER BY子句

如果单独使用ORDER BY子句,则整个结果集为一个分区,

下边的SQL语句单使用了ORDER BY子句,先按GRP_A排序,然后根据排序后的结果额外生成一连续自增的NUM列

SELECT 
    *,
    ROW_NUMBER()OVER(ORDER BY GRP_A) AS NUM
FROM T

结果集如下,不算复杂啊:

GRP_A                GRP_B                GRP_C                VAL         NUM
-------------------- -------------------- -------------------- ----------- --------------------
a1                   b1                   c1                   10          1
a1                   b1                   c2                   20          2
a1                   b2                   c2                   30          3
a1                   b2                   c3                   40          4
a1                   b2                   c3                   50          5
a2                   b3                   c3                   12          6
a2                   b3                   c3                   22          7
a2                   b3                   c3                   32          8
 
(8 行受影响)

跟不加ROW_NUMBER函数的区别就是增加了最后那一列,NUM,其值是递增的,增量为1

同理可以按照其它字段排序,如VAL,或者GRP_B,GRP_C等,列名不变,列值不变,变的是其它列的顺序

这个查询仅有一个分区,就是整个结果集,整个结果集内有这么一列,自增NUM列,可以用来分页或者啥的啥的

没有什么特别之处,ORDER BY 跟普通的ORDER BY 类似,也可以有多个列的排序,如:

SELECT 
    *,
    ROW_NUMBER()OVER(ORDER BY GRP_A ASC,GRP_B DESC,VAL ASC) AS NUM
FROM T

最终影响的还是结果集中除NUM列外的其它数据的排序

 

PARTITION BY XXX ORDER BY YYY子句

使用PARTITION BY子句后,结果集就会按照该字段进行分区,这时候仍然要使用ORDER BY子句,影响的是分区内的排序,然后在每个分区内生成从1开始的自增列:

SELECT 
    *,
    ROW_NUMBER()OVER(PARTITION BY GRP_A ORDER BY VAL) AS NUM
FROM T

这时候结果集发生变化了,必须得发生!

GRP_A                GRP_B                GRP_C                VAL         NUM
-------------------- -------------------- -------------------- ----------- --------------------
a1                   b1                   c1                   10          1
a1                   b1                   c2                   20          2
a1                   b2                   c2                   30          3
a1                   b2                   c3                   40          4
a1                   b2                   c3                   50          5
a2                   b3                   c3                   12          1
a2                   b3                   c3                   22          2
a2                   b3                   c3                   32          3
 
(8 行受影响)

结果集先是按照GRP_A字段进行了分区:a1分区和a2分区,分别为前五行和后三行,在每个分区内NUM从1递增,增量为1,排序规则为VAL ASC
嗯,很简单吧,
应用场景
比如我们有一张表,该表存储了某件商品的图片,商品与图片是一对多的关系,即一件商品有N件图片,现在要求取图片最多的那个商品
或者每件商品只取按照添加顺序的倒序取前两张图片,这时候就可以用ROW_NUMBER()OVER(PARTITION BY 商品编号 ORDER BY 添加时间 DESC) AS NUM来取
随后在结果集内过滤NUM于2的数据即可!
整体不算复杂,更多的应用场景还是分页时,当我们的自增主键不连续时,要取前20条数据,就可以利用该函数特性生成连续递增的NUM列,再BETWEEN即可
分页示例
SELECT * FROM (SELECT 
    *,
    ROW_NUMBER()OVER(ORDER BY VAL) AS NUM
FROM T) AS SOMETABLENAME
WHERE NUM BETWEEN 1 AND 5


相对应的结果集

GRP_A                GRP_B                GRP_C                VAL         NUM
-------------------- -------------------- -------------------- ----------- --------------------
a1                   b1                   c1                   10          1
a2                   b3                   c3                   12          2
a1                   b1                   c2                   20          3
a2                   b3                   c3                   22          4
a1                   b2                   c2                   30          5
 
(5 行受影响)
仅返回了前五行
 

转载于:https://www.cnblogs.com/kkun/archive/2011/08/12/2136012.html

def del_row(value, tab_name='tbCourse', col_name='Cno'): """删除指定行数据""" connection = pymysql.connect(host=db_config.HOST, user=db_config.USER, port=db_config.DB_PORT, password=db_config.PWD, database=db_config.DB_NAME, cursorclass=pymysql.cursors.Cursor) with connection: try: with connection.cursor() as cursor: sql = f"DELETE FROM {tab_name} WHERE {col_name}=%s;" affected_rows = cursor.execute(sql, (value,)) connection.commit() except Exception as e: print(f"删除错误: {e}") return 0 return affected_rows根据上述函数,修改一下代码使之可以实现删除数据的功能import FreeSimpleGUI as sg #需要切换为MySQL数据库,请将fetch_data_mssql更改为:fetch_data_mysql from fetch_data_mysql import getData, del_row, insert_row,insert_workrow,getData_aim from db_config import get_header def open_homework_delet(): """打开删除作业数据的窗口""" sublayout_homedelete = [ [sg.T('请输入要删除的学号:', justification='right'), sg.Input('', size=(50, None), key='-sub_Sno-')], [sg.Button('查询', key='-btn_homework_search-'), sg.Button('确认删除', key='-btn_homework_delet', disabled=True)] ] subwin_homeworkdelete = sg.Window('数据删除', sublayout_homedelete, font=('微软雅黑', 13), modal=True) result = None while True: ev2, vals2 = subwin_homeworkdelete.read() if ev2 in (sg.WIN_CLOSED, 'Exit'): break elif ev2 == '-btn_homework_search-': student_id = vals2['-sub_Sno-'].strip() if not student_id: sg.popup("请输入学号") continue # 查询该学号的作业记录 result = getData_aim(student_id, tab_name='homework') if result: # 显示查询结果 headers = get_header(tab_name='homework') table_layout = [ [sg.Table( values=result, headings=headers, auto_size_columns=True, display_row_numbers=True, justification='left', num_rows=min(5, len(result)), key='-RESULT_TABLE-' )], [sg.Text(f"找到 {len(result)} 条记录,确认删除吗?")] ] # 启用删除按钮 subwin_homeworkdelete['-btn_homework_delet'].update(disabled=False) # 添加结果表格 if hasattr(subwin_homeworkdelete, 'result_frame'): subwin_homeworkdelete['-RESULT_FRAME-'].update(visible=True) subwin_homeworkdelete['-RESULT_TABLE-'].update(result) else: frame = sg.Frame('查询结果', table_layout, key='-RESULT_FRAME-') subwin_homeworkdelete.extend_layout(sublayout_homedelete, [[frame]]) else: sg.popup("未找到相关记录") if hasattr(subwin_homeworkdelete, 'result_frame'): subwin_homeworkdelete['-RESULT_FRAME-'].update(visible=False) subwin_homeworkdelete['-btn_delete-'].update(disabled=True) elif ev2 == '-btn_homework_delet' and result: # 确认删除 if sg.popup_yes_no('确定要删除这些记录吗?此操作不可恢复!') == 'Yes': # 执行删除操作 affected_rows = del_row(vals2['-sub_Sno-'], tab_name='homework', col_name='Sno') if affected_rows > 0: sg.popup(f"成功删除 {affected_rows} 条记录") result = None subwin_homeworkdelete['-RESULT_FRAME-'].update(visible=False) subwin_homeworkdelete['-btn_delete-'].update(disabled=True) else: sg.popup("删除失败,请重试") subwin_homeworkdelete.close()if __name__ == '__main__': sg.theme('DefaultNoMoreNagging') data_ducsum = getData(tab_name='ducsum') header_ducsum = get_header(tab_name='ducsum') data_homework = getData(tab_name='homework') header_homework = get_header(tab_name='homework') data_courses = getData(tab_name='tbcourse') header_courses = get_header(tab_name='course') data_students = getData(tab_name='tbStuinfo') header_students = get_header(tab_name='student') header_sc = get_header(tab_name='sc') homework_layout = [ [sg.InputText(),sg.Button(button_text='搜索',key='-btn_homework_search'), sg.Button(button_text='添加',key='-btn_add_homework'), sg.Button(button_text='删除',key='-btn_homework_delet'), sg.Button(button_text='修改') ], [sg.Table(values=data_homework, headings=header_homework, size=(550,400), max_col_width=100, auto_size_columns=False, display_row_numbers=False, justification='left', num_rows=15, alternating_row_color='lightyellow', key='-TABLE_course-', selected_row_colors='red on yellow', enable_events=True, expand_x=True, expand_y=True, enable_click_events=True, # Comment out to not enable header and other clicks tooltip='This is a course table') ], [sg.Button(button_text='上一页', key='-btn_refresh_course-'), sg.Button(button_text='下一页', key='-btn_del_course-') ] ]window = sg.Window('课后作业评分系统', layout, default_element_size=(10, 1),size=(740,580), font=('微软雅黑', 14)) cur_cno=None #当前的选择的课程号 cur_course_row_number=None #当前课程表中被选择的行号,行号从0开始,-1为标题行 subwin_addCourse_active = False subwin_addCourse = None while True: event, values = window.read() # sg.popup_non_blocking(event, values) # print(event) # print(values) if event == sg.WIN_CLOSED: # always, always give a way out! break if event=='-btn_del_course-': # 删除选择的课程号 aff_rows = del_row(cno=cur_cno, tab_name='tbCourse') print('影响的行数:', aff_rows) if(aff_rows>0): data_courses = list(data_courses) data_courses.pop(cur_course_row_number) #从数据表格中移除选择的行数据 window['-TABLE_course-'].update(data_courses) cur_course_row_number = None cur_cno = None elif event=='-btn_add_course-': # 添加课程 # 先弹窗,获取信息 add_affected_rows = open_addCourseWindow() if add_affected_rows>0: data_courses = refreshData(table_key='-TABLE_course-', tab_name='tbcourse') elif event=='-btn_refresh_course-': # 刷新课程表的显示 data_courses = refreshData(table_key='-TABLE_course-', tab_name='tbcourse') elif event=='-btn_add_homework': add_affected_row = open_addHomeworkWindow() if add_affected_row>0: data_courses = refreshData(table_key='-TABLE_course-',tab_name='tbcourse') elif event=='-btn_hundred_system': add_affected_row = open_hundredsystem() if add_affected_row>0: data_courses = refreshData(table_key='-TABLE_course-',tab_name='tbcourse') elif event=='-btn_rank_system': add_affected_row = open_rank_system() if add_affected_row>0: data_courses = refreshData(table_key='-TABLE_course-',tab_name='tbcourse') elif event=='-btn_homework_search': add_affected_row = open_homework_search() if add_affected_row>0: data_courses = refreshData(table_key='-TABLE_course-',tab_name='tbcourse') elif event=='-btn_homework_delet': add_affected_row = open_homework_delet() if add_affected_row>0: data_courses = refreshData(table_key='-TABLE_course-',tab_name='tbcourse') if isinstance(event, tuple): # TABLE CLICKED Event has value in format ('-TABLE-', '+CLICKED+', (row,col)) if event[0] == '-TABLE_course-': cur_course_row_number = event[2][0] col_number = event[2][1] if cur_course_row_number is not None: # 获取鼠标点击行的数据 cur_row_value = data_courses[cur_course_row_number] # 获取点击行的Cno cur_cno = cur_row_value[0] print('当前选择的课程号:', cur_cno) window.close()
最新发布
06-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值