48

我有一个由其他人编写的 SQL 查询,我正试图弄清楚它的作用。有人可以解释一下Partition ByandRow_Number关键字在这里的作用,并给出一个简单的例子,以及为什么要使用它?

分区示例:

(SELECT cdt.*,
        ROW_NUMBER ()
        OVER (PARTITION BY cdt.country_code, cdt.account, cdt.currency
              ORDER BY cdt.country_code, cdt.account, cdt.currency)
           seq_no
   FROM CUSTOMER_DETAILS cdt);

我在网上看过一些例子,它们有点太深入了。

提前致谢!

4

4 回答 4

112

PARTITION BY隔离集合,这使您能够独立地在相关集合上工作(ROW_NUMBER()、COUNT()、SUM() 等)。

在您的查询中,相关集由具有相似 cdt.country_code、cdt.account、cdt.currency 的行组成。当您对这些列进行分区并在它们上应用 ROW_NUMBER 时。这些组合/集合上的其他列将从 ROW_NUMBER 接收序列号

但是该查询很有趣,如果您按一些唯一数据进行分区并且您在其上放置了 row_number,它只会产生相同的数字。就像您在保证唯一的分区上执行 ORDER BY 一样。例如,将 GUID 视为cdt.country_code, cdt.account, cdt.currency

newid()产生 GUID,那么你对这个表达式有什么期望呢?

select
   hi,ho,
   row_number() over(partition by newid() order by hi,ho)
from tbl;

...对,所有分区(没有分区,每一行都分区在自己的行中)行的 row_numbers 都设置为 1

基本上,您应该在非唯一列上进行分区。OVER 上的 ORDER BY 需要 PARTITION BY 具有非唯一组合,否则所有 row_numbers 将变为 1

例如,这是您的数据:

create table tbl(hi varchar, ho varchar);

insert into tbl values
('A','X'),
('A','Y'),
('A','Z'),
('B','W'),
('B','W'),
('C','L'),
('C','L');

然后这类似于您的查询:

select
   hi,ho,
   row_number() over(partition by hi,ho order by hi,ho)
from tbl;

那会是什么输出?

HI  HO  COLUMN_2
A   X   1
A   Y   1
A   Z   1
B   W   1
B   W   2
C   L   1
C   L   2

你看到你的HI HO组合了吗?前三行具有唯一的组合,因此它们设置为 1,B 行具有相同的 W,因此 ROW_NUMBERS 不同,HI C 行也是如此。

现在,为什么ORDER BY需要那里?如果以前的开发者只是想在相似的数据上放一个row_number(例如HI B,所有数据都是BW,BW),他可以这样做:

select
   hi,ho,
   row_number() over(partition by hi,ho)
from tbl;

但唉,Oracle(和 Sql Server 也是)不允许分区没有ORDER BY; 而在 Postgresql 中,ORDER BYPARTITION 是可选的:http ://www.sqlfiddle.com/#!1/27821/1

select
   hi,ho,
   row_number() over(partition by hi,ho)
from tbl;

ORDER BY的分区看起来有点多余,不是因为以前的开发人员的错,某些数据库只是不允许PARTITION使用 no ORDER BY,他可能无法找到一个好的候选列进行排序。如果 PARTITION BY 列和 ORDER BY 列都相同,只需删除 ORDER BY,但由于某些数据库不允许这样做,您可以这样做:

SELECT cdt.*,
        ROW_NUMBER ()
        OVER (PARTITION BY cdt.country_code, cdt.account, cdt.currency
              ORDER BY newid())
           seq_no
   FROM CUSTOMER_DETAILS cdt

您找不到用于对相似数据进行排序的好列?您不妨随机排序,分区数据无论如何都具有相同的值。例如,您可以使用 GUID(newid()用于 SQL Server)。所以这与以前的开发人员所做的输出相同,不幸的是某些数据库不允许PARTITION没有ORDER BY

虽然真的,它躲避了我,我找不到一个很好的理由在相同的组合上加上一个数字(BW,BW,在上面的例子中)。它给人的印象是数据库具有冗余数据。不知何故让我想起了这一点:如何从表中的同一记录列表中获取一条唯一记录?表中没有唯一约束

