SQL点滴10—使用with语句来写一个稍微复杂sql语句,附加和子查询的性能对比

原文:SQL点滴10—使用with语句来写一个稍微复杂sql语句,附加和子查询的性能对比

今天偶尔看到sql中也有with关键字,好歹也写了几年的sql语句,居然第一次接触,无知啊。看了一位博主的文章,自己添加了一些内容,做了简单的总结,这个语句还是第一次见到,学习了。我从简单到复杂地写,希望高手们不要见笑。下面的sql语句设计到三个表,表的内容我用txt文件复制进去,这里不妨使用上一个随笔介绍的建立端到端的package的方法将这些表导入到数据库中,具体的就不说了。

从这里下载文件employees.txt,customers.txt,orders.txt

参考文章:http://www.cnblogs.com/wwan/archive/2011/02/24/1964279.html

使用package导入数据:http://www.cnblogs.com/tylerdonet/archive/2011/04/17/2017471.html

简单的聚合

从orders表中选择各个年份共有共有多少客户订购了商品

  • 第一种写法,我们可以写成这样

    1select YEAR(o.orderdate) orderyear,COUNT(distinct(custid)) numCusts2from Sales.Orders o3group by YEAR(o.orderdate)4go
    

    要注意的是如果把group by YEAR(o.orderdata)换成group by orderyear就会出错,这里涉及到sql语句的执行顺序问题,有时间再了解一下          

  • 第二种写法,

    1select orderyear,COUNT(distinct(custid))numCusts2from (select YEAR(orderdate) as orderyear,custid from sales.orders) as D3group by orderyear4go
    

    在from语句中先得到orderyear,然后再select语句中就不会出现没有这个字段的错误了

  • 第三种写法,

    1select orderyear,COUNT(distinct(custid)) numCusts2from (select YEAR(orderdate),custid from sales.orders) as D(orderyear,custid)3group by orderyear4go
    

    在as D后面加上选择出的字段,是不是更加的清楚明了呢!

  • 第四种写法,with出场了

    1with c as(2select YEAR(orderdate) orderyear, custid from sales.orders)3select orderyear,COUNT(distinct(custid)) numCusts from c group by orderyear4go
    

    with可以使语句更加的经凑,下面是权威解释。  
        
    指定临时命名的结果集,这些结果集称为公用表表达式 (CTE)。该表达式源自简单查询,并且在单条 SELECT、INSERT、UPDATE、MERGE 或 DELETE 语句的执行范围内定义。该子句也可用在 CREATE VIEW 语句中,作为该语句的 SELECT 定义语句的一部分。公用表表达式可以包括对自身的引用。这种表达式称为递归公用表达式。               

                                                ----MSDN

  • 第五种写法,也可以借鉴第三种写法,这样使语句更加清楚明了,便于维护

    1 with c(orderyear,custid) as(2  select YEAR(orderdate),custid from sales.orders)3  select orderyear,COUNT(distinct(custid)) numCusts from c group by c.orderyear4  go
    

    上面5中写法都得到相同的结果,如下图1:图1

添加计算

  • 现在要求要求计算出订单表中每年比上一年增加的客户数目,这个稍微复杂

    1 with yearcount as(2  select YEAR(orderdate) orderyear,COUNT(distinct(custid)) numCusts from sales.orders group by YEAR(orderdate))3  select cur.orderyear curyear,cur.numCusts curNumCusts,prv.orderyear prvyear,prv.numCusts prvNumCusts,cur.numCusts-prv.numCusts growth4  from yearcount cur left join yearcount prv on cur.orderyear=prv.orderyear+15 go
    

    这里两次使用到with结果集。查询得到的结果如下图2

    图2

复杂的计算

  • 查找客户id,这些客户和所有来自美国的雇员至少有一笔交易记录,查询语句如下

    1 with TheseEmployees as(2 select empid from hr.employees where country=‘USA‘),3 CharacteristicFunctions as(4 select custid,5        case when custid in (select custid from sales.orders as o where o.empid=e.empid) then 1 else 0 end as charfun6 from sales.customers as c cross join TheseEmployees as e)7 select custid,min(charfun) from CharacteristicFunctions group by custid having min(charfun)=18 go
    

    这里嵌套with语句,第with语句查找美国雇员的id,第二个语句使用这个结果和拥有客户的客户id和拥有关系标识做笛卡尔积运算。最后从这个笛卡尔积中通过标识找到最终的custid。
    结果如下图3

    图3

