Fork me on GitHub

丁酉年闰六月初十

  看到这个标题,不要误会,今天是建军节。中国解放军成立了九十周年了,马上一百年了,1927年南昌起义。解放军自70年代后,也没咋打过仗,基本上是刀枪入库,马放南山。

  写sql的时候遇到这个情况,ROW_NUMBER()函数和count()函数的区别,在数据处理中,开窗函数还是会经常用到的,比如说,我想知道这个客户住了几次店,可以用group by直接分组,但是group by查询出来的结果,只有分组依据的列,和聚合函数返回值那一列,这对数据的完整性还是有影响的。所以这时候用开窗函数是挺好的。

 写法一:

  SELECT  *,ROW_NUMBER() OVER(PARTITION BY CustTel1 ORDER BY CreateDate desc) as 次数,
    count(CustTel1) OVER(PARTITION BY CustTel1  ORDER BY CreateDate desc ) as 总次数   from #aaa

    

 写法二:

 SELECT  *,ROW_NUMBER() OVER(PARTITION BY CustTel1 ORDER BY CreateDate desc) as 次数,
                  count(CustTel1) OVER(PARTITION BY CustTel1  ) as 总次数   from #aaa

  

  

   在count()函数中,over中 PARTITION表示根据电话号码进行求总数,之后,再将总数加到电话号码一样的数据上。

  以上两种写法的差别就是根据电话求总数的时候,有没有order by,然后结果就出现了差别了,原因是为啥,我也不知道。🐯

-------------------------------------------8月2日更新--------------------------------------

  昨天就上边两种写法,我们搞明白是为啥会出现这种区别,今天又查别的数据时,也用了这种相似的写法,但是情况和昨天的不一样。我就努努力,研究研究是为啥吧。下面是提供的5条数据,电话都是一样的,标题是两种,而创建时间是5条都不一样。下面我就分别对这三个字段进行开窗函数查询。

  

 1.count()函数对电话开窗:

 SELECT  CardId,CustTel1,Title,CreateDate,ROW_NUMBER() OVER(PARTITION BY CustTel1 ORDER BY CreateDate desc) as 次数,
                                  count(CustTel1) OVER(PARTITION BY CustTel1) as 总次数    from #aaa

  结果:总次数都是5次。

  

 2. count()函数对电话开窗  ,order by CreateDate:

 

  SELECT  CardId,CustTel1,Title,CreateDate,ROW_NUMBER() OVER(PARTITION BY CustTel1 ORDER BY CreateDate desc) as 次数,
                        count(CustTel1) OVER(PARTITION BY CustTel1 order by CreateDate) as 总次数    from #aaa

 结果:order by的时间,因为时间是五个都不相同的,产生了不同的值。

  

3. count()函数对电话开窗 , order by用CustTel1:

 

  SELECT  CardId,CustTel1,Title,CreateDate,ROW_NUMBER() OVER(PARTITION BY CustTel1 ORDER BY CreateDate desc) as 次数,
                         count(CustTel1) OVER(PARTITION BY CustTel1 order by CustTel1) as 总次数    from #aaa
     

 

  结果:order by用CustTel1,CustTel1就一个值,总次数也就产生了一个值

 

 

4. count()函数对电话开窗 , order by用Title:

 

SELECT  CardId,CustTel1,Title,CreateDate,ROW_NUMBER() OVER(PARTITION BY CustTel1 ORDER BY CreateDate desc) as 次数,
                        count(CustTel1) OVER(PARTITION BY CustTel1 order by Title) as 总次数  from #aaa
    

  结果:总数出现了两个值。一个3,一个5。

  

5. count()函数对电话开窗 , order by用Title 倒叙:

  SELECT  CardId,CustTel1,Title,CreateDate,ROW_NUMBER() OVER(PARTITION BY CustTel1 ORDER BY CreateDate desc) as 次数,
                        count(CustTel1) OVER(PARTITION BY CustTel1 order by Title desc) as 总次数    from #aaa
    

结果:到寻之后,总数出现了两个值。一个2,一个5。

  

这个我也不强行总结了,结果就是这样。知道就好,下次再用时,不懵逼就好了。

 

-------------------------------------------------------华丽的分割线-----------------------------------------

  昨天用游标的时候,还遇到一个问题。刚开始想着是游标设置成只读,可后来发现不是这个原因。这个问题搜了好久,原来是游标那个表,没有主键,或者唯一约束键,我就建了个主键id就好了。

  

 

 

参考:http://bbs.csdn.net/topics/380076087

   http://support.microsoft.com/kb/158773/zh-cn

 

 

游标的使用  
 讲了这个多游标的优点,现在我们就亲自来揭开游标的神秘的面纱。  
 使用游标的顺序: 声名游标、打开游标、读取数据、关闭游标、删除游标。  