看到 PARTITION BY 具有与 ORDER BY 相同的列组合,看起来真的很神秘,不能轻易推断出代码的意图。

现场测试:http ://www.sqlfiddle.com/#!3/27821/6


但正如 dbaseman 也注意到的那样,在同一列上进行分区和排序是没有用的。

你有一组这样的数据:

create table tbl(hi varchar, ho varchar);

insert into tbl values
('A','X'),
('A','X'),
('A','X'),
('B','Y'),
('B','Y'),
('C','Z'),
('C','Z');

然后你用 hi,ho 分区;然后你订购,嗨,嗬。对类似数据进行编号没有意义:-) http://www.sqlfiddle.com/#!3/29ab8/3

select
   hi,ho,
   row_number() over(partition by hi,ho order by hi,ho) as nr
from tbl;

输出:

HI  HO  ROW_QUERY_A
A   X   1
A   X   2
A   X   3
B   Y   1
B   Y   2
C   Z   1
C   Z   2

看?为什么需要将行号放在相同的组合上?您将在三重 A、X、双 B、Y、双 C、Z 上分析什么?:-)


您只需要在非唯一列上使用 PARTITION ,然后对非唯一列的唯一-ing 列进行排序。示例将使其更清楚:

create table tbl(hi varchar, ho varchar);

insert into tbl values
('A','D'),
('A','E'),
('A','F'),
('B','F'),
('B','E'),
('C','E'),
('C','D');

select
   hi,ho,
   row_number() over(partition by hi order by ho) as nr
from tbl;

PARTITION BY hi对非唯一列进行操作,然后在每个分区列上,您对其唯一列(ho)进行排序,ORDER BY ho

输出:

HI  HO  NR
A   D   1
A   E   2
A   F   3
B   E   1
B   F   2
C   D   1
C   E   2

该数据集更有意义

现场测试:http ://www.sqlfiddle.com/#!3/d0b44/1

这类似于您在 PARTITION BY 和 ORDER BY 上具有相同列的查询:

select
   hi,ho,
   row_number() over(partition by hi,ho order by hi,ho) as nr
from tbl;

这是输出:

HI  HO  NR
A   D   1
A   E   1
A   F   1
B   E   1
B   F   1
C   D   1
C   E   1

看?没有意义?

现场测试:http ://www.sqlfiddle.com/#!3/d0b44/3


最后,这可能是正确的查询:

SELECT cdt.*,
     ROW_NUMBER ()
     OVER (PARTITION BY cdt.country_code, cdt.account -- removed: cdt.currency
           ORDER BY 
               -- removed: cdt.country_code, cdt.account, 
               cdt.currency) -- keep
        seq_no
FROM CUSTOMER_DETAILS cdt
于 2012-05-07T06:30:36.283 回答
11

我经常使用 row_number() 作为从我的选择语句中丢弃重复记录的快速方法。只需添加一个 where 子句。就像是...

select a,b,rn 
  from (select a, b, row_number() over (partition by a,b order by a,b) as rn           
          from table) 
 where rn=1;
于 2015-09-17T18:21:35.993 回答
7

这将选择每个国家代码、帐户和货币的行号。因此,国家代码“US”、账户“XYZ”和货币“$USD”的行将分别获得一个从 1-n 分配的行号;结果集中这些列的所有其他组合也是如此。

这个查询有点好笑,因为order by子句什么都不做。每个分区中的所有行都具有相同的国家代码、帐户和货币,因此按这些列排序没有意义。因此,在此特定查询中分配的最终行号将是不可预测的。

希望有帮助...

于 2012-05-07T05:34:35.293 回答
3

我知道这是一个旧线程,但 PARTITION 相当于 GROUP BY 而不是 ORDER BY。此函数中的 ORDER BY 是 . . . 订购。这只是一种通过添加序列号从冗余中创建唯一性的方法。或者,您可以在引用函数的别名列时通过 WHERE 子句消除其他冗余记录。但是,SELECT 语句中的 DISTINCT 在这方面可能会完成同样的事情。

于 2016-01-13T18:51:43.490 回答