这里只有简单地介绍,没有深入,高手们不要见笑啊。

?

---------------------------------------------------------分界线----------------------------------------------------------

with语句和子查询的性能比较

在博友SingleCat的提醒下,对with语句做一些性能测试,这里使用的测试工具是SQL Server Profile。我选择了最后一个语句,因为这个语句比较复杂一点。开始的时候单独执行一次发现他们的差别不大,就差几个毫秒,后来想让他们多执行几次,连续执行10

次看看执行的结果。下面贴出测试用的语句。

 1 /*with查询*/ 2 declare @withquery varchar(5000) 3 declare @execcount int=0 4 set @withquery=‘with TheseEmployees as( 5 select empid from hr.employees where country=N‘‘USA‘‘), 6 CharacteristicFunctions as( 7 select custid, 8        case when custid in (select custid from sales.orders as o where o.empid=e.empid) then 1 else 0 end as charfun 9 from sales.customers as c cross join TheseEmployees as e)10 select custid from CharacteristicFunctions group by custid having min(charfun)=1 order by custid11 ‘ 12 while @execcount<1013 begin14 exec (@withquery);15 set @execcount=@execcount+116 end17 18 /*子查询*/19 declare @subquery varchar(5000)20 declare @execcount int=021 set @subquery=‘select custid from Sales.Orders where empid in22 (select empid from HR.Employees where country = N‘‘USA‘‘) group by custid23 having count(distinct empid)=(select count(*) from HR.Employees where country = N‘‘USA‘‘);24 ‘25 while @execcount<1026 begin27 exec (@subquery);28 set @execcount=@execcount+129 end

从SQL Server Profile中截图如下

从图中可以看到子查询语句的执行时间要少于with语句,我觉得主要是with查询中有一个cross join做了笛卡尔积的关系,于是又实验了上面的那个简单一点的,下面是测试语句。

 1 /*with语句*/ 2 declare @withquery varchar(5000) 3 declare @execcount int=0 4 set @withquery=‘with c(orderyear,custid) as( 5 select YEAR(orderdate),custid from sales.orders) 6 select orderyear,COUNT(distinct(custid)) numCusts from c group by c.orderyear‘  7 while @execcount<100 8 begin 9 exec (@withquery);10 set @execcount=@execcount+111 end12 13 /*子查询*/14 declare @subquery varchar(5000)15 declare @execcount int=016 set @subquery=‘select orderyear,COUNT(distinct(custid)) numCusts17 from (select YEAR(orderdate),custid from sales.orders) as D(orderyear,custid)18 group by orderyear‘19 while @execcount<10020 begin21 exec (@subquery);22 set @execcount=@execcount+123 end

这次做10次查询还是没有多大的差距,with语句用10个duration,子查询用了11个,有时候还会翻过来。于是把执行次数改成100,这次还是子查询使用的时间要少,截图如下

最终结论,子查询好比with语句效率高。

时间: 2024-12-24 10:24:39

SQL点滴10—使用with语句来写一个稍微复杂sql语句,附加和子查询的性能对比的相关文章

如何写一个像btgoogle一样的12306泄露数据查询

demo地址:http://www.btgoogle.com/12306/ 圣诞节,12306送给了我们一个大礼物.大约 14w的数据泄露, 看网上都沸沸扬扬的.开始也准备找一个数据库来看看,随后,我 得到了 一份 数据  大约 14M  也就是 143,863 个数据 .文件泄露的是 txt 格式的文件. 当群里有人也得到了这份数据.准备做一个 12306泄露的数据库做一个网站.提供他人查询自己是否在此次泄露信息的名单中. 信息是这种形式的 当时群里,有人开始比速度,程序员都好面子~~~ 有人

SQL应用与开发:(七)数据操作 &#183; 查 &#183; (三)使用子查询访问和修改数据