1.3.1声明游标  
最简单游标声明:DECLARE <游标名>CURSOR FOR<SELECT语句>;  
其中select语句可以是简单查询,也可以是复杂的接连查询和嵌套查询  
例子:[已表2 AddSalary为例子]  
Declare mycursor cursor for select * from AddSalary  
这样我就对表AddSalary申明了一个游标mycursor   
【高级备注】  
DECLARE <游标名> [INSENSITIVE] [SCROLL] CURSORFOR<SELECT语句>  
这里我说一下游标中级应用中的[INSENSITIVE][SCROLL]  
INSENSITIVE  
表明MS SQL SERVER 会将游标定义所选取出来的数据记录存放在一临时表内(建立在tempdb 数据库下)。对该游标的读取操作皆由临时表来应答。因此,对基本表的修改并不影响游标提取的数据,即游标不会随着基本表内容的改变而改变,同时也无法通过游标来更新基本表。如果不使用该保留字,那么对基本表的更新、删除都会反映到游标中。  
另外应该指出,当遇到以下情况发生时,游标将自动设定INSENSITIVE 选项。  
a.在SELECT 语句中使用DISTINCT、 GROUP BY、 HAVING UNION 语句;  
b.使用OUTER JOIN;  
c.所选取的任意表没有索引;  
d.将实数值当作选取的列。  
SCROLL  
表明所有的提取操作(如FIRST、 LAST、 PRIOR、 NEXT、 RELATIVE、 ABSOLUTE)都可用。如果不使用该保留字,那么只能进行NEXT 提取操作。由此可见,SCROLL 极大地增加了提取数据的灵活性,可以随意读取结果集中的任一行数据记录,而不必关闭再  
重开游标。   
1.3.2 打开游标  
非常简单,我们就打开刚才我们声明的游标mycursor  
OPEN mycursor   
1.3.3读取数据  
FETCH [ NEXT | PRIOR | FIRST | LAST] FROM { 游标名  | @游标变量名 } [ INTO @变量名 [,…] ]  
参数说明:  
NEXT   取下一行的数据,并把下一行作为当前行(递增)。由于打开游标后,行指针是指向该游标第1行之前,所以第一次执行FETCH NEXT操作将取得游标集中的第1行数据。NEXT为默认的游标提取选项。  
INTO @变量名[,…]  把提取操作的列数据放到局部变量中。列表中的各个变量从左到右与游标结果集中的相应列相关联。各变量的数据类型必须与相应的结果列的数据类型匹配或是结果列数据类型所支持的隐性转换。变量的数目必须与游标选择列表中的列的数目一致。  
现在我们就取出mycursor游标的数据吧!   
当游标被打开时,行指针将指向该游标集第1行之前,如果要读取游标集中的第1行数据,必须移动行指针使其指向第1行。就本例而言,可以使用下列操作读取第1行数据:  
Eg: Fetch next from mycursor 或则 Fetch first from mycursor  
这样我就取出了游标里的数据,但是光光这样可不够,我们还需要将取出的数据赋给变量  
--声明2个变量  
declare @O_ID NVARCHAR(20)  
declare @A_Salary float  
--将取出的值传入刚才声明的2个变量  
Fetch next from mycursor into @ O_ID,@ A_Salary  
   
1.3.4关闭游标  
CLOSE mycursor     
           
1.3.5删除游标  
DEALLOCATE mycursor           
   
1.3.6 实例训练  
**/  
CREATE PROCEDURE PK_Test  
AS  
    --声明2个变量  
    declare @O_ID nvarchar(20)    
    declare @A_Salary float    
    --声明一个游标mycursor,select语句中参数的个数必须要和从游标取出的变量名相同  
    declare mycursor cursor for select O_ID,A_Salary from AddSalary    
    --打开游标  
    open mycursor      
    --从游标里取出数据赋值到我们刚才声明的2个变量中  
    fetch next from mycursor into @O_ID,@A_Salary      
    --判断游标的状态  
    -- 0 fetch语句成功      
    ---1 fetch语句失败或此行不在结果集中      
    ---2 被提取的行不存在  
    while (@@fetch_status=0)  
    begin      
    --显示出我们每次用游标取出的值  
       print '游标成功取出一条数据'  
       print @O_ID  
       print @A_Salary     
    --用游标去取下一条记录  
       fetch next from mycursor into @O_ID,@A_Salary  
    end  
    --关闭游标  
    close mycursor  
    --撤销游标  
    DEALLOCATE mycursor   
GO  

 

posted @ 2017-08-01 14:10  神雕爱大侠  阅读(301)  评论(0编辑  收藏  举报