《SQL初學者指南》讀書筆記

關係型數據庫和SQL

  • SQL語言的三個部分
    • DML:Data Manipulation Language,數據操縱語言,檢索、修改、增長、刪除數據庫(表)中的數據
    • DDL:Data Definition Language,數據定義語言,建立和修改數據庫(表)自己
    • DCL:Data Control Language,維護數據庫安全
  • 關係型數據庫基本術語
    • relational(關係):表示各表彼此關聯
    • record(記錄):表中的行(row)
    • field(字段):表中的列(column)
    • primary key:表的主鍵,一般爲自增型(auto-increment),自己沒有特殊含義,只用於保證每一行都有一個惟一的值
    • foreign key:表的外鍵,確保這一列有一個有效的值,一般會把某個其餘表的共同列,一般是主鍵做爲外鍵,好比訂單表中的客戶列
    • column(field)的數據類型(更詳細的數據類型介紹參見SQL數據類型
      • 數字
      • bit:位,只容許0和1
      • integer:整數,不含小數位
      • decimal:浮點數,含小數位
      • real number:實數
    • 字符:string/character string類型,在SQL語句中須要用引號括起來
    • 日期/時間:用於表示日期和時間,在SQL語句中須要用引號括起來,容許對所涉及的日期進行特殊運算
    • NULL:空值,非數據類型,而是在容許包含空值的列中表示空置

基本數據檢索

  • SQL語句不區分大小寫,且能夠寫成任意多行
  • 能夠把重要關鍵字做爲單獨一行凸顯語義
  • 從表中選擇全部:SELECT * FROM tablename
  • 指定列:SELECT columnname FROM tablename
  • 指定多個列:SELECT column1, column2 FROM tablename
  • 帶空格的列名:Microsoft SQL Server使用方括號[],MySQL使用重音符`,Oracle使用雙引號

計算字段和別名

  • 計算字段
    • 直接量:與表中數據沒有任何關係的值叫作literal value(直接量),字符串直接量須要加引號,數字直接量不須要
    • 算數運算:容許使用列數據與直接量或其它列數據進行加減乘除運算,好比SELECT QuantityPurchased * PricePerItem FROM Orders
    • 鏈接字段:把字符數據鏈接或組合到一塊兒,Microsoft SQL Server使用加號+,MySQL使用CONCAT()函數鏈接字符串,Oracle使用雙豎線||
  • 別名
    • 列的別名:用於修改列(表頭)標題或爲計算字段提供列(表頭)標題,好比SELECT f_n AS 'first_name' from customers
    • 表的別名:一般有三種狀況須要修改表名稱,a.很差理解或複雜的表名,b.從多個表中進行選擇,c.使用子查詢;SELECE something from someshittablename AS another_table_name

使用函數

  • Scalar function:標量函數,針對單行中的數據執行
  • Aggregate function:聚合函數,針對較大的數據集合進行操做
  • 字符函數
    • LEFT/RIGHT (string, numberOfCharactors):從左/右取字符串數據的指定位數,在Oracle中以SUBSTR替代
    • SUBSTRING (string, start, end):取得字符串數據的子字符串,在Oracle中以SUBSTR替代
    • LTRIM/RTRIM (string):刪除字符串數據左側/右側的空格
    • CONCAT (string1, string2, string3 ...):拼接多個字符串,Oracle中只容許拼接兩個字符串
    • UPPER/LOWER (string):返回字符串的大/小寫
  • 複合函數:函數的嵌套使用被稱爲複合函數,好比RIGHT(RTRIM(something)) AS 'something'
  • 日期時間函數
    • GETDATE/NOW/CURRENT_DATE ():三個函數都用於獲取當前時間,對應Microsoft SQL Server/MySql/Oracle三家數據庫的實現
    • DATEPART (date_part, date_value):單獨返回某個部分的時間,date_part爲須要返回的時間部分,date_value爲原始時間,MySQL的實現爲DATE_FORMAT(date_value, date_format),date_value爲原始時間,date_format爲相似於%d這樣的格式用於告訴函數須要返回哪部分時間,date_part的有效值爲:year/quarter/month/dayofyear/day/month/hour/minute/second
    • DATEDIFF (date_part, start_date, end_date):用於計算任意兩個不一樣日期間相差的時間,在MySQL中該函數之容許計算天數差別,因此date_part參數不可用,僅須要傳入兩個日期便可
  • 數值函數(數學函數)
    • ROUND (NumbericValue, DecimalPlaces):對任意數進行四捨五入,NumbericValue表示要四捨五入的數,DecimalPlaces表示從第幾位開始四捨五入(即須要保留到第幾位),以十分位爲0,向左爲負數,向右爲正數
    • RAND ([seed]):產生一個隨機數 ,可選的seed參數爲一個整數,用於每次返回相同的值
    • PI ():返回數學運算中的pi值
  • 轉換函數
    • CAST (expression AS data_type):將數據從一種類型轉換爲另外一種類型,expression表示數據或函數表達式,data_type表示須要轉換到的數據類型,通常狀況下SQL在作計算時會進行自動的類型轉換,因此不多用到這個函數,它的典型使用場景是當一個日期被保存成了字符串,須要轉換爲真正的日期數據:CAST('2017-5-1', AS DATETIME),Oracle中該函數的參數形式會不同
    • ISNULL/IFNULL/NVL (column_data_maybe_null, if_null_will_use_this_data ):將NULL值轉換爲一個有意義的值,對應Microsoft SQL Server/MySql/Oracle三家數據庫的實現

排序數據

排序數據的語法以下:sql

SELECT
column1,
column2
FROM table1, table2
ORDER BY column3, column2 DESC
複製代碼
  • ORDER BY 句子老是在FROM子句以後,FROM子句老是在SELECT關鍵字以後
  • SELECTORDER BY後面指定的列,多是徹底不一樣的一些列
  • 使用關鍵字ASCDESC來升序/降序排列
  • ORDER BY後指定了多列,則首先按照第一列排序,如遇相同則相同的行按第二列排序,以此類推
  • 根據計算字段排序時,若是計算字段已經出如今SELECT關鍵字後,則只須要在ORDER BY子句中指定計算字段的別名便可(經測試在MySQL中若是計算字段別名帶有空格則暫時沒法在此引用,由於不能使用引號),若是計算字段沒有出如今SELECT關鍵字後,則可直接在ORDER BY子句中指定一個計算字段,例如:
    SELECT
    title,
    rental_duration,
    rental_rate
    FROM film
    ORDER BY rental_duration * rental_rate DESC
    複製代碼
  • 按升序排列時,大部分的SQL數據庫都會是按NULL(Oracle中排在最後,可以使用NULLS FIRST關鍵字來強制最早)-數字-字符(字符中首先顯示數字字符,再顯示普通字符,除Oracle外不區分大小寫)來進行排序,反之亦然。

基於列的邏輯

  • 基於列的邏輯主要用於根據邏輯條件改變想要展示給用戶的輸出
  • 簡單格式:判斷某列的值爲某個具體值後將其轉換爲一個其它值
SELECT column1, column2
CASE column3
WHEN value1 THEN result1
WHEN value2 THEN value2
(repeat WHEN-THEN any number of times)
[ELSE defaul_result]
END
column4
FROM tablename
複製代碼
  • 查詢格式:判斷一列或多列中的某個值是否符合某個條件而將其轉換爲一個其它值並顯示在一列中
SELECT
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN  result2
(repeat WHEN-THEN any number of times)
[ELSE defaul_result]
END AS custom_column_name,
FROM tablename

# 最新版的MySQL語法與書中的語法有細微差異:
# ELSE子句最後不須要逗號

SELECT
title,
CASE
WHEN rental_duration = 3 THEN 'Three Day Left'
WHEN rental_rate = 0.99 THEN 'Cheapest'
ELSE 'Normal'
END AS 'Rental Status'
FROM film
複製代碼

基於行的邏輯

  • 基於行的邏輯主要用於獲取知足特定條件的數據
  • 應用查詢條件
    • SQL中的查詢條件從WHERE子句開始
    • WHERE子句老是在FROMORDER BY 子句之間,實際上任何「子句」都必須按照這個順序來
      SELECT columnList
      FROM tableList
      WHERE condition
      ORDER BY columnList
      複製代碼
  • WHERE子句操做符,如下這些操做符均可以在基於列的邏輯CASE WHEN condition語句中使用
    • 等於:=
    • 不等於:<>
    • 大於:>
    • 小於:<
    • 大於等於:>=
    • 小於等於:<=
    SELECT
    first_name,
    last_name
    FROM actor
    WHERE age > 18
    複製代碼
  • 限制行
    • 使用TOP/LIMIT/ROWNUM(對應Microsoft SQL Server、MySQL和Oracle)限制行數(關鍵字TOP返回的行,並非真的隨機樣本,而是根據數據庫中的物理存儲方式限定了前幾行數據而已)
      # Microsoft SQL Server
      SELECT
      TOP number
      columnList
      FROM table
      
      # MySQL
      SELECT
      columnList
      FROM table
      LIMIT number
      
      # Oracle
      SELECT
      columnList
      FROM table
      WHERE ROWNUM <= number
      複製代碼
    • 結合SORT BY子句作「Top N」查詢(基於特定分類,獲得帶有最大/小值的必定數量的行)
      # 本月賣得最好的莎士比亞的三本書
      # MySQL
      SELECT
      title AS 'Book Title',
      current_month_sale AS 'Quantity Sold'
      FROM books
      WHERE author = 'Shakespear'
      LIMIT 3
      ORDER BY current_month_sale DESC
      
      # Oracle中的TOP N查詢須要用到子查詢,後文會細講
      SELECT * FROM
      (SELECT
      title AS 'Book Title',
      current_month_sale AS 'Quantity Sold'
      FROM books
      ORDER BY current_month_sale)
      WHERE ROWNUM <= 3
      複製代碼

布爾邏輯(更復雜的基於行的邏輯)

  • 使用與AND、或OR、非NOT三個關鍵字在WHERE子句中表示布爾邏輯。與其它語言的計算順序同樣,AND的優先級最高,OR其次,NOT優先級最低,也可使用()來改變三者的計算順序
    # 這個例子僅爲展現布爾邏輯,實際使用不該該這麼繞
    SELECT
    first_name,
    last_name,
    age
    FROM actors
    WHERE
    NOT(
    (age < 18 OR age > 60)
    AND last_name = 'Jhon'
    )
    複製代碼
  • 還有兩個表示範圍的操做符BETWEENIN,用於替代column >= range_bottom AND column <= range_topcolumn = value1 OR column = value2這樣的特例,簡化SQL語句的編寫
    # BETWEEN,等價於 age >= 18 AND age <= 60
    SELECT
    first_name,
    last_name,
    age
    FROM actors
    WHERE
    age BETWEEN 18 AND 60
    
    # IN,等價於 state = 'IL' AND state = 'NY'
    SELECT
    customer_name,
    state
    FROM orders
    WHERE state IN ('IL', 'NY')
    複製代碼
  • 使用IS NULLWHERE子句中判斷一個列是否爲空,也能夠與函數ISNULL(column, value)結合使用
    # 選取重量爲0或者沒有填寫重量的產品
    SELECT
    product_description,
    weight
    FROM products
    WHERE weight = 0
    OR weight IS NULL
    
    # 使用ISNULL等價的寫法
    SELECE
    product_description,
    weight
    FROM products
    WHERE ISNULL(weight, 0) = 0
    
    # IS NULL和ISNULL
    SELECT
    product_description,
    ISNULL(weight, 0) AS 'weight'
    FROM products
    WHERE weight = 0
    OR weight IS NULL
    複製代碼

模糊匹配

  • WHERE子句可使用LIKE操做符來查找針對列值的某部分匹配
    • 包含某關鍵字:
      SELECT 
        title
      FROM
        film
      WHERE
        title LIKE '%love%'
      複製代碼
    • 以某關鍵字開頭:
      SELECT
        title
      FROM
        film
      WHERE
        title LIKE 'love%'
      複製代碼
    • 以某關鍵字結尾
      SELECT
        title
      FROM
        film
      WHERE
        title LIKE '%love'
      複製代碼
    • 包含某關鍵字但不以其開頭也不以其結尾(未能在MySQL 4中驗證,只驗證經過了單獨的不以某字符串開頭,或者不以某字符串結尾兩種狀況)
      SELECT
        title
      FROM
        film
      WHERE
        title LIKE '% love %'
      複製代碼
  • 通配符
    符號 含義
    % 任意個任意字符
    _ 一個任意字符
    [characterlist] 一個指定字符列表中的字符(在MySQL和Oracle中沒有)
    [^charactorlist] 一個非指定字符列表中的字符(在MySQL和Oracle中沒有)
  • NOT操做符能夠和LIKE操做符組合使用,例如
    SELECT
    first_name,
    last_name
    FROM actor
    WHERE first_name LIKE '%ARY%'
    AND last_name NOT LIKE '[MG]ARY'
    複製代碼
  • 按照讀音匹配(不經常使用也很差用)
    • SOUNDEX函數:可以輸出一個表示字符讀音的四字代碼(以首字母開頭,而後刪去剩餘字符中全部元音和字母y,最後轉換爲一個三位數的數字用於表示讀音,最後輸出相似S530
    • DIFFERENCE函數:能夠和SOUNDEX函數一塊兒使用(僅Microsoft SQL Server支持),檢查兩個字符的SOUNDEX類似度並返回一個表示讀音相近度的數字,(兩個字符的SOUNDEX值共有四個字符,每有一個位置的字符相等,則結果自增1,因此DIFFERENCE函數的返回值只有0到4五個可能的數字,越大越相近,越小越不一樣

彙總數據

  • 消除重複:使用DISTINCT關鍵字來刪除輸出重複的行
    # 查看全部藝術家(沒有顯示相同藝術家的行)
    SELECT
    DISTINCT
    artist
    FROM songs
    ORDER BY artist
    
    # 查看全部藝術家和專輯的惟一組合(沒有顯示同一藝術家和同一專輯的行,每一行中藝術家和專輯的組合是惟一的)
    SELECT
    DISTINCT
    artist,
    album
    FROM songs
    ORDER BY artist, album
    複製代碼
  • 聚合函數:標量函數只能針對單個的數字或值進行計算,而聚合函數則能夠用於分組數據
    函數 解釋
    SUM 合計、加總
    AVG 平均值
    MIN 最小值
    MAX 最大值
    COUNT 數量
    # 總值、均值、最大值、最小值
    SELECT
    SUN(fee) AS 'Total Gym Fees'
    AVG(grade) AS 'Average Quiz Score'
    MIN(grade) AS 'Minimum Quiz Score'
    MAX(grade) AS 'Maximum Quiz Score'
    FROM grades
    WHERE grade_type = 'quiz'
    
    # 返回全部選中行的數目
    SELECT
    COUNT(*) AS 'Count of Homework Rows'
    FROM grades
    WHERE grade_type = 'homework'
    
    # 返回指定列中存在值的行的數目
    SELECT
    COUNT(grade) AS 'Count of Homework Rows'
    FROM grades
    WHERE grade_type = 'homework'
    
    # 與DISTINCT配合返回指定列中惟一值的行數
    SELECT
    COUNT(DISTINCT fee_type) AS 'Number of Fee Types'
    FROM Fees   
    複製代碼
  • 分組數據:以指定列爲依據對全部選中行進行分組,從新劃分了行的顯示規則
    • 單列分組
      # 統計每一個分級下的電影數量
      SELECT 
      rating, COUNT(rating) AS 'Rating Count'
      FROM
      film
      GROUP BY rating
      複製代碼
    • columnlist中的全部列,要麼是GROUP BY子句中的列,要麼是在聚合函數中使用的列,由於全部內容都在組中出現,不在組中的內容沒有辦法處理,這種狀況下MySQL與其它兩種數據庫不一樣,它只會得出錯誤的結果,而不會報錯
    • 多列分組:組的概念能夠擴展,從而根據多列進行分組
      # 統計不一樣租金率下的不一樣分級電影的數量
      SELECT 
      rating, 
      rental_rate,
      COUNT(rating) AS 'Rating Count'
      FROM film
      GROUP BY rating, rental_rate    
      複製代碼
    • 在沒有分組的狀況下,聚合函數(SUMAVGMINMAXCOUNT)統計的是全部行的數據,在有分組的狀況下,這些聚合函數則僅會統計組內的數據,固然實際上也是最終顯示的表的每一行的聚合
    • GROUP BY子句中的columnlist順序沒有意義,但ORDER BY子句中的順序是有意義的,通常按照排序的優先順序來列出這些列會頗有幫助(也即SELECT中的columnlist與ORDER BY中的columnlist保持一致)
  • 基於分組應用查詢條件:WHERE子句中的查詢條件是針對單獨的行來應用的,若是存在GROUP BY分組,就須要使用HAVING關鍵字了
    # 查看分級中全部電影平均時長大於100分鐘的分級中電影的數量
    SELECT 
      rating AS '分級',
      COUNT(title) AS '電影數量',
      AVG(length) AS '平均時長'
    FROM
      film
    GROUP BY rating
    HAVING AVG(length) > 100
    ORDER BY 電影數量 DESC
    複製代碼
  • 至此,SELECT語句的通常格式以下:
    SELECT
      columnlist
    FROM
      tablelist
    WHERE
      condition
    GROUP BY
      columnlist
    HAVING
      condition
    ORDER BY
      COLUMNLIST
    複製代碼

用內鏈接來組合表

  • 關係型數據庫最重要的成就是可以把數據組織到任意多個相互關聯的表中,但同時這些又是彼此獨立的;人們能夠分析業務實體而後進行適當的數據庫設計,這樣就能夠具備最大的靈活性;關係型數據庫能夠以任何你想要的方式把表明業務實體的錶鏈接到一塊兒,從而實現「關係」
  • 相似「客戶」和「訂單」這樣兩個獨立的實體信息,至少應該要拆分到兩個表中(訂單表頗有可能須要繼續拆分紅多個表),可使用實體關係圖(entity-relationship diagram)來表示可視化地表示兩個表以及他們之間存在的隱性關係,實體(entity)指的是表,關係(relationship)指的是這些表中數據元素之間所畫的線
    實體關係圖
  • 內鏈接
    • 使用關鍵字INNER JOIN來指定想要鏈接的第二個表,使用ON來指定兩個表的共同列因爲共同列名稱是同樣的,因此須要在列名前面使用表名做爲命名空間來區分兩個表中獨立的列
      # 簡單地依據customer_id將顧客表和訂單表拼接到一個表中
      SELECT *
      FROM customers
      INNER JOIN orders
      ON customers.customer_id = orders.customer_id
      複製代碼
    • 內鏈接只會返回關聯的兩個表之間相匹配的數據,表在FROMINNER JOIN之間的順序僅會影響哪一個表的列先顯示,不會影響行的順序
    • SQL不是過程式語言,不會指定任務的前後順序,而只是指定須要的邏輯並讓數據庫內部機制去決定如何執行任務。
    • 僅使用FROMWHERE也能夠指定表的內鏈接,這是內鏈接的另外一種範式,但因其沒有顯示地表示出鏈接的邏輯,因此不推薦使用(因此其實INNER JOIN ON的惟一做用僅僅是表達語義而已)
      SELECT *
      FROM customers, orders
      WHERE customers.customer_id = orders.customer_id
      複製代碼
    • 能夠經過顯式地指定表的別名和列的別名(注意Oracle中表的別名與其餘兩個庫的區別,前文有說起),來去除內鏈接後的重複列或者只顯示須要的列,這是推薦的作法:
      SELECT
      c.customer_id AS 'Customer Id',
      c.first_name AS 'First Name',
      c.last_name AS 'Last Name',
      o.order_id AS 'Order Id',
      o.quantity AS 'Quantity',
      o.price_per_item AS 'Price'
      FROM customers AS 'c',
      INNER JOIN
      複製代碼

用外鏈接來組合表

  • SQL中表鏈接的默認類型就是內鏈接,因此能夠只使用JOIN來指定一個內鏈接
  • 外鏈接有三種類型:左鏈接LEFT OUTER JOIN,右鏈接RIGHT OUTER JOIN,全鏈接FULL OUTER JOIN,其中關鍵字OUTER並非必須的。
  • 因此總結起來有4種類型的鏈接:
    鏈接類型 全稱 簡寫 用途
    內鏈接 INNER JOIN JOIN 兩個表都是主表(primary table),共同列中全部的行都必須同時在這兩個表中才會被選中
    左鏈接 LEFT OUTER JOIN LEFT JOIN 左表爲主表,右表爲從表(secondary table),選中共同列中全部在主表中的行,無論它是否出如今從表
    右鏈接 RIGHT OUTER JOIN RIGHT JOIN 左表爲從表,右表爲主表,規則同左鏈接
    全鏈接 FULL OUTER JOIN FULL JOIN 兩個表都是從表,共同列中的行只要出如今任意一個表中都會被選中
  • 在實體關係圖中,單向箭頭表示表之間的鏈接是單向的,箭頭終點的表中有一列全部行都能在箭頭起點的表中找到,但反過來則不必定,好比,不是全部的客戶都有訂單,且一個客戶可能有多個訂單,但全部的訂單都會有客戶信息(甚至能夠說全部的訂單有且只有一個客戶信息),退貨信息與訂單的關係相似
  • 當鏈接主表和從表時,咱們須要主表中全部的行,即便在從表中的共同列沒有任何行與之匹配
  • 使用IS NOT NULLIS NULL來過濾空行或顯示空行
    # 過濾了沒有訂單的客戶和有退貨的訂單
    SELECT
    customers.first_name AS 'First Name',
    customers.last_name AS 'Last Name',
    orders.order_date AS 'Order Date',
    orders.order_amount AS 'Order Amt'
    FROM customers
    LEFT JOIN orders
    ON orders.customer_id = customers.customre_id
    LEFT JOIN refunds
    ON orders.order_id = refunds.order_id
    WHERE orders.order_id IS NOT NULL
    AND refunds.refund_id IS NULL
    ORDER BY customers.customer_id, orders.order_id
    複製代碼
  • 右鏈接與左鏈接惟一的不一樣就是主從表在關鍵字先後的位置,因此基本上沒有必要使用右鏈接,建議只使用左鏈接,由於人直覺上每每認爲先列出的表更爲重要
  • 當設計有多個表的複雜FROM子句時,建議僅使用關鍵字LEFT JOIN而且避免使用圓括號
  • 全鏈接會顯示全部行,即便沒有在其餘表中找到任何一個匹配。但在實際工做中不多會用到全鏈接,由於表之間的這種類型的關係是很是少見的。

自鏈接和視圖

  • 自鏈接:處理那些本質上是自引用的表(表中的一列指向本身的另外一列,好比員工表中的manager列指向本身的employee_id,員工的經理也是員工),爲其建立多個視圖
    • 可使用四種錶鏈接中的任意一種來實現自鏈接,惟二的區別就是ON子句中,非自鏈接的共同列來自兩個表,自鏈接的共同列來自同一個表,因此這時候須要在FROM關鍵字和JOIN關鍵字後爲該表各自建立一個別名用以在ON子句中進行區分
    # 列出personnel表中全部員工的經理名字
    SELECT
    employees.employee_name AS 'Employee Name',
    managers.employee_name AS 'Maneger Name'
    FROM personnel AS 'employees'
    LEFT JOIN personnel AS 'managers'
    ON employees.manager_id = managers._employee_id
    ORDER BY employee.employee_id
    複製代碼
  • 視圖
    • 視圖只是保存在數據庫中的SELECT語句,它不包含任何數據。
    • 隨着時間的流逝,訪問數據的需求會有所變化,但有時很難去從新組織數據庫中的數據以知足新的需求。視圖容許爲數據庫中已經存在的數據建立新的虛擬視圖(或理解爲虛擬的表)而無需從新組織數據,這爲咱們增長了始終能保持數據庫設計不斷更新的能力。
    • 由於視圖沒有保存物理數據,因此在視圖中不能包含ORDER BY子句
  • 建立視圖
    # 建立視圖的語法:
    CREATE VIEW view_name AS
    select_statement
    
    # 一個建立視圖的例子,注意不能有ORDER BY子句
    CREATE VIEW customers_orders_refunds AS
    SELECT
    customers.first_name AS 'First Name',
    customers.last_name AS 'Last Name',
    orders.order_date AS 'Order Date',
    orders.order_amount AS 'Order Amt'
    FROM customers
    LEFT JOIN orders
    ON orders.customer_id = customers.customre_id
    LEFT JOIN refunds
    ON orders.order_id = refunds.order_id
    WHERE orders.order_id IS NOT NULL
    AND refunds.refund_id IS NULL
    複製代碼
  • 引用視圖
    # 建立視圖
    CREATE VIEW view_name AS
    select_statement
    
    # 引用視圖
    SELECT * from view_name
    複製代碼
    • 當引用視圖中的列的時候,須要指定列的別名,而列的別名是在建立視圖時指定的
      # 建立視圖
      CREATE VIEW customers_view AS
      SELECT
      first_name AS 'First Name',
      last_name AS 'Last Name'
      FROM customers
      
      # 引用視圖中的列
      SELECT
      `First Name`,
      `Last Name`,
      FROM customers_view
      WHERE `Last Name` = 'Lopez'
      複製代碼
  • 視圖的優勢
    • 視圖能夠減小複雜度:將複雜的SELECT語句封裝爲一個視圖
    • 視圖能夠增長複用性:封裝那些老是相連的表
    • 視圖能夠正確地格式化數據:若是一個表中的某些數據老是須要格式化,能夠將其封裝到視圖中
    • 視圖能夠建立計算的列:若是須要一個含有大量的計算字段的表,也可將其封裝到視圖中
    • 視圖能夠用來從新命名列的名稱:若是一個表中的列名老是須要重命名,能夠將其封裝到視圖中
    • 視圖能夠建立數據子集:若是老是隻須要看到某個表的某些子集,能夠將它們封裝到不一樣的視圖
    • 視圖能夠用來增強安全性限制:若是一個表中的某些數據但願對某些用戶作訪問限制,可使用視圖將它們挑出來而後僅將視圖的權限給那些用戶而不是整個表的權限
  • 修改視圖:使用ALTER關鍵字修改一個已經建立的視圖,從新指定被封裝到其中的SELECT語句
    # 整個被封裝到視圖的select語句都須要從新指定
    ALTER VIEW view_name AS
    new_select_statement
    
    # 與Microsoft SQL Server和MySQL不一樣,Oracle在修改視圖以前,須要使用DROP VIEW view_name先刪除視圖
    複製代碼
    • 一樣,修改視圖與建立視圖同樣,只是修改了視圖的定義,它自己不會返回任何數據
  • 刪除視圖:使用DROP VIEW view_name來刪除視圖

子查詢

  • 包含在其餘查詢中的查詢叫作子查詢,子查詢能夠用在SELECTINSERTUPDATEDELETE語句
  • SELECT語句中子查詢能夠有三種用法:
    • 一個通常的SELECT語句格式以下:
      SELECT column_list
      FROM table_list
      WHERE condition
      GROUP BY column_list
      HAVING condition
      ORDER BY column_list
      複製代碼
    • 當子查詢是table_list的一部分時,它指定了一個數據源
    • 當子查詢時condition的一部分時,它成爲查詢條件的一部分
    • 當子查詢是column_list的一部分時,它建立了一個單個的計算的列
  • 使用子查詢指定數據源:把一個子查詢指定爲FROM子句的一部分時,它當即建立了一個新的數據源,並被當作一個獨立的表或視圖來引用,與視圖的區別是視圖是永久保存在數據庫中的,而子查詢只是臨時的
    # 使用子查詢指定數據源的通常格式
    SELECT column_list
    FROM [table_list]
    [JOIN] subquery
    AS custom_subquery_name
    
    # 從address表,city表和country表中列出五個地址對應的城市和國家
    SELECT 
      address AS 'Address',
      city AS 'City',
      country AS 'Country'
    FROM address
    LEFT JOIN(
      SELECT 
          city.city,
          city.city_id,
      	country.country,
          country.country_id
      FROM city
      LEFT JOIN country
      ON city.country_id = country.country_id
    ) AS city_and_country ON address.city_id = city_and_country.city_id
    ORDER BY address
    LIMIT 5
    複製代碼
  • 使用子查詢指定查詢條件:把一個子查詢指定爲WHERE子句中IN操做符的右值,能夠以更復雜的邏輯來爲IN操做符建立一個可選列表;注意,當子查詢用於指定查詢條件時,僅能返回單一的列
    # 使用子查詢指定查詢條件的通常格式
    SELECT column_list
    FROM table_list
    WHERE column IN subquery
    
    SELECT column_list
    FROM table_list
    WHERE subquery match_some_comdition
    
    # 列出全部使用現金支付的客戶名稱
    SEELCT customer_name AS 'Customer Name'
    FROM costomers
    WHERE customer_id IN
    (
      SELECT customer_id
      FROM orders
      WHERE order_type = 'cash'
    )
    
    # 列出訂單金額少於20美圓的客戶列表
    SELECT customer_name AS 'Customer Name'
    FROM customers
    WHERE
    (
      SELECT SUM(orderAmount)
      FROM orders
      WHERE customers.customer_id = orders.customer_id
    ) < 20
    複製代碼
  • 使用子查詢做爲計算列:把一個子查詢做爲column_list中的一項,將其用做一個計算的列
    # 使用子查詢做爲計算列的通常格式
    SELECT column_list,
    subquery_result AS 'Result Alia'
    FROM table_list
    
    # 查詢客戶及其訂單數量
    SELECT
    customer_name AS 'Customer Name',
    (
      SELECT COUNT(order_id)
      FROM orders
      WHERE customers.customer_id = orders.customer_id
    ) AS 'Number of Orders'
    FROM customers
    ORDER BY customers.customer_id
    複製代碼
  • 關聯子查詢:沒法獨立運行的子查詢爲關聯子查詢,能夠獨立運行的子查詢爲非關聯子查詢。非關聯子查詢徹底獨立與外圍查詢語句,只會計算和執行一次,而關聯子查詢須要針對返回的每一行逐行計算,且每次執行子查詢的時候獲得的結果可能都不同,上文中查詢客戶及其訂單數量中的子查詢即爲關聯子查詢,它使用了外圍查詢的數據來源customers
  • EXISTS操做符:用於肯定一個關聯子查詢中是否存在數據
    # 查詢下過訂單的用戶
    SELECT
    customer_name AS 'Customer'
    FROM customers
    WHERE EXISTS
    (
      SELECT * FROM orders
      WHERE customers.customer_id = orders.customer_id
    )
    複製代碼

集合邏輯

在前文中,鏈接JOIN能夠未來自兩個表的列組合到一個表中,子查詢則是將一條SELECT語句的結果提供給第另外一條SELECT語句使用。然而有時候咱們但願未來自兩個表的行組合到一個表中,這時候就須要使用SQL中的集合邏輯UNION,來作合併查詢。數據庫

  • UNION-合併兩條SELECT語句,選取在A或B中的數據,若是同時存在在A或B中,僅顯示一條
    SELECT
    order_date AS 'Date',
    'order' AS 'Type',
    order_amount AS 'amount'
    FROM orders
    WHERE custom_id = 2
    
    UNION
    
    SELECT
    return_date AS 'Date',
    'return' AS 'type',
    return_amount AS 'amount'
    FROM returns
    WHERE custom_id = 2
    
    ORDER BY date
    
    複製代碼
    使用UNION須要遵照3個規則(實際就一條規則:相同列):
    • 兩個SELECT語句中的列的數量必須相等
    • 兩個SELECT語句中的列排列順序必須相同
    • 兩個SELECT語句中的列數據類型必須相同
  • UNION ALL-合併兩條SELECT語句,選取在A或B中的數據,即便同時存在在A或B中,都將顯示在結果中
    SELECT
    DISTINCT
    order_date AS 'Date'
    FROM orders
    
    UNION ALL
    
    SELECT
    DISTINCT
    return_date AS 'Date'
    FROM returns
    ORDER BY Date
    
    # UNION 確保來自兩個表的行沒有重複數據,但 UNION ALL 容許來自兩個表的行能夠有相同數據
    # DISTINCT 確保來自同一個表(或者說同一個SELECT語句)的行沒有重複數據
    # 因此上面的語句選取的數據可能會存在重複數據,但重複的數據並不來自兩個表而是來自同一個表,而且僅會重複一次
    複製代碼
  • INTERSECT-合併兩條SELECT語句,選取同時出如今A和B中的行(MySql不支持該操做符)
    SELECT order_date AS 'Date'
    FROM orders
    
    INTERSECT
    
    SELECT return_date As 'Date'
    FROM returns
    
    ORDER BY Date
    複製代碼
  • EXCEPT-合併兩條SELECT語句,選取僅出如今A或僅出如今B中的的數據(MySql和Oracle不支持該操做符,但Oracle提供了等價的MINUS操做符)
    SELECT order_date AS 'Date' FROM orders
    
    EXCEPT
    
    SELECT return_date AS 'Date' FROM returns
    
    ORDER BY Date
    複製代碼

存儲過程和參數

到目前爲止,前文全部的SQL語句都是單獨使用,然而不少時候,你會但願SQL語句可以像函數同樣,定義一次,而後重複調用,而且可使用參數來增長靈活性。這時,你就可使用存儲過程來實現這一目的。express

  • 建立存儲過程:建立存儲過程不會執行任何內容,只是直接建立了一個過程,以便後續執行它。與表和視圖同樣,建立好的存儲過程在管理工具中是能夠查看的
    -- Microsoft SQL Server
    CREATE PROCEDURE ProcedureName (OptionalPrameterDeclarations)
    AS
    BEGIN
    SQLStatements
    END
    
    -- MySQL
    DELIMITER $$ -- 規定END語句使用的分隔符,默認爲分號
    CREATE PROCEDURE ProcedureName (OptionalPrameterDeclarations)
    BEGIN
    SQLStatements; -- 每一條SQL語句都必須使用分號分隔,即便只有一條
    END$$
    DELIMITER ; -- 將分隔符改回分號
    複製代碼
  • 存儲過程的參數:例如存儲一個選取特定用戶的SQL過程,可使用參數來指定用戶的ID
    -- Microsoft SQL Server
    CREATE PROCUDURE CustomerProcudure (@custId INT)
    AS
    BEGIN
      SELECT * FROM customers
      WHERE customer_id = @custId
    END
    
    -- MySQL
    DELIMITER $$
    CREATE PROCEDURE CustomerProcudure (custId INT)
    BEGIN
      SELECT * FROM customers
      WHERE CUSTOMER_ID = custId;
    END
    DELEMITER ;
    複製代碼
  • 執行存儲過程
    -- Microsoft SQL Server
    EXEC CustomerProcudure @custId = 2
    
    -- MySQL
    CALL CustomerProcudure (2)
    複製代碼
  • 修改和刪除存儲過程:在Microsoft SQL Server中,修改過程和建立過程幾乎同樣,只須要把CREATE關鍵字替換爲ALTER關鍵字便可;然而在MySQL中,雖然也存在ALTER命令,但它的功能十分簡單,因此通常咱們選擇先刪除存儲過程而後再從新建立
    -- 刪除存儲過程
    DROP PROCEDURE ProcedureName
    複製代碼
  • 函數與存儲過程的兩點區別
    • 存儲過程能夠有任意數目的輸出,而函數只有單一的返回值
    • 只能經過調用程序來執行存儲過程,而函數能夠在SQL語句中使用

修改數據

  • 修改策略:使用「軟刪除(使用表中特定的列來標記該行數據是否有效)」技術替代真正的刪除;插入新行時在特定列中標記準確的插入日期和時間以便出錯時對其進行刪除;使用單獨的表來保存事務所要更新的數據一般是明智的選擇。請永遠記住,SQL中沒有撤銷命令。
  • 插入數據:使用INSERT命令來插入指定數據,注意不須要爲自增型的列指定數據,數據庫會自動處理它;另外,Oracle不容許一次插入多行數據,須要分開書寫
    • 插入INSERT語句中指定的具體數據
      -- 向customer表插入兩條新數據
      INSERT INTO customers
      (first_name, last_name, state) -- 只要列名是正確的,它們的順序無所謂
      -- 當這裏的列名順序與數據庫中的物理順序一致時可省略它們,但強烈不建議這麼作
      VALUES
      ('Virginia', 'Jones', 'OH'), -- VALUES關鍵字後的數據列,要與INSERT INTO後的列相對應
      ('Clark', 'Woodland', 'CA')
      複製代碼
    • 插入用一條SELECT語句指定的數據
      -- 將customer_transaction中的RI州的用戶插入到customer表中
      INSERT INTO customer
      (first_name, last_name, state)
      SELECT
      fn, ln, state -- 這裏選中列的順序須要與INSERT INTO 語句中的順序一致
      FROM customer_transactions
      WHERE state = 'RI'
      複製代碼
  • 刪除數據:使用DELETE命令來刪除一條數據,一般是一整行(刪除某行中的列沒有意義,那屬於修改數據的範疇)
    -- 刪除數據的通常寫法
    DELETE
    FROM table_name
    WHERE conditions
    
    -- 可使用SELECT語句來驗證刪除結果
    SELECT
    COUNT (*) -- 使用聚合函數COUNT來統計被刪除數據的數量以確認是否所有都被刪除了
    FROM table_name
    WHERE conditions
    
    -- 清空一個表中的全部數據,可使用TRUNCATE TABLE語句
    TRUNCATE TABLE customers
    -- 上面的語句與下面的DELETE語句效果基本相同
    DELETE FROM customers
    -- 惟一不一樣在於,TRUNCATE TABLE語句重置了自增列,而DELETE語句沒有
    複製代碼
  • 更新(修改)數據:刪除數據只須要指定刪除的行便可,但更新數據是針對具體行中的具體列,因此須要首先指定更新哪些列,而後指定更新這些列中的哪些行
    • 使用指定的具體數據更新數據
      -- 更新數據的通常格式
      UPDATE table
      SET
        column1 = expression1,
        column2 = expression2
        -- repeat any number of time
      WHERE conditions -- 若是沒有指定行,該句會把全部行的指定列都更新一次
      複製代碼
    • 使用子查詢中的數據修改數據(使用一個表中的數據來更新另外一個表中的數據)
      -- 通常格式
      UPDATE table -- 指定要更新的表
      SET table.column_1 = -- 指定須要更新的列1
        (
          SELECT another_table.column_1 -- 子查詢從另外一表中獲取數據,並經過主鍵(也但是其它)來進行匹配
          FROM another_table
          WHERE another_table.auto_increment_primary_key = table.auto_increment_primary_key
        )
      SET table.column_2 = -- 指定須要更新的列2
        (
          SELECT another_table.column_2
          FROM another_table
          WHERE another_table.auto_increment_primary_key = auto_increment_primary_key.column_2
        )
      WHERE EXISTS -- 指定須要更新的行,使用子查詢指定只更新table中存在於another_table中的行
        (
          SELECT *
          FROM another_table
          WHERE another_table.auto_increment_primary_key = table.auto_increment_primary_key
        )
      複製代碼

維護表

  • 回顧SQL語言的三種組成部分:數據操縱語言(Data Manipulation Language,DML,對數據庫中或者更詳細地說是表中的數據進行增刪改查操做)、數據定義語言(Data Definition Language,DDL,對數據庫中的表及其索引進行增刪改查)、和數據控制語言(Data Control Language,DCL,維護數據庫安全)。本章主要講述DDL,但前文也已經用到過DDL,視圖VIEW、過程PROCEDURE須要用到的都是DDL
  • 添加或修改表和索引的SQL語句很複雜,可是咱們無需瞭解細節,數據庫軟件一般提供了圖形化的工具來修改表的結構,而不必定須要使用SQL語句
  • 表屬性:表(Table)是數據庫中最重要的對象,數據庫中全部數據物理地存儲在表中,沒有了表數據庫中的一切也就沒有意義了。前文已經介紹過一些表的屬性,主鍵、外鍵、數據類型、自增型列等等
    • 表的列
      • 列名:表中的每一個列都必須有惟一的列名
      • 數據類型:決定列能夠包含什麼樣的數據
      • 是否自增型:表中每增長一行,該列會以升序序列自動賦值(術語auto-increment是MySQL中的的特定用法,Oracle沒有自增型屬性)
      • 默認值
  • 主鍵和索引
    • 主鍵:只能指定一個列做爲主鍵,目的是保證這個列包含惟一值(因此不容許它們包含NULL值);實際上主鍵能夠跨越多個列,叫作複合主鍵(當但願使用電影名稱列來做爲主鍵時可能會存在重複名稱, 這時可使用電影名稱+年份兩個列做爲複合主鍵來惟一地定義每部電影)
    • 索引:索引是一種物理結構,目的是當SQL語句中包含該列的時候,能夠加速數據檢索,缺點是須要更多的磁盤空間,而且會減慢更新數據時的速度
  • 外鍵:
    • 外鍵定義:外鍵是從一個表中的一個列到另外一個不一樣的表中的列的直接引用,含有外鍵的表爲「子表」,被外鍵引用的表被稱爲「父表」
    • 外鍵級聯(Cascade):當父表有更新或刪除時,會自動更新或刪除子表中的關聯行
    • Set Null:當父表有更新或刪除時,若是影響到子表,是否把子表中關聯行的外鍵設置爲NULL
  • 建立表:使用CREATE TABLE語句來建立表及其屬性(列),不一樣數據庫之間存在差別:
    -- Microsoft SQL Server
    CREATE TABLE my_table
    (
      column_1 INT IDENTITY (1, 1) PRIMARY KEY NOT NULL, -- 列名column_1,INT類型,自增型,主鍵,不能爲NULL
      column_2 NOT NULL REFERENCES related_table (first_column), -- 列名column_2,INT類型,不能爲NULL,外鍵,關聯到related_table表的first_column列
      column_3 VARCHAR (25) NULL, -- 列名column_3,VARCHART類型,能夠是NULL
      column_4 FLOAT NULL DEFAULT (10) -- 列名column_4,FLOAT類型,能夠是NULL
    )
    
    
    -- My SQL
    CRAET TABLE my_table
      column_1 INT AUTO_INCREMENT PRIMARY KEY NOT NULL,
      column_2 INT NOT NULL,
      column_3 VARCHAR (25) NULL,
      column_4 FLOAT NULL DEFAULT 10 NULL,
      CONSTRAINT FOREIGN KEY (column_2) REFERENCE 'related_table' (first_column) -- 指定外鍵
      
    -- Oracle
    CREATE TABLE my_table
    (
      column_1 INT PRIMARY KEY NOT NULL, -- Oracle不容許有自增型的列
      column_2 INT NOT NULL,
      column_3 VARCHAR2 (25) null,
      column_4 FLOAT DEFAULT 10 NULL
      CONSTRAINT "foreign_key" FOREIGN KEY (column_2) REFERENCES related_table (first_column)
    )
    複製代碼
    使用ALTER TABLE語句修改表的具體屬性,該語句的複雜性及數據庫差別巨大,這裏再也不展開;使用DROP TABLE table_name語句來刪除一個表
    -- 修改表
    ALTER TABLE my_table
    DROP COLUMN column_3
    
    -- 刪除表
    DROP TABLE my_table
    複製代碼
  • 建立索引:使用CREATE INDEX語句,用來在建立表以後建立索引,使用ALTER INDEX語句來添加和修改索引
    -- 建立索引
    CREATE INDEX index_2
    ON my_table (column_4)
    
    -- 刪除索引
    DROP INDEX index_2
    ON my_table
    複製代碼

數據庫設計原理與顯示數據的策略(略)

  • 關係型數據庫是一個數據集合,數據庫中的表以某些方式相互關聯。
  • SQL語句僅僅是使用數據庫的工具,數據庫設計則是另一個更爲重要的話題。《SQL初學者指南》中對這個話題進行了簡單的歸納:規範化及其替代方法,這裏將再也不展開。
  • 關於這個話題建議閱讀另外的一些書籍:《SQL必知必會》、《高性能MySQL》
相關文章
相關標籤/搜索