3.使用子查询访问和修改数据 子查询和连接查询一样提供了使用单个查询访问多个表中的数据的方法.子查询在其他结果的基础上提供一种有效地方式来表示WHERE子句的条件.子查询是一个SELECT语句,它定义在SELECT.INSERT.UPDATE或DELECT语句或者另一个子查询中.子查询的SELECT语句可与外部查询指向不同的表. 嵌套的子查询或嵌套的SELECT语句是指包含一个或多个子查询的SELECT语句.子查询可嵌套在外部的SELECT.INSERT.UPDATE或DELECT语句的WHER

SQL优化10原则

1.查询的模糊匹配尽量避免在一个复杂查询里面使用 LIKE '%parm1%'—— 红色标识位置的百分号会导致相关列的索引无法使用,最好不要用. 解决办法:其实只需要对该脚本略做改进,查询速度便会提高近百倍.改进方法如下:a.修改前台程序——把查询条件的供应商名称一栏由原来的文本输入改为下拉列表,用户模糊输入供应商名称时,直接在前台就帮忙定位到具体的供应商,这样在调用后台程序时,这列就可以直接用等于来关联了.b.直接修改后台——根据输入条件,先查出符合条件的供应商,并把相关记录保存在一个临时表里

SQL学习之学会使用子查询

1.SELECT语句是SQL的查询.我之前的随笔中所用的SELECT语句都是简单的查询,即从单个数据库表中检索数据的单条SELECT语句. 查询:任何SQL语句都是查询,但此术语一般指SELECT语句. 2.SQL不仅允许简单的SELECT查询,还允许创建子查询,即嵌套在其他查询中的查询. 下面通过实例来了解子查询在实际项目中的应用: create database Study gouse Study go create table Customers( Id int identity(1,1)

SQL 子查询,索引优化

场景 索引优化 单列索引 多列索引 索引覆盖 排序 场景 我用的数据库是mysql5.6,下面简单的介绍下场景 课程表 create table Course( c_id int PRIMARY KEY, name varchar(10) ) 数据100条 学生表: create table Student( id int PRIMARY KEY, name varchar(10) ) 数据70000条 学生成绩表SC CREATE table SC( sc_id int PRIMARY KEY

SQL编程之高级查询(子查询)以及注意事项

1.什么是子查询? 当一个查询是另一个查询的条件时,称之为子查询.子查询可以使用几个简单命令构造功能强大的复合命令.子查询最常用于SELECT-SQL命令的WHERE子句中.子查询是一个 SELECT 语句,它嵌套在一个 SELECT.SELECT...INTO 语句.INSERT...INTO 语句.DELETE 语句.或 UPDATE 语句或嵌套在另一子查询中. 语法:select ....from  表1  where  列1  > (子查询) 外面的查询成为父查询,圆括号嵌入的查询成为称

SQL随着子查询结果更新多个字段

笔者:iamlasong 要求:表格内容需要改变,在临时表中内容的变化,使用SQL官方声明更新表若干领域. 假设更新一个字段,直接用字段名=子查询就能够了,多个字段更新,将字段在括号里并列写出就可以,例如以下: update tb_jg t set t.jgfl = 'sd', (     t.zj_code, t.zj_mc) = (select a.zj_code, a.zj_mc from song_temp a where a.zj_code = t.zj_code) where exi

SQL用子查询结果更新多个字段

作者:iamlasong 要求:表格的内容需要变更,变更的内容放在一个临时表中,用SQL语句更新正式表中多个字段. 如果更新一个字段,直接用字段名=子查询就可以了,多个字段更新,将字段在括号中并列写出即可,如下: update tb_jg t set t.jgfl = 'sd', (     t.zj_code, t.zj_mc) = (select a.zj_code, a.zj_mc from song_temp a where a.zj_code = t.zj_code) where ex

python写一个乘法表的脚本

学习脚本的时候经常会被问到会不会写一个99乘法表,现在就用python语句简单写一个乘法表 [[email protected] python_py]# cat while3.py i = 1 while (i<=9):        j=1        while(j<=i):               printj,"x",i,"=",j*i,"\t",               j=j+1        print&quo