简体   繁体   English

Oracle SQL:至少选择前n行,直到列值与最后一行不同为止

[英]Oracle SQL: Select at least the first n rows, continue until a column value is different from the last one

given a table foo of the following structure (Oracle 11g): 给出具有以下结构的表foo(Oracle 11g):

ID | GROUP_ID
 1 | 100
 2 | 100
 3 | 100
 4 | 200
 5 | 300
 6 | 300
 7 | 400

I want to select the first n rows (ordered by ID) or more, such that I always get a complete group. 我想选择前n行(按ID排序)或更多,这样我总能得到一个完整的组。

Example: 例:

n = 2: I want to get at least the first two rows, but since ID 3 also belongs to group 100, I want to get that as well. n = 2:我想至少获得前两行,但是由于ID 3也属于组100,所以我也希望得到它。

n = 4: Give me the first four rows and I am happy ;-) n = 4:给我前四行,我很高兴;-)

n = 5: Rows 1-6 are requested. n = 5:请求1-6行。

Your help is highly appreciated! 非常感谢您的帮助!

Solution using rank() : 使用rank()解决方案:

select id, group_id
from (select t.*, rank() over (order by group_id) as rnk
    from t)
where rnk <= :n;

Building test data: 建筑测试数据:

SQL> create table t (id number not null primary key
  2      , group_id number not null);

Table created.

SQL> insert into t values (1, 100);

1 row created.

SQL> insert into t values (2, 100);

1 row created.

SQL> insert into t values (3, 100);

1 row created.

SQL> insert into t values (4, 200);

1 row created.

SQL> insert into t values (5, 300);

1 row created.

SQL> insert into t values (6, 300);

1 row created.

SQL> insert into t values (7, 400);

1 row created.

SQL> commit;

Commit complete.
SQL>

Running... 正在运行...

SQL> var n number
SQL> exec :n := 2;

PL/SQL procedure successfully completed.

SQL> select id, group_id
  2  from (select t.*, rank() over (order by group_id) as rnk
  3      from t)
  4  where rnk <= :n;

        ID   GROUP_ID
---------- ----------
         1        100
         2        100
         3        100

SQL> exec :n := 4;

PL/SQL procedure successfully completed.

SQL> select id, group_id
  2  from (select t.*, rank() over (order by group_id) as rnk
  3      from t)
  4  where rnk <= :n;

        ID   GROUP_ID
---------- ----------
         1        100
         2        100
         3        100
         4        200

SQL> exec :n := 5;

PL/SQL procedure successfully completed.

SQL> select id, group_id
  2  from (select t.*, rank() over (order by group_id) as rnk
  3      from t)
  4  where rnk <= :n;

        ID   GROUP_ID
---------- ----------
         1        100
         2        100
         3        100
         4        200
         5        300
         6        300

6 rows selected.

EDIT Here is version that includes the for update clause (:n = 2): 编辑这是包含for update子句(:n = 2)的版本:

SQL> select id, group_id
  2  from T
  3  where rowid in (select RID
  4      from (select t.rowid as RID, t.*, rank() over (order by group_id) as rnk
  5          from t)
  6      where rnk <= :n)
  7  for update;

        ID   GROUP_ID
---------- ----------
         1        100
         2        100
         3        100

If your ID s are always sequential (without gaps) from 1. And if your Group_ID s never occur as a second group elsewhere. 如果您的ID始终从1开始是连续的(无间隔),并且您的Group_ID从未在其他地方作为第二个组出现。 And if your Group_ID s are always ascending in value... 如果您的Group_ID的价值始终在上升...

SELECT
  *
FROM
  foo
WHERE
  Group_ID <= (SELECT Group_ID FROM foo WHERE ID = n)
ORDER BY
  ID

You'll benefit here from having separate indexes on ID and Group_ID 您将从IDGroup_ID上具有单独的索引中Group_ID

If it is always true that GROUP_ID is contiguous and ascending, then this is easily solved with SQL using an analytical ROW_NUMBER() function: 如果始终确实GROUP_ID是连续且递增的,那么可以使用SQL的解析ROW_NUMBER()函数轻松解决此问题:

SQL> select id
  2         , group_id
  3  from foo
  4  where group_id <= ( select group_id
  5                     from (
  6                              select f.group_id
  7                                     , row_number() over (order by f.id asc) rn
  8                              from foo f
  9                              )
 10                          where rn = &n )
 11  order by id
 12  /
