zoukankan      html  css  js  c++  java
  • 《SQL Server 2012 T-SQL基础》读书笔记

    Chapter 3 Joins

    Cross Joins(交叉联接)就是返回两个表的笛卡尔积(m行的表cross join一个n行的表得到一个m * n行的结果),它有两种标准SQL语法,第一种:

    SELECT C.custid, E.empid
    FROM Sales.Customers AS C
      CROSS JOIN HR.Employees AS E;
    

    第二种:

    SELECT C.custid, E.empid
    FROM Sales.Customers AS C, HR.Employees AS E;
    

    你也可以对同一个表的多个实例进行联接,这种self join对cross joins, inner joins, 和outer join都适用。有这么一个技巧:首先创建一个表Digits,它只有一列digit,值是0到9,然后如果你要输出1到1000,那么可以这样:

    SELECT D3.digit * 100 + D2.digit * 10 + D1.digit + 1 AS n
    FROM dbo.Digits AS D1
      CROSS JOIN dbo.Digits AS D2
      CROSS JOIN dbo.Digits AS D3
    ORDER BY n;
    

    Inner Joins(内联接)有两个逻辑上的处理阶段:首先算出笛卡尔积,然后根据你指定的谓词对结果的行进行过滤。它同样有两种标准SQL的语法,第一种:

    SELECT E.empid, E.firstname, E.lastname, O.orderid
    FROM HR.Employees AS E
      JOIN Sales.Orders AS O   --因为inner join是默认,所以INNER可以省略
        ON E.empid = O.empid;
    

    对这种一对多的内联接,其实可以想成就是对每一行employee都对应很多行有相同employee ID的orders行。这里的ON与WHERE和HAVING一样,只接受TRUE而不接受FALSE或UNKNOWN。
    第二种语法:

    SELECT E.empid, E.firstname, E.lastname, O.orderid
    FROM HR.Employees AS E, Sales.Orders AS O
    WHERE E.empid = O.empid;
    

    其实只是把第一种语法的ON移到了WHERE里面去,本质都一样。对于Cross Joins和Inner Joins,作者都建议第一种语法,因为意图更清晰,不容易出错。

    连接条件也可以是多个的,例如:

    FROM dbo.Table1 AS T1
      JOIN dbo.Table2 AS T2
        ON T1.col1 = T2.col1
        AND T1.col2 = T2.col2
    

    一般运用于这种情况:有个一对多的关系,而“一”的表的主键是基于多个列的,比如(orderid, productid),然后那个“多”的表肯定就要有一个外键来引用这个“一”的表的主键了对吧?(外键也能基于不止一个列)。我的理解是:如果在外键的那个表中如果添加了一行,外键的那两个列(比如就两列)的值必须是主键的那个表中已经存在的任意一行的那两个列的值,所以我推测字段的顺序必须一样,必须:FOREIGN KEY(orderid, productid) REFERENCES Sales.OrderDetails(orderid, productid),而不能FOREIGN KEY(productid, orderid) REFERENCES Sales.OrderDetails(orderid, productid)。所以在联接的时候就要这样:ON OD.orderid = ODA.orderid AND OD.productid = ODA.productid)。

    联接条件也可以不是等于号:

    SELECT
      E1.empid, E1.firstname, E1.lastname,
      E2.empid, E2.firstname, E2.lastname
    FROM HR.Employees AS E1
      JOIN HR.Employees AS E2
        ON E1.empid < E2.empid;
    

    这样的话会去掉self pairs (for example,1 with 1)和mirrored pairs (for example, 1 with 2 and also 2 with 1),于是得到雇员id之间的唯一的一对一配对。

    Outer Joins(外联接)
    有三种,虽然后两种都不常用:LEFT OUTER JOIN,RIGHT OUTER JOIN, FULL OUTER JOIN。RIGHT就是保留右边的全部的行,FULL就是左右两边的表都保留,OUTER可以省略。外联接相比内联接又多加了一步的处理,就是在笛卡尔积并且过滤结果后,又把要保留的那个表的一些行加进来了,这些行我们叫它outer row(外部行)。
    如果你要在外联接的结果中选出所有外部行,就:

    SELECT C.custid, C.companyname
    FROM Sales.Customers AS C
      LEFT OUTER JOIN Sales.Orders AS O
        ON C.custid = O.custid
    WHERE O.orderid IS NULL;
    

    注意,这里除了可以选择O.orderid(因为它用作ON上)作为WHERE中的条件,也可以选择在不被保留的表(这里就是Sales.Orders)里面不为NULL的字段,或者主键(因为主键不为NULL)。

    打印出介于某两个日期之间的所有日期:

    SELECT DATEADD(day, n-1, '20060101') AS orderdate
    FROM dbo.Nums
    WHERE n <= DATEDIFF(day, '20060101', '20081231') + 1
    ORDER BY orderdate;
    

    这里的dbo.Nums是1,2,3,4,5,6……越大越好。
    如果想查出Orders表中,对于以上日期的每一天的订单情况(包括没有订单的情况):

    SELECT DATEADD(day, Nums.n - 1, '20060101') AS orderdate,
      O.orderid, O.custid, O.empid
    FROM dbo.Nums
      LEFT OUTER JOIN Sales.Orders AS O
        ON DATEADD(day, Nums.n - 1, '20060101') = O.orderdate
    WHERE Nums.n <= DATEDIFF(day, '20060101', '20081231') + 1
    ORDER BY orderdate;
    

    其实这里的日期计算函数只是为了把连续的Nums映射到连续的日期,然后对每一个日期都保留。

    关于外联接有个常见的错误,就是如果你在WHERE中用非保留的表中的列作为过滤条件的话,那么就等于内联接,例如:

    SELECT C.custid, C.companyname, O.orderid, O.orderdate
    FROM Sales.Customers AS C
      LEFT OUTER JOIN Sales.Orders AS O
        ON C.custid = O.custid
    WHERE O.orderdate >= '20070101';
    

    这样的话会把所有orderdate为NULL的外部行也过滤掉,那你还不如改成Inner join。

    另一种常见错误是:

    SELECT C.custid, O.orderid, OD.productid, OD.qty
    FROM Sales.Customers AS C
      LEFT OUTER JOIN Sales.Orders AS O
        ON C.custid = O.custid
      JOIN Sales.OrderDetails AS OD
        ON O.orderid = OD.orderid;
    

    注意,第一次Outer Join之后,可能会有orderid是null的行,结果第二次inner join之后,这些行就绝逼被过滤掉了。解决方法是要么就两次outer join,要么就先inner join后面两个表,然后在right outer join第一个表,或者也可以把“inner join后面两个表”用括号括起来,再用第一个表Left join它们。

    另一个常见错误:

    SELECT C.custid, COUNT(*) AS numorders
    FROM Sales.Customers AS C
      LEFT OUTER JOIN Sales.Orders AS O
      ON C.custid = O.custid
    GROUP BY C.custid;
    

    还记得COUNT(*)会返回包括NULL的列吗,所以说这里就算有个顾客没有订单,也会算他的订单是一个。所以说记得用COUNT(O.orderid)。

  • 相关阅读:
    宏定义中的#
    HDU1506 Largest Rectangle in a Histogram 动态规划
    HDU1864 最大报销额 DP
    POJ2771 Guardian of Decency 最大独立子集
    POJ1698 Alice's Chance 最大流
    HDU1003 Max Sum 动态规划
    eval格式化事件类型的字符串
    C#虚方法virtual详解
    c# 利用反射获得某个类或者对象的所有属性
    windows服务的通常写法
  • 原文地址:https://www.cnblogs.com/raytheweak/p/7141044.html
Copyright © 2011-2022 走看看