使用动态ORDER BY和LIMIT /分页保持嵌套行

时间:2013-01-09 17:02:46

标签: php mysql sql postgresql parent

考虑需要显示如下数据的情况:

Parent 1 info
    Child 1A info
    Child 1B info
    ...
Parent 2 info
    Child 2A info
    Child 2B info
    ...

请注意,孩子只有一个级别,所以没有必要进入"嵌套集"在数据库级别。例如,在我当前的项目中,数据需要显示如下:

First name  | Last name | City  | ...
---------------------------------
John        | Doe       | Denver

    Sales:
    ---------------------
    Date    | Total | ...
    -----------------
    Jan 1   | $100
    Jan 15  | $200
    Feb 1   | $100

Suzie       | Springer  | New York

    Sales:
    ---------------------
    Date    | Total | ...
    -----------------
    ...

我的问题是关于获取此页面数据所需的SQL,同时仍然通过使用LIMIT子句(或等效子句)进行分页。

在这种情况下,应该首先列出具有最新销售额的帐户,但当然每个帐户的所有销售额都要低于一年。

首先让我先问一个更简单的案例:让我们说排序顺序并不重要。然后我可以简单地通过帐户ID(简化示例)订购:

SELECT sh.account_id, a.first_name, a.last_name,
    sh.sale_id, sh.total, sh.payment_time
FROM sales_header sh
JOIN account a on sh.account_id = a.account_id
WHERE a.account_type = 'parent'
ORDER BY sh.account_id

但是,当引入分页时,如何确保特定帐户的所有销售保持在一起?很难知道LIMIT应该有多大。

现在让我们添加所需的真实排序顺序:

SELECT sh.account_id, a.first_name, a.last_name,
    sh.sale_id, sh.total, sh.payment_time
FROM sales_header sh
JOIN account a on sh.account_id = a.account_id
WHERE a.account_type = 'parent'
ORDER BY sh.payment_time

检索所有行时,没有问题 - 我只需运行查询,然后在我的应用程序代码中使用account_id索引结果(在本例中为PHP)。

但如果我添加LIMIT,那么我可能无法获得每个帐户的所有销售额,特别是对于既有旧销售又近期销售的帐户。

我已经提出了一个解决方案,但它相当复杂,特别是因为我处理动态查询参数而不是我在这里展示的简化版本。我的解决方案是运行两个单独的查询。

首先,获取我需要的所有帐户ID:

SELECT DISTINCT sh.account_id
FROM sales_header sh
JOIN account a on sh.account_id = a.account_id
WHERE a.account_type = 'parent'
ORDER BY sh.payment_time
LIMIT 10

然后,将ID放入名为$ account_ids的PHP数组中,然后构建返回显示页面所需的实际结果的查询:

$sql = 'SELECT sh.account_id, a.first_name, a.last_name,
    sh.sale_id, sh.total, sh.payment_time
FROM sales_header sh
JOIN account a on sh.account_id = a.account_id
WHERE sh.account_id IN ('. implode(',', $account_ids). ')
ORDER BY sh.payment_time';

这当然也可以通过放置SQL的SQL在单个查询中完成 第一个查询implode()函数的位置,如:

WHERE sh.account_id IN (SELECT DISTINCT sh.account_id ... LIMIT 10)

但这引起了对子查询和数据库可移植性限制的担忧 - 我使用的框架允许您使用可移植的limit()方法设置限制 到不同的数据库,但我不确定如果我试图在子查询中使用限制它会如何反应 例如,与SQL Server一样。

所以,我真正的问题是:这是解决问题的最佳方法,还是有替代(更简单)的解决方案?你在类似的情况下做了什么?

1 个答案:

答案 0 :(得分:1)

SELECT
    sh.account_id, a.first_name, a.last_name,
    sh.sale_id, sh.total, sh.payment_time
FROM
    sales_header sh
    inner JOIN
    account a on sh.account_id = a.account_id
    inner join (
        SELECT sh.account_id, max(sh.payment_time) payment_time
        FROM
            sales_header sh
            inner JOIN
            account a on sh.account_id = a.account_id
        WHERE a.account_type = 'parent'
        group by sh.account_id
        order by 2 desc
        limit 10
    ) acs on acs.account_id = a.account_id
ORDER BY acs.payment_time desc, acs.account_id, sh.payment_time desc

我尝试使其与数据库无关,但必须在Sql Server中将limit子句转换为select top 10