Enter value for n: 2
old  10:                         where rn = &n )
new  10:                         where rn = 2 )

        ID   GROUP_ID
---------- ----------
         1        100
         2        100
         3        100

SQL> r
  1  select id
  2         , group_id
  3  from foo
  4  where group_id <= ( select group_id
  5                     from (
  6                              select f.group_id
  7                                     , row_number() over (order by f.id asc) rn
  8                              from foo f
  9                              )
 10                          where rn = &n )
 11* order by id
Enter value for n: 4
old  10:                         where rn = &n )
new  10:                         where rn = 4 )

        ID   GROUP_ID
---------- ----------
         1        100
         2        100
         3        100
         4        200

SQL> r
  1  select id
  2         , group_id
  3  from foo
  4  where group_id <= ( select group_id
  5                     from (
  6                              select f.group_id
  7                                     , row_number() over (order by f.id asc) rn
  8                              from foo f
  9                              )
 10                          where rn = &n )
 11* order by id
Enter value for n: 5
old  10:                         where rn = &n )
new  10:                         where rn = 5 )

        ID   GROUP_ID
---------- ----------
         1        100
         2        100
         3        100
         4        200
         5        300
         6        300

6 rows selected.

SQL>

If we assume that the group_id's are contiguous and ascending, then @Shannon's answer works perfectly. 如果我们假设group_id是连续的并且是递增的,那么@Shannon的答案将非常有效。 If we do not make that assumption, and we have data that looks like this, for example: 如果我们不做这个假设,并且我们有看起来像这样的数据,例如:

SQL> select * from foo order by id;

ID GROUP_ID
-- --------
 1      100
 2      100
 3      100
 4      200
 6      100
 7      400
 9      500
10      500
11      500
12      600

Then it's a stickier problem. 这是一个棘手的问题。 For example, if N = 3, 4, or 5, then we need to get the rows through ID = 6. For N = 6, we need up to ID = 7. For N = 7, we need through ID = 11. 例如,如果N = 3、4或5,则需要通过ID = 6获取行。对于N = 6,我们需要高达ID =7。对于N = 7,我们需要通过ID = 11。

I believe this query works regardless of the order of group_id: 我相信无论group_id的顺序如何,该查询都有效:

For N = 7: 对于N = 7:

WITH q AS (SELECT ID, group_id
                , row_number() OVER (ORDER BY ID) rn
                , MAX(id) OVER (PARTITION BY group_id) rn2
             FROM foo)
SELECT ID, group_id FROM q
 WHERE ID <= (SELECT max(rn2) FROM q WHERE rn <= :N)
 ORDER BY ID; 

ID GROUP_ID
-- --------
 1      100
 2      100
 3      100
 4      200
 6      100
 7      400
 9      500
10      500
11      500

9 rows selected

For N = 6: 对于N = 6:

ID GROUP_ID
-- --------
 1      100
 2      100
 3      100
 4      200
 6      100
 7      400

For N = 1: 对于N = 1:

ID GROUP_ID
-- --------
 1      100
 2      100
 3      100
 4      200
 6      100

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 SQL:选择列值至少出现N次的行? - SQL: Select rows with a column value that occurs at least N times? ORACLE SQL:根据另一列中的相同键值选择基于一列中不同值的多行 - ORACLE SQL: select multiple rows basd on different values in one column for a same key value in another column Oracle SQL-按列值和数量对分区数据集进行分区,然后从每个分区中检索第一行和最后一行 - Oracle SQL - Partition dataset by column value and quantity, then retrieve first and last rows from each partition Oracle SQL 按分区从不同列返回第一个和最后一个值 - Oracle SQL Return First & Last Value From Different Columns By Partition 从 PHP/SQL 的 7 个选定行中选择第一行和最后一行值 - Select first and last row value from 7 selected rows PHP/SQL 将具有 N-1 个相同列和 1 个不同列的多行合并为一行,保留前 N-1 列并对最后一列求和 - Combine multiple rows with N-1 identical columns and 1 different column into one row, preserving the first N-1 columns and summing the last column SQL选择至少一列不为NULL的所有行 - SQL select all rows where at least one column is not NULL oracle sql选择其他列中值最低的位置 - oracle sql select where value is lowest from a different column 带有“仅FETCH第一n行”的Oracle SQL“列定义不明确” - Oracle SQL “column ambiguously defined” with `FETCH FIRST n ROWS ONLY` SQL:选择具有至少两次出现的列值的行? - SQL: Select rows with a column value that occurs at least two times?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM