分组后分组合计以及总计SQL语句--收集未整理
2015-10-16 10:39
417 查看
分组后分组合计以及总计SQL语句
1)想一次性得到分组合计以及总计,sql:
SELECT
分组字段FROM
表
GROUPBY
分组字段
computesum(COUNT(*))
=====
2)分组合计1:
SELECTCOUNT(*)
FROM
(SELECT
分组字段FROM
表
GROUPBY
分组字段
)别名
3)分组合计2:
SELECTCOUNT(*)
FROM
(SELECTdistinct
分组字段FROM
表)别名
例子1:分组合计
SELECTJSSKQK_JGHFROMSJ_JSSKQKWHEREJSSKQK_JGHIN(SELECTJSJBXX_JGHFROMSJ_JSJBXXWHEREJSJBXX_JSLXM1=1)GROUPBYJSSKQK_JGHHAVING((SUM(JSSKQK_SSKCXS1)/40)>5)
上面的语句已经可以满足要求分组了.假设执行后有3条记录,怎么才能把这个COUNT值求出?
例子2:[PL/SQL]
如何得到分组后,组中最大日期的纪录
TABLE:A
ABCD
12001/01/0111
12001/12/1222
32002/01/0133
32003/12/1244
按列A分组,请问如何得到每组中时间最大的数据?
12001/12/1222
32003/12/1244
我的笨方法:
SELECT*
FROMA
WHERE(A,B)IN(
SELECTA,MAX(B)
FROMA
GROUPBYA
)
有更好的方法吗?
1,select*fromaout
whereb=(selectmax(b)fromain
wherein.a=out.a)
2,Select*from
(selecta,row_number()over(partitionbya
orderbybdesc)rn
froma)
wherern=1
3,Selecta,b,c,dfrom
(selecta,b,c,d,row_number()over(partitionbya
orderbybdesc)rn
froma)
wherern=1
4,selectA,B,C,Dfromtest
whererowidin
(
selectrdfrom
(
selectrowidrd,rank()over(partionAorderbyBdesc)rkfromtest
)whererk=1
)
)
数据库
首先查询Employees表
查询结果:
city列里面只有5个城市
使用ROW_NUMBER()OVER(PARTITIONBYCOL1ORDERBYCOL2)
先进行分组
注:根据COL1分组,在分组内部根据
COL2排序,而此函数计算的值就表示每组内部排序后的顺序编号(组内连续的唯一的).
sql语句为:
selectEmployeeID,LastName,FirstName,Title,TitleOfCourtesy,City,ROW_NUMBER()over(partitionbyCityorderbyEmployeeID)asnew_index
fromEmployees
执行结果图:
可以看到是按照City分组,EmployeeID排序。
select出分组中的第一条记录
执行语句:
select*from
(selectEmployeeID,LastName,FirstName,Title,TitleOfCourtesy,City,ROW_NUMBER()over(partitionbyCityorderbyEmployeeID)asnew_index
fromEmployees)awherea.new_index=1
执行结果图:
例子4:sql
Ericred20
ericblue30
andyred10
andyblue5
例如,只获取黑体的记录。
2,SQL2005以上版本
select*from(select*,row=row_number()over(partitionbyColororderbyColor)fromtable1)twhererow=1andcolor='xx'--加上條件
SQL2000用top1
例子5:一条SQL语句搞定分组并且每组限定记录集的数量
如果我想得到这样一个结果集:分组,并且每组限定记录集的数量,用一条SQL语句能办到吗?
比如说,我想找出学生期末考试中,每科的前3名,只用一条SQL语句,该怎么写?
表[TScore]的结构
code学号char
subject科目int
score成绩int
可以这样写:
SELECT
例子7:分组后取第一条记录的SQL语句
有如下表结构:
字段A,B,C
值为a1,b1,c1
a2,b2,c2
a2,b3,c3
a3,b4,c4
a3,b5,c5
想要得到的结果集以A字段为分组条件,并取出每一个分组中的第一条记录,如下:
A,B,C
值为a1,b1,c1--a1分组的第一条记录。
a2,b2,c2--a2分组的第一条记录。
a3,b4,c4--a3分组的第一条记录。
select*from表temwherec=(selecttop1cfrom
表wherea=tem.a)
现有数据表call如下:
zjthbj
----------------------------------
031066666660000103101111111
1371111111100001031122222222
03108898888950000
031177778777950000
03115595555500001031187888876
注:th如为950000,则bj为空,th如为00001,则bj不是空。
1、bj分组
selectsubstr(bj,1,4)as区号,count(*)as
呼叫总量fromcall
groupbysubstr(bj,1,4);
执行结果
区号
呼叫总量
--------------------------
03101
03112
2
2、zj分组,条件是th为950000的记录
selectsubstr(zj,1,4)as区号,count(*)as
呼叫总量fromcall
whereth=950000
groupbysubstr(zj,1,4);
执行结果:
区号
呼叫总量
--------------------------
03101
03111
能否有一个语句就能实现如下结果:
区号
呼叫总量
--------------------------
03102
03113
注:想要得到结果是1对应的行加2对应的行。
union起来再求和
select区号,sum(呼叫总量)from
(selectsubstr(bj,1,4)as区号,count(*)as
呼叫总量fromcall
groupbysubstr(bj,1,4))
unionall
(selectsubstr(zj,1,4)as区号,count(*)as
呼叫总量fromcall
whereth=950000
groupbysubstr(zj,1,4))
groupby区号;
这个应该在oracle中运行
select
decode(th,'950000',substr(zj,1,4),substr(bj,1,4))as
区号,
count(*)as呼叫总量
from
call
groupby
decode(th'950000',substr(zj,1,4),substr(bj,1,4))
decode(条件,值1,翻译值1,值2,翻译值2,...值n,翻译值n,缺省值)
该函数的含义如下:
IF条件=值1THEN
RETURN(翻译值1)
ELSIF条件=值2THEN
RETURN(翻译值2)
......
ELSIF条件=值nTHEN
RETURN(翻译值n)
ELSE
RETURN(缺省值)
ENDIF
例子8:在SQL
假设有一个表,SQL语句如下:
CREATETABLE[dbo].[scan](
[km][int]NULL,
[kh][int]NULL,
[cj][int]NULL
)ON[PRIMARY]
其中km为科目号、kh为考生号、cj为成绩,现对km和kh进行分组,并获得每组前2条记录(按cj从高到低排序)。基本思想是为每组加一个序号列,再用where取序号小于等于2的。SQL语句如下:
select*from
(
selecta.km,a.kh,cj,row_number()over(partitionbya.kmorderbya.km,a.cjdesc)n
from
(selectkm,kh,SUM(cj)cjfromscangroupbykm,kh)a
)bwheren<=2
orderbykm,cjdesc
最后得到的结果集如下图所示。
例子9:
在表A中根据字段B分组、根据字段C排序并查询出每组中的前三条记录,查询结果要求包含所有字段,请问sql语句该怎么写?下面的sql语句虽然可以实现,但由于数据量比较大,耗费时间太长,有没有不通过表联接而直接分组取记录的方法呢?多谢!
select*
from表Aast1
where主键in(
selecttop3主键
from表Aast2
wheret1.B=t2.B
orderbyt2.C)
注释
(隐藏注释)
答案1
作者:邹建
selectid=identity(int,1,1),b,
主键into#from
表AorderbyB,C
selecta.*
from表Aa,#b,(selectid1=min(id),id2=min(id)+2from#groupbyb)c
wherea.主键=b.主键
andb.idbetweenc.id1andc.id2
droptable#
Ericwoo
发表于
四月
11,200709:50
答案2
作者:aierong
求每组前2名,你有几种方法?(MS
SQL2000)
createtableabc(
invarchar(10),
iiint,
iiiint,
iiiiint,
pricemoney)
go
insertintoabc
select'b',1,2,1,11
unionall
select'b',211,2,1,211
unionall
select'a',21,2,1,311
unionall
select'd',41,42,1,411
unionall
select'd',41,42,1,511
unionall
select'd',41,42,1,611
unionall
select'e',1,2,1,11
unionall
select'e',71,2,1,31
unionall
select'e',61,2,1,911
unionall
select'e',771,2,1,1
go
要求的结果是:
以i分组,求每组price最大的前2条记录
iiiiiiiiiiprice
----------------------------------------------------------------
a2121311.0000
b12111.0000
b21121211.0000
d41421511.0000
d41421611.0000
e712131.0000
e6121911.0000
1.
select*
fromabca
where(
selectcountfromabcb
wherea.i=b.iandb.price>a.price)<2
orderbyi,price
连接查询,判断数量
2.
selecti,ii,iii,iiii,price
from(
select(selectisnull(sum(1),0)+1fromabcbwherea.i=b.ianda.price<b.price)ids,*
fromabca)tem
whereids<3
orderbyi,price
生产一个内部表,给内部表tem中的每一组排序,并把排序号放入新列ids中
3.
declare@looptimeint
declare@countint
declare@invarchar(10)
/定义表变量@abc,和表ABC中的所有列类型相同/
declare@abctable(
invarchar(10),
iiint,
iiiint,
iiiiint,
pricemoney)
declare@temtable(
idsintidentity,
classnvarchar(10))
/把表ABC中的所有组全部查询出来,暂时存在表变量@tem中/
insertinto@tem(class)
selecti
fromabc
groupbyi
/求出表变量@tem中行数量/
select@count=@@rowcount
/循环变量@looptime赋初值=1/
select@looptime=1
while(@looptime<=@count)
begin
/将每组名赋值到变量@i/
select@i=class
from@tem
whereids=@looptime
/将每组前2名插入到表变量@abc中/
insertinto@abc
selecttop2*
fromabc
wherei=@i
orderbypricedesc
/循环变量@looptime累加1/
select@looptime=@looptime+1
end
/显示结果/
select*
from@abc
orderbyi,price
4.
用游标来处理
方法和我的第3种方法类似,大家可以自己试试
我共写了4种,不知道大家还有什么其他好方法,可以交流,谢谢。
Ericwoo
发表于
四月
11,200709:52
今天用到了,利用此方法可以解决一个删除重复记录的问题
当然表必须带有唯一索引,仔细看以下代码
DeleteFromdbo.TB_WorkflowTaska
WHEREItemIDNotin(selecttop1ItemIDfromTB_WorkflowTaskwhereTaskName=a.TaskNameAndEmpID=a.EmpIDAndBillTypeID=a.BillTypeIDAndBillID=a.BillIDAndStatus=a.StatusANDWFStatus=a.WFStatus)
注意:只能用In
或Notin
,不能用Exists 或
NotExists ,至于为什么,大家思考一下?
重新整理一下格式:
现有一表Log:DayInOutCurrent
2012.4.510010
2012.4.5055
2012.4.6302015
2012.4.60312
………………………………………………
希望显示为
、
2012.4.51055
2012.4.6302312
SQLcode
withtb
as(
select
[day],sum([in])
as
[in],sum(out)
asout,sum([in])-sum(out)
as
[current],rank()
over(
order
by
[day])
asrow
from
[log]
group
by
[day]
)
select
[day],[in],out,(select
sum([current])
fromtbb
whereb.row<=a.row)[current]
fromtba
SQLcode
2012.4.5
10
5
5
2012.4.6
30
23
12
SQLcode
-->
测试数据:[Log]
if
object_id('[Log]')
is
not
null
drop
table
[Log]
create
table
[Log]([Day]
date,[In]
int,[Out]
int,[Current]
int)
insert
[Log]
select
'2012.4.5',10,0,10
union
all
select
'2012.4.5',0,5,5
union
all
select
'2012.4.6',30,20,15
union
all
select
'2012.4.6',0,3,12
select
[Day],sum([In])
[In],sum([Out])
[Out],min([Current])
as
[Current]
from
[Log]
group
by
[Day]
/*
Day
InOutCurrent
2012-04-05
1055
2012-04-06
302312
*/
http://wenku.baidu.com/view/c1df001352d380eb62946db3.html
例子12:
以
有意义的方式安排数据可能是一种挑战。有时您只需进行简单分类。通常您必须进行更多处理——进行分组以利于分析与总计。可喜的是,SQL提供了大量用于分
类、分组和总计的子句及运算符。下面的建议将有助于您了解何时进行分类、何时分组、何时及如何进行总计。欲了解每个子句和运算符的详细信息,请查看
。
#1:分类排序
通常,我们确实需要对所有数据进行排序。SQL的ORDERBY子句将数据按字母或数字顺序进行排列。因此,同类数据明显分类到各个组中。然而,这些组只是分类的结果,它们并不是真正的组。ORDERBY显示每一个记录,而一个组可能代表多个记录。
#2:减少组中的相似数据
分类与分组的最大不同在于:分类数据显示(任何限定标准内的)所有记录,而分组数据不显示这些记录。GROUPBY子句减少一个记录中的相似数据。例如,GROUPBY能够从重复那些值的源文件中返回一个唯一的邮政编码列表:
SELECTZIP
FROMCustomers
GROUPBYZIP
仅包括那些在GROUPBY和SELECT列列表中字义组的列。换句话说,SELECT列表必须与GROUP列表相匹配。只有一种情况例外:SELECT列表能够包含聚合函数。(而GROUP
BY不支持聚合函数。)
记住,GROUPBY不会对作为结果产生的组分类。要对组按字母或数字顺序排序,增加一个ORDERBY子句(#1)。另外,在GROUP
BY子句中您不能引用一个有别名的域。组列必须在根本数据中,但它们不必出现在结果中。
#3:分组前限定数据
您可以增加一个WHERE子句限定由GROUPBY分组的数据。例如,下面的语句仅返回肯塔基地区顾客的邮政编码列表。
SELECTZIP
FROMCustomers
WHEREState='KY'
GROUPBYZIP
在GROUPBY子句求数据的值之前,WHERE对数据进行过滤,记住这一点很重要。
和GROUPBY一样,WHERE不支持聚合函数。
#4:返回所有组
当
您用WHERE过滤数据时,得到的组只显示那些您指定的记录。符合组定义但不满足子句条件的数据将不会出现在组中。不管WHERE条件如何,如果您想包括
所有数据,增加一个ALL子句。例如,在前面的语句中增加一个ALL子句会返回所有邮政编码组,而不仅仅是肯塔基地区的组。
SELECTZIP
FROMCustomers
WHEREState='KY'
GROUPBYALLZIP
照这个样子,这两个子句会造成冲突,您可能不会以这种方式使用ALL子句。当您用聚合求一个列的值时,应用ALL子句很方便。例如,下面的语句计算每个肯塔基邮政编码的顾客数目,同时显示其它邮政编码值。
SELECTZIP,Count(ZIP)ASKYCustomersByZIP
FROMCustomers
WHEREState='KY'
GROUPBYALLZIP
得到的组由根本数据中的所有邮政编码值构成。但是,聚合列(KYCustomerByZIP)显示为0,因为除肯塔基邮政编码组外没有别的组。
远程查询不支持GROUPBYALL。
#5:分组后限定数据
WHERE子句(#3)在GROUPBY子句之前求数据的值。当您希望在分组以后限定数据时,使用HAVING。通常,不管您使用WHERE还是HAVING,得到的结果相同。但要记住,这
两个子句不能互换,这点很重要。如果您存在疑问,这里有一条应用指南:过滤记录时使用WHERE;过滤组时使用HAVING。
一般,您会用HAVING,利用聚合来求一个组的值。例如,下面的语句返回一个邮政编码列表,但这个表内可能不包含根本数据源中的每个邮政编码:
SELECTZIP,Count(ZIP)ASCustomersByZIP
FROMCustomers
GROUPBYZIP
HAVINGCount(ZIP)=1
仅仅那些只有一名顾客的组出现在结果中。
#6:详细了解WHERE和HAVING
如果您仍然对WHERE和HAVING的用法感到迷惑,应用下面的指导方法:
WHERE出现在GROUPBY之前;SQL在它分组记录前求WHERE子句的值。
HAVING出现在GROUPBY之后;SQL在它分组记录后求HAVING子句的值。
#7:用聚合总计分组值
分组数据有助于对数据进行分析,但有时您还需要组本身以外的其它信息。您可以增加一个聚合函数来总计分组数据。例如,下面的语句为每次排序显示一个小计:
SELECTOrderID,Sum(Cost*Quantity)ASOrderTotal
FROMOrders
GROUPBYOrderID
与其它的组一样,SELECT和GROUPBY列表必须相匹配。在SELECT子句中包含一个聚合是这一规则的唯一例外。
#8:总计聚合
您可以通过显示每个组的小计进一步总计数据。SQL的ROLLUP运算符为每个组显示一个额外的记录,一个小计。那个记录是用聚合函数在每个组中求所有记录的值的结果。下面的语句为每个组合计OrderTotal列。
SELECTCustomer,OrderNumber,Sum(Cost*Quantity)ASOrderTotal
FROMOrders
GROUPBYCustomer,OrderNumber
WITHROLLUP
一个包含20和25这两个OrderTotal值的组的ROLLUP行将显示OrderTotal值45。ROLLUP结果的第一个值是唯一的,因为它求所有组记录的值。那个值是整个记录集的总和。
ROLLUP不支持聚合函数中的DISTINCT或GROUPBYALL子句。
#9:总计每一列
CUBE运算符比ROLLUP更进一步,它返回每个组中每个值的总数。得到的结果与ROLLUP相似,但CUBE包括组中每一列的一个额外记录。下面的语句显示每个组的小计和每名顾客的一个额外总数。
SELECTCustomer,OrderNumber,Sum(Cost*Quantity)ASOrderTotal
FROMOrders
GROUPBYCustomer,OrderNumber
WITHCUBE
用CUBE得到的总计最为复杂。不仅完成聚合与ROLLUP的工作,而且还求定义组的其它列的值。也就是说,CUBE总计每一个可能的列组合。
CUBE不支持GROUPBYALL。
#10:给总计排序
当CUBE的结果杂乱无章时(一般都是这样),可以增加一个GROUPING函数,如下所示:
SELECTGROUPING(Customer),OrderNumber,Sum(Cost*Quantity)ASOrderTotal
FROMOrders
GROUPBYCustomer,OrderNumber
WITHCUBE
其结果包括每一行的两个额外的值。
值1表明左边的值是一个总计值——ROLLUP或CUBE的运算符的结果。
值0表明左边的值是一个原始GROUPBY子句产生的详细记录。
在分组查询中还可以配合使用HAVING子句,定义查询条件。
使用groupby进行分组查询
在使用groupby关键字时,在select列表中可以指定的项目是有限制的,select语句中仅许以下几项:
〉被分组的列
〉为每个分组返回一个值得表达式,例如用一个列名作为参数的聚合函数
groupby有一个原则,就是select后面的所有列中,没有使用聚合函数的列,必须出现在groupby后面(重要)
groupby实例
实例一
数据表:
姓名科目分数
张三语文80
张三数学98
张三英语65
李四语文70
李四数学80
李四英语90
期望查询结果:
姓名语文数学英语
张三809865
李四708090
代码
SQL代码复制
实例二
有如下数据:(为了看得更清楚,我并没有使用国家代码,而是直接用国家名作为PrimaryKey)
根据这个国家人口数据,统计亚洲和北美洲的人口数量。应该得到下面这个结果。
代码
SQL代码复制
同样的,我们也可以用这个方法来判断工资的等级,并统计每一等级的人数。SQL代码如下;
SQL代码复制
对于groupby后面一般都是跟一个列名,但在该例子中通过case语句使分组变得跟强大了。
实例三
有如下数据
按照国家和性别进行分组,得出结果如下
代码
SQL代码复制
GROUPBY子句中的NULL值处理
当GROUPBY子句中用于分组的列中出现NULL值时,将如何分组呢?SQL中,NULL不等于NULL(在WHERE子句中有过介绍)。然而,在GROUPBY子句中,却将所有的NULL值分在同一组,即认为它们是“相等”的。
HAVING子句
GROUPBY子句分组,只是简单地依据所选列的数据进行分组,将该列具有相同值的行划为一组。而实际应用中,往往还需要删除那些不能满足条件的行组,为了实现这个功能,SQL提供了HAVING子句。语法如下。
SELECTcolumn,SUM(column)
FROMtable
GROUPBYcolumn
HAVINGSUM(column)conditionvalue
说明:HAVING通常与GROUPBY子句同时使用。当然,语法中的SUM()函数也可以是其他任何聚合函数。DBMS将HAVING子句中的搜索条件应用于GROUPBY子句产生的行组,如果行组不满足搜索条件,就将其从结果表中删除。
HAVING子句的应用
从TEACHER表中查询至少有两位教师的系及教师人数。
实现代码:
SQL代码复制
HAVING子句与WHERE子句的区别
HAVING子句和WHERE子句的相似之处在于,它也定义搜索条件。但与WHERE子句不同,HAVING子句与组有关,而不是与单个的行有关。
1、如果指定了GROUPBY子句,那么HAVING子句定义的搜索条件将作用于这个GROUPBY子句创建的那些组。
2、如果指定WHERE子句,而没有指定GROUPBY子句,那么HAVING子句定义的搜索条件将作用于WHERE子句的输出,并把这个输出看作是一个组。
3、如果既没有指定GROUPBY子句也没有指定WHERE子句,那么HAVING子句定义的搜索条件将作用于FROM子句的输出,并把这个输出看作是一个组。
4、在SELECT语句中,WHERE和HAVING子句的执行顺序不同。在本书的5.1.2节介绍的SELECT语句的执行步骤可知,WHERE子句只能接收来自FROM子句的输入,而HAVING子句则可以接收来自GROUPBY子句、WHERE子句和FROM子句的输入。
分组后分组合计以及总计SQL语句
1)想一次性得到分组合计以及总计,sql:
SELECT
分组字段FROM
表
GROUPBY
分组字段
computesum(COUNT(*))
=====
2)分组合计1:
SELECTCOUNT(*)
FROM
(SELECT
分组字段FROM
表
GROUPBY
分组字段
)别名
3)分组合计2:
SELECTCOUNT(*)
FROM
(SELECTdistinct
分组字段FROM
表)别名
4)统计分组后的种类数:
例子1:分组合计
SELECTJSSKQK_JGHFROMSJ_JSSKQKWHEREJSSKQK_JGHIN(SELECTJSJBXX_JGHFROMSJ_JSJBXXWHEREJSJBXX_JSLXM1=1)GROUPBYJSSKQK_JGHHAVING((SUM(JSSKQK_SSKCXS1)/40)>5)
上面的语句已经可以满足要求分组了.假设执行后有3条记录,怎么才能把这个COUNT值求出?
selectcount(*)from
(
SELECTJSSKQK_JGH
FROMSJ_JSSKQK
WHEREJSSKQK_JGHIN(SELECTJSJBXX_JGHFROMSJ_JSJBXXWHEREJSJBXX_JSLXM1=1)
GROUPBYJSSKQK_JGHHAVING((SUM(JSSKQK_SSKCXS1)/40)>5)
)t
例子2:
TABLE:A
ABCD
12001/01/0111
12001/12/1222
32002/01/0133
32003/12/1244
按列A分组,请问如何得到每组中时间最大的数据?
12001/12/1222
32003/12/1244
我的笨方法:
SELECT*
FROMA
WHERE(A,B)IN(
SELECTA,MAX(B)
FROMA
GROUPBYA
)
有更好的方法吗?
1,select*fromaout
whereb=(selectmax(b)fromain
wherein.a=out.a)
2,Select*from
(selecta,row_number()over(partitionbya
orderbybdesc)rn
froma)
wherern=1
3,Selecta,b,c,dfrom
(selecta,b,c,d,row_number()over(partitionbya
orderbybdesc)rn
froma)
wherern=1
4,selectA,B,C,Dfromtest
whererowidin
(
selectrdfrom
(
selectrowidrd,rank()over(partionAorderbyBdesc)rkfromtest
)whererk=1
)
)
例子3:SQL语句分组获取记录的第一条数据的方法
使用Northwind数据库
首先查询Employees表
查询结果:
city列里面只有5个城市
使用ROW_NUMBER()OVER(PARTITIONBYCOL1ORDERBYCOL2)
先进行分组
注:根据COL1分组,在分组内部根据
COL2排序,而此函数计算的值就表示每组内部排序后的顺序编号(组内连续的唯一的).
sql语句为:
selectEmployeeID,LastName,FirstName,Title,TitleOfCourtesy,City,ROW_NUMBER()over(partitionbyCityorderbyEmployeeID)asnew_index
fromEmployees
执行结果图:
可以看到是按照City分组,EmployeeID排序。
select出分组中的第一条记录
执行语句:
select*from
(selectEmployeeID,LastName,FirstName,Title,TitleOfCourtesy,City,ROW_NUMBER()over(partitionbyCityorderbyEmployeeID)asnew_index
fromEmployees)awherea.new_index=1
执行结果图:
例子4:sql
获取分组结果后,如何每一组的第一条记录
Ericred20ericblue30
andyred10
andyblue5
例如,只获取黑体的记录。
1,declare@fTabletable(fNamevarchar(10),fColorvarchar(10),fOrderint)
insertinto@fTablevalues('Eric','red',20)
insertinto@fTablevalues('eric','blue',30)
insertinto@fTablevalues('andy','red',10)
insertinto@fTablevalues('andy','blue',5)
--只获取红色
select*from@fTablewherefColor='red'
--每个fColor取一条记录(按fOrder正序)
select*from@fTableAwherefName=(selecttop1fNamefrom@fTablewherefColor=A.fColororderbyfOrder)
--每个fColor取一条记录(按fOrder反序)
select*from@fTableAwherefName=(selecttop1fNamefrom@fTablewherefColor=A.fColororderbyfOrderdesc)
2,SQL2005以上版本
select*from(select*,row=row_number()over(partitionbyColororderbyColor)fromtable1)twhererow=1andcolor='xx'--加上條件
SQL2000用top1
例子5:一条SQL语句搞定分组并且每组限定记录集的数量
如果我想得到这样一个结果集:分组,并且每组限定记录集的数量,用一条SQL语句能办到吗?比如说,我想找出学生期末考试中,每科的前3名,只用一条SQL语句,该怎么写?
表[TScore]的结构
code学号char
subject科目int
score成绩int
可以这样写:
SELECT
,[subject]
,[score]
FROM(
SELECT*
,RANK()OVER(PARTITIONBYsubjectORDERBYscoreDESC)AS
Row
FROMTScore
)ASa
WHERERow<=3;例子6:SQL获取每个分组的第一条记录
SQL查询以下伪数据获取粗体字行的记录
ID,Name,ItemID,Price,CreatedOn
1a110.00xxx1
2a112.00xxx2
3b19.00xxx1
4b111.50xxx2
5c120.00xxx1
6a221.00xxx1
7a223.00xxx2
8b235.00xxx1
9c231.00xxx1
10c230.50xxx2
获取每个分组中的第一条记录,当ItemID有多条记录时,选取Price最高的
[code]--sql2000
select*
fromtbnamek
wherenotexists(select*fromtbnamewhere
name=k.nameandITemID=K.ITemIDandk.price<price
)
--sql2005
selectID,Name,ItemID,Price,CreatedOnfrom(select*,rn=ROW_NUMBER()over(PARTITIONbyname,ITemIDorderbypricedesc)fromtb)kwherek.rn=1
例子7:分组后取第一条记录的SQL语句
有如下表结构:字段A,B,C
值为a1,b1,c1
a2,b2,c2
a2,b3,c3
a3,b4,c4
a3,b5,c5
想要得到的结果集以A字段为分组条件,并取出每一个分组中的第一条记录,如下:
A,B,C
值为a1,b1,c1--a1分组的第一条记录。
a2,b2,c2--a2分组的第一条记录。
a3,b4,c4--a3分组的第一条记录。
select*from表temwherec=(selecttop1cfrom
表wherea=tem.a)
现有数据表call如下:
zjthbj
----------------------------------
031066666660000103101111111
1371111111100001031122222222
03108898888950000
031177778777950000
03115595555500001031187888876
注:th如为950000,则bj为空,th如为00001,则bj不是空。
1、bj分组
selectsubstr(bj,1,4)as区号,count(*)as
呼叫总量fromcall
groupbysubstr(bj,1,4);
执行结果
区号
呼叫总量
--------------------------
03101
03112
2
2、zj分组,条件是th为950000的记录
selectsubstr(zj,1,4)as区号,count(*)as
呼叫总量fromcall
whereth=950000
groupbysubstr(zj,1,4);
执行结果:
区号
呼叫总量
--------------------------
03101
03111
能否有一个语句就能实现如下结果:
区号
呼叫总量
--------------------------
03102
03113
注:想要得到结果是1对应的行加2对应的行。
union起来再求和
select区号,sum(呼叫总量)from
(selectsubstr(bj,1,4)as区号,count(*)as
呼叫总量fromcall
groupbysubstr(bj,1,4))
unionall
(selectsubstr(zj,1,4)as区号,count(*)as
呼叫总量fromcall
whereth=950000
groupbysubstr(zj,1,4))
groupby区号;
这个应该在oracle中运行
select
decode(th,'950000',substr(zj,1,4),substr(bj,1,4))as
区号,
count(*)as呼叫总量
from
call
groupby
decode(th'950000',substr(zj,1,4),substr(bj,1,4))
decode(条件,值1,翻译值1,值2,翻译值2,...值n,翻译值n,缺省值)
该函数的含义如下:
IF条件=值1THEN
RETURN(翻译值1)
ELSIF条件=值2THEN
RETURN(翻译值2)
......
ELSIF条件=值nTHEN
RETURN(翻译值n)
ELSE
RETURN(缺省值)
ENDIF
例子8:在SQL
Server2005/2008中对记录进行分组,并获得每组前N条记录
假设有一个表,SQL语句如下:CREATETABLE[dbo].[scan](
[km][int]NULL,
[kh][int]NULL,
[cj][int]NULL
)ON[PRIMARY]
其中km为科目号、kh为考生号、cj为成绩,现对km和kh进行分组,并获得每组前2条记录(按cj从高到低排序)。基本思想是为每组加一个序号列,再用where取序号小于等于2的。SQL语句如下:
select*from
(
selecta.km,a.kh,cj,row_number()over(partitionbya.kmorderbya.km,a.cjdesc)n
from
(selectkm,kh,SUM(cj)cjfromscangroupbykm,kh)a
)bwheren<=2
orderbykm,cjdesc
最后得到的结果集如下图所示。
例子9:如何实现分组Group取前N条记录的sql语句
在表A中根据字段B分组、根据字段C排序并查询出每组中的前三条记录,查询结果要求包含所有字段,请问sql语句该怎么写?下面的sql语句虽然可以实现,但由于数据量比较大,耗费时间太长,有没有不通过表联接而直接分组取记录的方法呢?多谢!select*
from表Aast1
where主键in(
selecttop3主键
from表Aast2
wheret1.B=t2.B
orderbyt2.C)
注释
(
答案1
作者:邹建
selectid=identity(int,1,1),b,
主键into#from
表AorderbyB,C
selecta.*
from表Aa,#b,(selectid1=min(id),id2=min(id)+2from#groupbyb)c
wherea.主键=b.主键
andb.idbetweenc.id1andc.id2
droptable#
发表于
四月
11,200709:50
答案2
作者:aierong
求每组前2名,你有几种方法?(MS
SQL2000)
createtableabc(
invarchar(10),
iiint,
iiiint,
iiiiint,
pricemoney)
go
insertintoabc
select'b',1,2,1,11
unionall
select'b',211,2,1,211
unionall
select'a',21,2,1,311
unionall
select'd',41,42,1,411
unionall
select'd',41,42,1,511
unionall
select'd',41,42,1,611
unionall
select'e',1,2,1,11
unionall
select'e',71,2,1,31
unionall
select'e',61,2,1,911
unionall
select'e',771,2,1,1
go
要求的结果是:
以i分组,求每组price最大的前2条记录
iiiiiiiiiiprice
----------------------------------------------------------------
a2121311.0000
b12111.0000
b21121211.0000
d41421511.0000
d41421611.0000
e712131.0000
e6121911.0000
1.
select*
fromabca
where(
selectcountfromabcb
wherea.i=b.iandb.price>a.price)<2
orderbyi,price
连接查询,判断数量
2.
selecti,ii,iii,iiii,price
from(
select(selectisnull(sum(1),0)+1fromabcbwherea.i=b.ianda.price<b.price)ids,*
fromabca)tem
whereids<3
orderbyi,price
生产一个内部表,给内部表tem中的每一组排序,并把排序号放入新列ids中
3.
declare@looptimeint
declare@countint
declare@invarchar(10)
/定义表变量@abc,和表ABC中的所有列类型相同/
declare@abctable(
invarchar(10),
iiint,
iiiint,
iiiiint,
pricemoney)
declare@temtable(
idsintidentity,
classnvarchar(10))
/把表ABC中的所有组全部查询出来,暂时存在表变量@tem中/
insertinto@tem(class)
selecti
fromabc
groupbyi
/求出表变量@tem中行数量/
select@count=@@rowcount
/循环变量@looptime赋初值=1/
select@looptime=1
while(@looptime<=@count)
begin
/将每组名赋值到变量@i/
select@i=class
from@tem
whereids=@looptime
/将每组前2名插入到表变量@abc中/
insertinto@abc
selecttop2*
fromabc
wherei=@i
orderbypricedesc
/循环变量@looptime累加1/
select@looptime=@looptime+1
end
/显示结果/
select*
from@abc
orderbyi,price
4.
用游标来处理
方法和我的第3种方法类似,大家可以自己试试
我共写了4种,不知道大家还有什么其他好方法,可以交流,谢谢。
发表于
四月
11,200709:52
今天用到了,利用此方法可以解决一个删除重复记录的问题
当然表必须带有唯一索引,仔细看以下代码
DeleteFromdbo.TB_WorkflowTaska
WHEREItemIDNotin(selecttop1ItemIDfromTB_WorkflowTaskwhereTaskName=a.TaskNameAndEmpID=a.EmpIDAndBillTypeID=a.BillTypeIDAndBillID=a.BillIDAndStatus=a.StatusANDWFStatus=a.WFStatus)
注意:只能用In
或Notin
,不能用Exists 或
NotExists ,至于为什么,大家思考一下?
例子10:如何取得分组后最后一条记录的值?
还是很混乱,再排一下:重新整理一下格式:
现有一表Log:DayInOutCurrent
2012.4.510010
2012.4.5055
2012.4.6302015
2012.4.60312
………………………………………………
希望显示为
、
2012.4.51055
2012.4.6302312
SQLcode
withtb
as(
select
[day],sum([in])
as
[in],sum(out)
asout,sum([in])-sum(out)
as
[current],rank()
over(
order
by
[day])
asrow
from
[log]
group
by
[day]
)
select
[day],[in],out,(select
sum([current])
fromtbb
whereb.row<=a.row)[current]
fromtba
SQLcode
2012.4.5
10
5
5
2012.4.6
30
23
12
SQLcode
-->
测试数据:[Log]
if
object_id('[Log]')
is
not
null
drop
table
[Log]
create
table
[Log]([Day]
date,[In]
int,[Out]
int,[Current]
int)
insert
[Log]
select
'2012.4.5',10,0,10
union
all
select
'2012.4.5',0,5,5
union
all
select
'2012.4.6',30,20,15
union
all
select
'2012.4.6',0,3,12
select
[Day],sum([In])
[In],sum([Out])
[Out],min([Current])
as
[Current]
from
[Log]
group
by
[Day]
/*
Day
InOutCurrent
2012-04-05
1055
2012-04-06
302312
*/
例子11:sql分组后二次汇总
例子12:
sql的分类与分组统计
您需要了解如何使用某些SQL子句和运算符来安排SQL数据,从而对它进行高效分析。下面这些建议告诉您如何建立语句,获得您希望的结果。以
有意义的方式安排数据可能是一种挑战。有时您只需进行简单分类。通常您必须进行更多处理——进行分组以利于分析与总计。可喜的是,SQL提供了大量用于分
类、分组和总计的子句及运算符。下面的建议将有助于您了解何时进行分类、何时分组、何时及如何进行总计。欲了解每个子句和运算符的详细信息,请查看
。
#1:分类排序
通常,我们确实需要对所有数据进行排序。SQL的ORDERBY子句将数据按字母或数字顺序进行排列。因此,同类数据明显分类到各个组中。然而,这些组只是分类的结果,它们并不是真正的组。ORDERBY显示每一个记录,而一个组可能代表多个记录。
#2:减少组中的相似数据
分类与分组的最大不同在于:分类数据显示(任何限定标准内的)所有记录,而分组数据不显示这些记录。GROUPBY子句减少一个记录中的相似数据。例如,GROUPBY能够从重复那些值的源文件中返回一个唯一的邮政编码列表:
SELECTZIP
FROMCustomers
GROUPBYZIP
仅包括那些在GROUPBY和SELECT列列表中字义组的列。换句话说,SELECT列表必须与GROUP列表相匹配。只有一种情况例外:SELECT列表能够包含聚合函数。(而GROUP
BY不支持聚合函数。)
记住,GROUPBY不会对作为结果产生的组分类。要对组按字母或数字顺序排序,增加一个ORDERBY子句(#1)。另外,在GROUP
BY子句中您不能引用一个有别名的域。组列必须在根本数据中,但它们不必出现在结果中。
#3:分组前限定数据
您可以增加一个WHERE子句限定由GROUPBY分组的数据。例如,下面的语句仅返回肯塔基地区顾客的邮政编码列表。
SELECTZIP
FROMCustomers
WHEREState='KY'
GROUPBYZIP
在GROUPBY子句求数据的值之前,WHERE对数据进行过滤,记住这一点很重要。
和GROUPBY一样,WHERE不支持聚合函数。
#4:返回所有组
当
您用WHERE过滤数据时,得到的组只显示那些您指定的记录。符合组定义但不满足子句条件的数据将不会出现在组中。不管WHERE条件如何,如果您想包括
所有数据,增加一个ALL子句。例如,在前面的语句中增加一个ALL子句会返回所有邮政编码组,而不仅仅是肯塔基地区的组。
SELECTZIP
FROMCustomers
WHEREState='KY'
GROUPBYALLZIP
照这个样子,这两个子句会造成冲突,您可能不会以这种方式使用ALL子句。当您用聚合求一个列的值时,应用ALL子句很方便。例如,下面的语句计算每个肯塔基邮政编码的顾客数目,同时显示其它邮政编码值。
SELECTZIP,Count(ZIP)ASKYCustomersByZIP
FROMCustomers
WHEREState='KY'
GROUPBYALLZIP
得到的组由根本数据中的所有邮政编码值构成。但是,聚合列(KYCustomerByZIP)显示为0,因为除肯塔基邮政编码组外没有别的组。
远程查询不支持GROUPBYALL。
#5:分组后限定数据
WHERE子句(#3)在GROUPBY子句之前求数据的值。当您希望在分组以后限定数据时,使用HAVING。通常,不管您使用WHERE还是HAVING,得到的结果相同。但要记住,这
两个子句不能互换,这点很重要。如果您存在疑问,这里有一条应用指南:过滤记录时使用WHERE;过滤组时使用HAVING。
一般,您会用HAVING,利用聚合来求一个组的值。例如,下面的语句返回一个邮政编码列表,但这个表内可能不包含根本数据源中的每个邮政编码:
SELECTZIP,Count(ZIP)ASCustomersByZIP
FROMCustomers
GROUPBYZIP
HAVINGCount(ZIP)=1
仅仅那些只有一名顾客的组出现在结果中。
#6:详细了解WHERE和HAVING
如果您仍然对WHERE和HAVING的用法感到迷惑,应用下面的指导方法:
WHERE出现在GROUPBY之前;SQL在它分组记录前求WHERE子句的值。
HAVING出现在GROUPBY之后;SQL在它分组记录后求HAVING子句的值。
#7:用聚合总计分组值
分组数据有助于对数据进行分析,但有时您还需要组本身以外的其它信息。您可以增加一个聚合函数来总计分组数据。例如,下面的语句为每次排序显示一个小计:
SELECTOrderID,Sum(Cost*Quantity)ASOrderTotal
FROMOrders
GROUPBYOrderID
与其它的组一样,SELECT和GROUPBY列表必须相匹配。在SELECT子句中包含一个聚合是这一规则的唯一例外。
#8:总计聚合
您可以通过显示每个组的小计进一步总计数据。SQL的ROLLUP运算符为每个组显示一个额外的记录,一个小计。那个记录是用聚合函数在每个组中求所有记录的值的结果。下面的语句为每个组合计OrderTotal列。
SELECTCustomer,OrderNumber,Sum(Cost*Quantity)ASOrderTotal
FROMOrders
GROUPBYCustomer,OrderNumber
WITHROLLUP
一个包含20和25这两个OrderTotal值的组的ROLLUP行将显示OrderTotal值45。ROLLUP结果的第一个值是唯一的,因为它求所有组记录的值。那个值是整个记录集的总和。
ROLLUP不支持聚合函数中的DISTINCT或GROUPBYALL子句。
#9:总计每一列
CUBE运算符比ROLLUP更进一步,它返回每个组中每个值的总数。得到的结果与ROLLUP相似,但CUBE包括组中每一列的一个额外记录。下面的语句显示每个组的小计和每名顾客的一个额外总数。
SELECTCustomer,OrderNumber,Sum(Cost*Quantity)ASOrderTotal
FROMOrders
GROUPBYCustomer,OrderNumber
WITHCUBE
用CUBE得到的总计最为复杂。不仅完成聚合与ROLLUP的工作,而且还求定义组的其它列的值。也就是说,CUBE总计每一个可能的列组合。
CUBE不支持GROUPBYALL。
#10:给总计排序
当CUBE的结果杂乱无章时(一般都是这样),可以增加一个GROUPING函数,如下所示:
SELECTGROUPING(Customer),OrderNumber,Sum(Cost*Quantity)ASOrderTotal
FROMOrders
GROUPBYCustomer,OrderNumber
WITHCUBE
其结果包括每一行的两个额外的值。
值1表明左边的值是一个总计值——ROLLUP或CUBE的运算符的结果。
值0表明左边的值是一个原始GROUPBY子句产生的详细记录。
在分组查询中还可以配合使用HAVING子句,定义查询条件。
使用groupby进行分组查询
在使用groupby关键字时,在select列表中可以指定的项目是有限制的,select语句中仅许以下几项:
〉被分组的列
〉为每个分组返回一个值得表达式,例如用一个列名作为参数的聚合函数
groupby有一个原则,就是select后面的所有列中,没有使用聚合函数的列,必须出现在groupby后面(重要)
groupby实例
实例一
数据表:
姓名科目分数
张三语文80
张三数学98
张三英语65
李四语文70
李四数学80
李四英语90
期望查询结果:
姓名语文数学英语
张三809865
李四708090
代码
SQL代码复制
createtabletestScore
(
tidintprimarykeyidentity(1,1),
tnamevarchar(30)null,
ttypevarchar(10)null,
tscorintnull
)
go
---插入数据
insertintotestScorevalues('张三','语文',80)
insertintotestScorevalues('张三','数学',98)
insertintotestScorevalues('张三','英语',65)
insertintotestScorevalues('李四','语文',70)
insertintotestScorevalues('李四','数学',80)
insertintotestScorevalues('李四','英语',90)
selecttnameas'姓名',
max(casettypewhen'语文'thentscorelse0end)'语文',
max(casettypewhen'数学'thentscorelse0end)'数学',
max(casettypewhen'英语'thentscorelse0end)'英语'
fromtestScore
groupbytname
实例二
有如下数据:(为了看得更清楚,我并没有使用国家代码,而是直接用国家名作为PrimaryKey)
国家(country) | 人口(population) |
中国 | 600 |
美国 | 100 |
加拿大 | 100 |
英国 | 200 |
法国 | 300 |
日本 | 250 |
德国 | 200 |
墨西哥 | 50 |
印度 | 250 |
洲 | 人口 |
亚洲 | 1100 |
北美洲 | 250 |
其他 | 700 |
SQL代码复制
SELECTSUM(population),
CASEcountry
WHEN'中国'THEN'亚洲'
WHEN'印度'THEN'亚洲'
WHEN'日本'THEN'亚洲'
WHEN'美国'THEN'北美洲'
WHEN'加拿大'THEN'北美洲'
WHEN'墨西哥'THEN'北美洲'
ELSE'其他'END
FROMTable_A
GROUPBYCASEcountry
WHEN'中国'THEN'亚洲'
WHEN'印度'THEN'亚洲'
WHEN'日本'THEN'亚洲'
WHEN'美国'THEN'北美洲'
WHEN'加拿大'THEN'北美洲'
WHEN'墨西哥'THEN'北美洲'
ELSE'其他'END;
同样的,我们也可以用这个方法来判断工资的等级,并统计每一等级的人数。SQL代码如下;
SQL代码复制
SELECT
CASEWHENsalary<=500THEN'1'
WHENsalary>500ANDsalary<=600THEN'2'
WHENsalary>600ANDsalary<=800THEN'3'
WHENsalary>800ANDsalary<=1000THEN'4'
ELSENULLENDsalary_class,
COUNT(*)
FROMTable_A
GROUPBY
CASEWHENsalary<=500THEN'1'
WHENsalary>500ANDsalary<=600THEN'2'
WHENsalary>600ANDsalary<=800THEN'3'
WHENsalary>800ANDsalary<=1000THEN'4'
ELSENULLEND;
对于groupby后面一般都是跟一个列名,但在该例子中通过case语句使分组变得跟强大了。
实例三
有如下数据
国家(country) | 性别(sex) | 人口(population) |
中国 | 1 | 340 |
中国 | 2 | 260 |
美国 | 1 | 45 |
美国 | 2 | 55 |
加拿大 | 1 | 51 |
加拿大 | 2 | 49 |
英国 | 1 | 40 |
英国 | 2 | 60 |
国家 | 男 | 女 |
中国 | 340 | 260 |
美国 | 45 | 55 |
加拿大 | 51 | 49 |
英国 | 40 | 60 |
SQL代码复制
SELECTcountry,
SUM(CASEWHENsex='1'THEN
populationELSE0END),--男性人口
SUM(CASEWHENsex='2'THEN
populationELSE0END)--女性人口
FROMTable_A
GROUPBYcountry;
GROUPBY子句中的NULL值处理
当GROUPBY子句中用于分组的列中出现NULL值时,将如何分组呢?SQL中,NULL不等于NULL(在WHERE子句中有过介绍)。然而,在GROUPBY子句中,却将所有的NULL值分在同一组,即认为它们是“相等”的。
HAVING子句
GROUPBY子句分组,只是简单地依据所选列的数据进行分组,将该列具有相同值的行划为一组。而实际应用中,往往还需要删除那些不能满足条件的行组,为了实现这个功能,SQL提供了HAVING子句。语法如下。
SELECTcolumn,SUM(column)
FROMtable
GROUPBYcolumn
HAVINGSUM(column)conditionvalue
说明:HAVING通常与GROUPBY子句同时使用。当然,语法中的SUM()函数也可以是其他任何聚合函数。DBMS将HAVING子句中的搜索条件应用于GROUPBY子句产生的行组,如果行组不满足搜索条件,就将其从结果表中删除。
HAVING子句的应用
从TEACHER表中查询至少有两位教师的系及教师人数。
实现代码:
SQL代码复制
SELECTDNAME,COUNT(*)ASnum_teacher
FROMTEACHER
GROUPBYDNAME
HAVINGCOUNT(*)>=2
HAVING子句与WHERE子句的区别
HAVING子句和WHERE子句的相似之处在于,它也定义搜索条件。但与WHERE子句不同,HAVING子句与组有关,而不是与单个的行有关。
1、如果指定了GROUPBY子句,那么HAVING子句定义的搜索条件将作用于这个GROUPBY子句创建的那些组。
2、如果指定WHERE子句,而没有指定GROUPBY子句,那么HAVING子句定义的搜索条件将作用于WHERE子句的输出,并把这个输出看作是一个组。
3、如果既没有指定GROUPBY子句也没有指定WHERE子句,那么HAVING子句定义的搜索条件将作用于FROM子句的输出,并把这个输出看作是一个组。
4、在SELECT语句中,WHERE和HAVING子句的执行顺序不同。在本书的5.1.2节介绍的SELECT语句的执行步骤可知,WHERE子句只能接收来自FROM子句的输入,而HAVING子句则可以接收来自GROUPBY子句、WHERE子句和FROM子句的输入。
相关文章推荐
- 学习MySQL——下载与安装
- mysql sql中类似转换引起的索引无法使用的问题
- SQL SERVER 批量插入记录
- java 插入oracle中clob字段
- SQL SERVER 批量插入记录
- Oracle 10g RAC TAF介绍
- MSSQLSERVER 启动模式设置为自动
- sql2008数据库一致性错误恢复
- vs2015安装ORACLE的DbFirst
- mysql性能优化建议
- mysqldump 的用法
- 17.3.1.1 Backing Up a Slave Using mysqldump
- ADO存取数据库时如何分页显示
- 查询某条记录在数据库中是 第几行
- [BBED]Oracle 11.2.0.4 Centos6.5下编译bbed
- oracle 11g 查询 列转换成行
- Oracle Rman修复逻辑坏块
- redis客户端hiredis
- Oracle 目录
- Oracle 11g R2 常用配置与日志的文件位置