Python中如何解决MySQL后端分页问题

刚刚入门没多久的后端 想请教一下,大家一般怎么做数据查询和分页问题 需要知道数据的总数( total ),以及第一页(前十条),大家一般怎么查询 想到的第一种就是查询一次总数 count(),然后在查询第 1-10 条 还有就是一口气查询全部,然后取到列表 count()得到总数,再从这么列表取第 1-10 条 因为我们项目的数据量并不大,其实怎么写问题都不大,不过还是很想了解。。。 只了解基础的 sql 语句,对复杂的不是很懂,所以想请教一下!!谢谢各位了~~

还有一个疑问,现在我有 2 个 id,然后有个表记录日志(很多),我只想取这两个 id 最近的三十条日志,有没有直接一条 sql 语句能实现


Python中如何解决MySQL后端分页问题

11 回复

问题 1
$query->where()->where();
$count = $query->count();
$data = $query->limit()->offset()->get();
## 问题 2
$query->whereIn([id1, id2])->limit(30)->get();

****
楼下大神帮转换成 SQL


在Python里用MySQL做分页,直接用LIMITOFFSET就行,这是最直接的方法。不过数据量大了性能会变差,特别是OFFSET值很大的时候。给你个完整的例子:

import mysql.connector
from mysql.connector import Error

def get_paginated_data(page_number=1, page_size=10):
    """获取分页数据的基础实现"""
    try:
        connection = mysql.connector.connect(
            host='localhost',
            database='your_database',
            user='your_user',
            password='your_password'
        )
        
        cursor = connection.cursor(dictionary=True)
        
        # 计算偏移量
        offset = (page_number - 1) * page_size
        
        # 基础分页查询
        query = """
        SELECT id, name, email, created_at 
        FROM users 
        ORDER BY id 
        LIMIT %s OFFSET %s
        """
        
        cursor.execute(query, (page_size, offset))
        results = cursor.fetchall()
        
        # 获取总记录数(用于计算总页数)
        count_query = "SELECT COUNT(*) as total FROM users"
        cursor.execute(count_query)
        total_records = cursor.fetchone()['total']
        total_pages = (total_records + page_size - 1) // page_size
        
        return {
            'data': results,
            'current_page': page_number,
            'page_size': page_size,
            'total_records': total_records,
            'total_pages': total_pages
        }
        
    except Error as e:
        print(f"数据库错误: {e}")
        return None
    finally:
        if connection.is_connected():
            cursor.close()
            connection.close()

# 使用示例
if __name__ == "__main__":
    # 获取第2页,每页10条
    page_data = get_paginated_data(page_number=2, page_size=10)
    
    if page_data:
        print(f"第{page_data['current_page']}页数据:")
        for row in page_data['data']:
            print(row)
        print(f"总记录数: {page_data['total_records']}")
        print(f"总页数: {page_data['total_pages']}")

如果数据量特别大,考虑用基于游标的分页,用WHERE子句代替OFFSET

def get_paginated_data_cursor(last_id=None, page_size=10):
    """基于游标的分页(性能更好)"""
    try:
        connection = mysql.connector.connect(
            host='localhost',
            database='your_database',
            user='your_user',
            password='your_password'
        )
        
        cursor = connection.cursor(dictionary=True)
        
        if last_id:
            # 不是第一页,使用WHERE过滤
            query = """
            SELECT id, name, email, created_at 
            FROM users 
            WHERE id > %s 
            ORDER BY id 
            LIMIT %s
            """
            cursor.execute(query, (last_id, page_size))
        else:
            # 第一页
            query = """
            SELECT id, name, email, created_at 
            FROM users 
            ORDER BY id 
            LIMIT %s
            """
            cursor.execute(query, (page_size,))
        
        results = cursor.fetchall()
        
        # 获取下一页的起始ID
        next_last_id = results[-1]['id'] if results else None
        
        return {
            'data': results,
            'next_last_id': next_last_id,
            'has_more': len(results) == page_size
        }
        
    except Error as e:
        print(f"数据库错误: {e}")
        return None
    finally:
        if connection.is_connected():
            cursor.close()
            connection.close()

简单说就是小数据用LIMIT/OFFSET,大数据用游标分页。

问题 2 是需要两个 id 各取 30 条,而不是加起来 30 条

mysql 分页的话,我一般是使用 calc_found_rows 修饰 sql:
比如:查询第一页有 10 条数据:
select calc_found_rows * from table where … limit 0,10;
获取总数直接在这条后面执行:
select found_rows(); 就可以拿到前一条查询的总数。

最近的三十条的话,应该是按照时间或者自增 id,逆序查询取前三十就好了吧
select * from table where log_id in (id1, id2) order by id desc limit 30;

是的。分成兩個,一個查 count,一個查偏移數據。不過 mysql innodb 數據量大後會 count 會慢出翔,需要特殊優化。

数据总数获取没办法, 如果需要精确的数值只能一次查完, 不过大部分时间我们并不需求精确的记数的, 因为大数据的情况下每分每秒都在往里面插数据, 所以就算你显示出来也是当时的正确数值, 所以这里可以取巧一下, 以 1 分钟为一个单位获取总数存在缓存里, 每次只读取那一分钟定点的数值就行了.
而且大数据情况下 limit 写法和普通写法也不一样的, mysql limit 是获取所有数据以后再抛弃不需要的数据, 所以如果你 limit 的值特别大的话会很慢, 所以这种情况一般是获取主键再回表查询的.

至于你第二个问题, 我觉得做人没必要那么偷懒一次获取…

有个叫 pagehelper 的插件.使用前要先在 xml 里配置。

PageHelper.offsetPage(offset, count );
查询 sql 语句;
int total = new PageInfo().getTotal();

我是新手,不了解具体实现,只会使用,可以参考下。

楼上有个地方写错了,new pageinfo()要用查询结果集当参数。

count 查所有, limit offset, size 查分页数据.

注意当 offset 特别大的时候这个方法行不通

calc_found_rows 这种方式其实性能不好,倒不如用 count(*)

哦,一直在用,倒是没有关注过内部实现性能问题。请教一下,具体会有多少影响呢,到什么量级的查询量会凸现出来这个问题,谢谢。

回到顶部