选择前N个不同子表行的所有行

时间:2016-03-08 00:10:30

标签: sql-server oracle postgresql many-to-many

看起来这应该是一个简单解决方案的常见问题,但我还没有找到它。 我想计算child_order,这是不同子表行的出现顺序,如下面的数据所示:

child_order   PK1  PK2       ACCESS     ACCESS_ID
1             99   Al        NULL       NULL
2             55   Charles   Accounts   1
2             55   Charles   Desktop    2
2             55   Charles   Printer    3
2             55   Charles   Servers    4
2             55   Charles   VMs        5
3             66   Charles   Desktop    2
3             66   Charles   VMs        5
4             22   Chris     Desktop    2
4             22   Chris     Printer    3
4             22   Chris     Servers    4
5             89   Evan      Desktop    2

通过以下查询检索:

SELECT sub1.*
FROM (
    SELECT ??? as child_order, sub2.*
    FROM (
        SELECT ct.PK1, ct.PK2, pt1.ACCESS, pt1.ACCESS_ID
        FROM child_table ct
        LEFT JOIN some_linktable lt ON lt.child_id = ct.id
        LEFT JOIN parent_table1 pt1 ON lt.parent_id = pt1.id
        WHERE ct.PK2 IN ('Charles', 'Evan', 'Al', 'Chris')
        ORDER BY ct.PK2, pt1.ACCESS -- Order must be preserved
    ) sub2
) sub1
WHERE child_order < 10 AND (other_conditions)

我可以使用子查询,聚合,分析等,但不能使用CTE /&#34; WITH&#34;语句或临时表,因为动态生成SQL的复杂性。

具体来说,我正在为连接多个表的查询生成搜索结果的分页SQL(针对多个DBMS)。 我试图弄清楚如何简单地显示前N行,不计算由于连接而重复(例如,Chris只计算一行。Access显示&#34;桌面,打印机,服务器&#34;)

我已经尝试了DENSE_RANK() OVER (ORDER BY PK1, PK2),但当然我获得了PK1 PK2顺序的排名,这对于WHERE子句来说是无用的。例如,Al的值将大于1。

我已尝试过DENSE_RANK() OVER (ORDER BY PK2, ACCESS),但它只列举了搜索字词,而不是子表格行。

我已经尝试DENSE_RANK() OVER (PARTITION BY PK2, ACCESS ORDER BY (SELECT NULL))(让DENSE_RANK使用它给出的行顺序,这就是我想要对值进行排名的方式),但只有&#34; 1&#34;归还。

我会省略我的其他&#34; 尝试随机的东西&#34; - 阶段尝试。

我想避免使用SELECT DISTINCT PK1, PK2 WHERE (search) ORDER BY (sortorder)子查询,因为可能存在零个或非常多个主键字段,因此动态SQL生成会很棘手,另外,我怀疑性能会因所有{{1检查。

1 个答案:

答案 0 :(得分:1)

尽管您对SELECT DISTINCT存有疑虑,但这可能是子查询的最佳选择:

SELECT row_number() OVER (ORDER BY PK2) AS child_order, PK1, PK2
FROM (
  SELECT DISTINCT PK1, PK2
  FROM child_table
  WHERE PK2 IN ('Charles', 'Evan', 'Al', 'Chris')
  ORDER BY PK2
  LIMIT 9) sub2

child_order字段仅取决于表child_table,并且您只需要9行,因此仅在该表的子查询中计算child_order。完成后,您可以加入其他表。如果您在child_table(PK1, PK2)上有索引,那么这应该是一个非常快速的索引搜索。它需要一些过滤和内部限制,因此包络查询更简单:

SELECT sub1.child_order, PK1, PK2, pt1.ACCESS, pt1.ACCESS_ID
FROM child_table ct
JOIN (
  SELECT row_number() OVER (ORDER BY PK2) AS child_order, PK1, PK2
  FROM (
    SELECT DISTINCT PK1, PK2
    FROM child_table
    WHERE PK2 IN ('Charles', 'Evan', 'Al', 'Chris')
    ORDER BY PK2
    LIMIT 9) sub2
  ) sub1 USING (PK1, PK2)
LEFT JOIN some_linktable lt ON lt.child_id = ct.id
LEFT JOIN parent_table1 pt1 ON lt.parent_id = pt1.id
WHERE <other conditions>
ORDER BY sub1.child_order, pt1.ACCESS; -- Faster to order by int