zoukankan      html  css  js  c++  java
  • TSQL查询的逻辑工作原理

    很多朋友都会使用T-SQL编写查询,但实际上对于其工作原理大多知之甚少。下面我们来分析一下查询的逻辑工作原理,也就是说一个查询到底是怎么一步一步的工作,最终给我们展示数据的。

    认真观察下面这个图。想想看,这是我们常写的语句,你真的有没有想过哪个部分是先执行,而哪是个后面执行的呢

    image

    image

    ON,WHERE,HAVING是三种筛选的子句。正因为他们是在SELECT语句执行之前执行的,所以他们所用的表达式是不可以为SELECT子句中重命名的列的

    他们还有一个共同特征,如果比较运算符的一个部分为NULL,则既不返回TRUE也不返回FALSE,而是返回UNKNOWN

     

    下面我们用一个实例来讲解这些步骤

    第一步:准备实验环境和数据

    SET NOCOUNT ON;
    USE tempdb;
    GO
    IF OBJECT_ID('dbo.Orders') IS NOT NULL
      DROP TABLE dbo.Orders;
    GO
    IF OBJECT_ID('dbo.Customers') IS NOT NULL
      DROP TABLE dbo.Customers;
    GO
    CREATE TABLE dbo.Customers
    (
      customerid  CHAR(5)     NOT NULL PRIMARY KEY,
      city        VARCHAR(10) NOT NULL
    );
    
    INSERT INTO dbo.Customers(customerid, city) VALUES('FISSA', 'Madrid');
    INSERT INTO dbo.Customers(customerid, city) VALUES('FRNDO', 'Madrid');
    INSERT INTO dbo.Customers(customerid, city) VALUES('KRLOS', 'Madrid');
    INSERT INTO dbo.Customers(customerid, city) VALUES('MRPHS', 'Zion');
    
    CREATE TABLE dbo.Orders
    (
      orderid    INT        NOT NULL PRIMARY KEY,
      customerid CHAR(5)    NULL     REFERENCES Customers(customerid)
    );
    
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(1, 'FRNDO');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(2, 'FRNDO');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(3, 'KRLOS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(4, 'KRLOS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(5, 'KRLOS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(6, 'MRPHS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(7, NULL);
    

    以上语句是在tempdb中创建了两个表,分别表示客户信息和订单信息。订单表与客户表是通过customerid进行连接的。我们也分别为他们输入了一些数据。如下面所示

    image image

    如你所见,这两个表很简单的

    第二步:准备一个查询做测试

    /*
    一个测试查询,检索那些订单个数小于3的客户,并且按订单总数排序(升序)
    */
    SELECT C.customerid, COUNT(O.orderid) AS numorders
    FROM dbo.Customers AS C
      LEFT OUTER JOIN dbo.Orders AS O
        ON C.customerid = O.customerid
    WHERE C.city = 'Madrid'
    GROUP BY C.customerid
    HAVING COUNT(O.orderid) < 3
    ORDER BY numorders;

    同样是很简单的一个查询,它返回是订单总数小于3的客户。如下所示

    image

    很多朋友研究到这里就停止了,不过,我们下面要详细的分析这个查询的结果是怎么出来的

    /*第一步:处理FROM子句,把来源的表进行CROSS JOIN(笛卡尔乘积)
    我这里把结果做一个生成表查询,写到一个临时表(VT1)中去
    */
    SELECT C.customerid as Customer,c.city,o.*  INTO #VT1 FROM dbo.Customers C,dbo.Orders  O
    SELECT * FROM #VT1
    --返回28行数据(4*7)
    
    image 
     
    --第二步:处理ON子句,只把那些两个表的customerid匹配的行找出来,我把它们放到VT2中去
    SELECT temp.* INTO #VT2 FROM (SELECT * FROM #VT1 WHERE Customer=customerid) temp
    SELECT * FROM #VT2
    --返回6行数据
    
    image 
     
    --第三步:根据JOIN语句的类型,决定是否要添加行到VT2中去,例如如果是LEFT  JOIN的话,那么就要检查坐边的表(我们这里是customers表)的连接键值是否都存在,如果不存在就要去添加到VT2中
    SELECT temp.* INTO #VT3 FROM
    (SELECT * FROM #VT2
    UNION ALL
    SELECT CustomerID,City,NULL,NULL FROM dbo.Customers c WHERE NOT EXISTS(SELECT DISTINCT Customer FROM #VT2 WHERE Customer=c.CustomerID)) temp
    SELECT * FROM #VT3
    --返回7行数据,其中有一个客户,因为没有订单,这一步中被添加进来。它的Orders的记录被标记为NULL
    
    image 
    --第四步:处理WHERE 子句,对VT3的结果集进行过滤,我们的条件是city=Madid
    SELECT temp.* INTO #VT4 FROM
    (SELECT * FROM #VT3 WHERE city='Madrid') temp
    SELECT * FROM #VT4
    --返回6行数据,因为有一个客户不是这个城市的
    
    image 
     
    --第五步:处理GROUP子句,进行分类汇总
    SELECT temp.* INTO #VT5 FROM
    (SELECT Customer,COUNT(OrderID)  as orderidcount,COUNT(city) as citycount,Count(customerid)  as customeridcount FROM #VT4 GROUP BY Customer) temp
    SELECT * FROM #VT5
    --返回3行数据,根据客户分组,统计了订单的个数 
    --这里会不会去统计其他列的汇总呢
    image 
     
    更正:这一步,逻辑上的数据结构是下面这样的,也就是说其实并没有计算。而是分组
    image 
     
    --因为没有WITH Rollup和WITH Cube语句,所以跳过第六步,进入HAVING子句的处理
    
    --第六步:处理HAVING子句,对GROUP之后的结果进行筛选,我们这里的条件是orderidcount<3
    SELECT temp.* INTO #VT6 FROM
    (SELECT * FROM #VT5 WHERE orderidcount<3) temp
    SELECT * FROM #VT6
    --返回2行数据
    image 
     
    --第七步:使用SELECT 的字段列表过滤结果集
    SELECT #VT6.Customer,#VT6.orderidcount as  numorders  INTO #VT7 FROM #VT6
    SELECT * FROM #VT7
    --还是2行数据,只不过只有两个列了
    image 
     
    SELECT 返回的结果在内部其实是一个游标集,本身并没有顺序,它就好比是一个集合。
     
    --第八步:跳过DISTINCT,进行OrderBy操作
    SELECT #VT7.* INTO #VT8 FROM #VT7 Order By #VT7.Customer
    SELECT * FROM #VT8
    --返回2行数据,经过排序
    
    image 
    
    --这个查询结束演示。完整的8个步骤
    仔细看看,是不是我们需要的结果呢?
    
    
  • 相关阅读:
    EasyUI-datagrid中load,reload,loadData方法的区别
    CAD ObjectARX扩展工具的源码(二)
    CAD ObjectARX扩展工具的源码(一)
    ObjectARX常用类和函数
    acdb类杂记
    ARX错误1 LINK : fatal error LNK1104: 无法打开文件“D:win10Desktop123x64Debugxxx.arx”
    关键功能函数是 (VLAX-Add-Cmd)。
    ObjectARX_2020函数收集
    得到ARX程序当前的路径
    objectarx向量的用法2
  • 原文地址:https://www.cnblogs.com/chenxizhang/p/1330375.html
Copyright © 2011-2022 走看看