Chinaunix首页 | 论坛 | 博客
  • 博客访问: 290677
  • 博文数量: 163
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: -40
  • 用 户 组: 普通用户
  • 注册时间: 2017-03-08 00:28
文章分类

全部博文(163)

文章存档

2015年(2)

2014年(35)

2013年(28)

2012年(30)

2011年(22)

2010年(14)

2009年(8)

2008年(13)

2007年(11)

分类: 数据库开发技术

2011-07-21 15:12:52

一个查询语句的延伸功能

Author: Mars
MSN:    hnynes@gmail.com

注意本文中的所有SQL执行环境为SQL SERVER 2008 R2 SP2,如果在您的环境下执行有问题或是语法错误,请查看文档,作相应的修改.row_numer为SQL 2005才出现的功能,因此呢,至少在sql 2005以上的才能执行下面的语句

今天在一个群里讨论一个小问题如下,(一个例子片断)
n1   n2   n3   n4
1    a    x    5
1    a    x    5
1    b    y    1
2    b    y    4
2    c    y    2
2    d    z    5
3    e    n    2

现在需要取出一个结果集,要求每个n1的值只出现一次,如下
n1   n2   n3   n4
1    b    y    1
2    d    z    5
3    e    n    2

我首先想到的是distinct,但发现如果只是distinct单个字段,记录就无法出来,因此不符合,如果distinct所有字段,这里没有完全重复的记录,因此有distinct与没有distinct的结果都是一样的.
select distinct * from test
select * from test

有人给出了一个解决方案如下
select *
  from (select t.n1,
               t.n2,
               t.n3,
               t.n4,
               row_number() OVER(PARTITION BY t.n1 ORDER BY t.n2 desc) rn
          from test t) t
 where t.rn = 1

这个解决方案OK的.
下面分析下这个解决方案,前面的几个简单的字段,相信大家都能看懂,就不说了,主要看row_number() OVER(PARTITION BY t.n1 ORDER BY t.n2 desc) rn
这里PARTITION BY t.n1是将结果集,按n1分为多个分区,意思就是对于n1中出现的每个相同值,会被划分到不同的分区中,分区结果如下
n1   n2   n3   n4   rn
1    b    y    1    1
1    a    x    5    2
1    a    x    5    3
1    a    y    5    4
2    d    z    5    1
2    c    y    2    2
2    b    y    4    3
3    e    n    2    1

Transact SQL对OVER(PARTION BY...)的说明是 将结果集分为多个分区,开窗函数分别应用于每个分区,并为每个分区重新启动计算

下面是本人对此种方案的一个扩展.
有一个非常经典的问题,就是筛选数据库中的重复记录
下面我就上面的方法,说说我想到的方法,先贴代码


select t.n1,
       t.n2,
       t.n3,
       t.n4,
       row_number() OVER(PARTITION BY t.n1,t.n2,t.n3,t.n4 ORDER BY t.n2 desc) rn
from test t


很简单的方法,上面语句的意义就是对全字段完全一样的记录,会被划分到不同的分区,这样一来,就可以通过取分区数来筛选重复记录了.执行结果如下
n1   n2   n3   n4   rn
1    a    x    5    1
1    a    x    5    2
1    a    y    4    1
1    a    y    5    1
1    b    y    1    1
2    b    y    4    1
2    c    y    2    1
2    d    z    5    1
3    e    n    2    1
这样一来,筛选重复记录,只需要根据rn列来筛选就OK了.呵呵,很简单的实现.

我将整个测试过程的代码贴一下


DECLARE @Temp TABLE
(
    n1 int,
    n2 varchar(10),
    n3 varchar(10),
    n4 int
)

insert into @Temp
values(1,'a','x',5),
(1,'a','x',5),
(1,'a','y',5),
(1,'a','y',4),
(1,'b','y',1),
(2,'b','y',4),
(2,'c','y',2),
(2,'d','z',5),
(3,'e','n',2)

SELECT * FROM @Temp

select t.n1,
       t.n2,
       t.n3,
       t.n4,
       row_number() OVER(PARTITION BY t.n1,t.n2,t.n3,t.n4 ORDER BY t.n2 desc) rn
from @Temp t

select *
  from (select t.n1,
               t.n2,
               t.n3,
               t.n4,
               row_number() OVER(PARTITION BY t.n1 ORDER BY t.n2 desc) rn
          from @Temp t) t
 where t.rn = 1


注意本文中的所有SQL执行环境为SQL SERVER 2008 R2 SP2,如果在您的环境下执行有问题或是语法错误,请查看文档,作相应的修改.row_numer为SQL 2005才出现的功能,因此呢,至少在sql 2005以上的才能执行下面的语句
阅读(764) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~