【问题标题】:Node.js API with express & mysql - Getting record count, page number, ... & provide pagination带有 express 和 mysql 的 Node.js API - 获取记录数、页码……并提供分页
【发布时间】:2018-08-03 08:09:10
【问题描述】:

我想在响应中获取并提供总的 total_record_count、page_number、page_size、total_pages、has_more 信息,并且能够对结果进行分页,因为我将查询限制为 100。最好的方法是什么?

这是我目前的设置:

router.get('/?', function(req, res, next) {
    const sql = "SELECT * from users ";
    const existingParams = ["title", "active"].filter(field => req.query[field]);

    if (existingParams.length) {
        sql += " WHERE ";
        sql += existingParams.map(field => `${field} = ?`).join(" AND ");
        sql += " LIMIT 100 ";
    }

    connection.query(
        sql,
        existingParams.map(field => req.query[field]),
        function (error, results, fields) {
            res.json({"status": 200, "error": null, "total_record_count": 604, "page_number": 1, "page_size": 100, "total_pages": 7, "has_more": true, "records": results});
        }
    );
});

如果 total_record_count 超过 LIMIT,我希望在 url 中能够提供/允许页面参数“p”。所以查询参数p指定返回哪个页面,从1开始。如果省略p参数,则默认值为1。类似这样:

http://localhost:4001/api/v1/users/?active=0&title=mr&p=1

【问题讨论】:

    标签: javascript mysql sql node.js express


    【解决方案1】:

    对于分页,您需要在 MySQL 中进行如下查询

    SELECT * FROM users LIMIT 0,10

    有两个参数,第一个参数指定要返回的第一行的偏移量,第二个参数指定要返回的最大行数。初始行的偏移量为 0(不是 1)。

    因为您希望将默认值设置为第一页和 100 个结果

    router.get('/?', function(req, res, next) {
        const sql = "SELECT * from users ";
        const existingParams = ["title", "active"].filter(field => req.query[field]);
        const pageNum = req.query.p || 1;
        const pageSize = req.query.p_size || 100;
    
    
        if (existingParams.length) {
            sql += " WHERE ";
            sql += existingParams.map(field => `${field} = ?`).join(" AND ");
        }
        sql += ` LIMIT  ${(pageNum - 1) * PageSize},${PageSize}`;
        ...
    });
    

    关于提供总行数的第二个问题,您需要为此运行两个查询。您可以在 mysql>4.0 中使用SQL_CALC_FOUND_ROWS and FOUND_ROWS()。但是,当我们在查询中为 WHERE/ORDER 子句提供适当的索引时,使用两个单独的查询而不是一个使用 SQL_CALC_FOUND_ROWS 的查询要快得多。(一个用于数据,一个用于获取所有行数)。

    现在您必须并行运行这两个查询以提高性能,因此您可以使用我编写的this function 来并行运行回调并等待所有回调完成。或者你可以在这里使用promisified MySQL 库,或者你可以使用Bluebird 使你当前的库promisified。

    像这样:

    const connection = mysql.createConnection({.....});
    global.db  = Bluebird.promisifyAll(connection);
    db.queryAsync("SELECT * FROM users")
    .then(function(rows){ 
        console.log(rows);
    })
    

    然后像这样运行查询

    Promise.all([
        db.queryAsync("SELECT * FROM users WHERE title='ridham' LIMIT 0,10"), // generated by your code
        db.queryAsync("SELECT COUNT(*) FROM users WHERE title='ridham'")
    ]).then(([data, count]) => {
            // your logic to send response
    })
    

    或者您也可以运行以下查询,这也可以

    SELECT * FROM 'table' JOIN (SELECT COUNT(*) FROM 'table') t2 WHERE title='ridham' LIMIT 0,10")

    另外,您可以使用 SequilizeWaterline 之类的 ORM。这至少会让你喜欢 MYSQL 轻松 10 倍。

    作为 sequilize 中的示例:

    User.findAndCountAll({
        where: {
            title: {
              [Op.like]: 'ridham'
            }
        },
        offset: 10,
        limit: 2
    })
    .then(result => {
        console.log(result.count);
        console.log(result.rows);
    });
    

    【讨论】:

    • But if you are having WHERE and/or Order clause it's much better to run two queries 为什么要运行两次查询?理想情况下,记录计数要包括 WHERE 部分,否则分页没有意义。例如。假设该表包含 1000 条记录,出于安全/过滤等原因,该用户可以访问 500 条记录,因此您有一个 where 子句,总页数应等于 500 / 100 = 5,而不是 1000 / 100 = 10;所以SELECT COUNT(*) FROM table 会给出错误的结果。
    • 如果您有限制的结果,那么您可以随时添加条件来计算查询。 percona.com/blog/2007/08/28/…正在编辑答案。
    • 感谢 Ridham 的解释和示例。它已经在工作了。我不确定如何使用它:pastebin.com/we4bdUFu。我需要如何或在哪里实现这一点?
    • 抱歉,您可以选择其中一种方法。 1.按照@Keith的建议使用SQL_CALC_FOUND_ROWS,2.运行两个查询,3.使用连接查询,4.使用sequilize。如果您选择 2 查询方法,您可以使用该 pastebin.com/we4bdUFu 并行而不是顺序运行 2 个回调。
    【解决方案2】:

    MySQL LIMIT 有 2 个值,偏移量和行数。操纵这些是您当然可以进行分页的方式。

    例如。如果说每页有 10 条记录。 第1页=LIMIT 0, 10 第2页=LIMIT 10, 10 第3页=LIMIT 20, 10

    IOW:LIMIT (pageNo - 1) * PageSize, PageSize

    现在使用限制的一个问题是记录数是针对结果集的,IOW:有限的 10 条记录。

    但是你可以做的是让 MySQL 存储如果没有应用 LIMIT 的记录数。您可以通过在 SQL 前面加上 SQL_CALC_FOUND_ROWS 来检索它。

    例如。 SELECT SQL_CALC_FOUND_ROWS * FROM TABLE WHERE something LIMIT 10, 10

    然后您可以执行另一个检索该值的查询。

    SELECT FOUND_ROWS();

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-10-19
      • 2021-10-27
      • 2016-03-11
      • 2017-12-07
      • 2021-06-11
      • 1970-01-01
      • 2019-05-20
      相关资源
      最近更新 更多