SQL学习

# SQL学习
## 基础(90%可以用到其它DBMS)(database management system:数据库管理系统、关系型数据库)
- 增删改查:
  - inserting data:
  - deleting data:
  - updating data:
  - retrieving data:
- 汇总数据(summarizing data)


- 使用子查询来复杂查询(writing complex queries)
  

- 基本内置函数(built-in Functions)  
  - 用于和数字、日期、文本数据打交道。


- 创建视图(views)


- 存储过程和函数(Stored procedures)


- 简单子句:
  - SELECT:
    - 可以使用算数表达式 
  - SELECT customer_id, first_name(选择的是列)
    FROM customers 
    -- WHERE customer_id = 1 (-- 为注释)
    ORDER BY first_name
    - FROM、WHERE、ORDER BY三个字句都是可选的 ,但是顺序不可以改变
    - 列名不能有空格,要加空格的话加 " "或‘ ‘
  - DISTINCT关键字:
    - 去除重复项(去重) 
  - AS:
    - 起别名  
  - WHERE:
    - !=相同于<>  
    - 可以使用算数表达式
  - AND/OR/NOT(与或非):
    - AND优先级高于OR  
  - IN:
    - 同一系列值比较一个属性
      - 例:WHERE state IN(‘VA‘, ‘FL‘, ‘GA‘) 等同于 WHERE state = ‘VA‘ OR state = ‘FL‘ OR state = ‘GA‘
  - BETWEEN:
    - 取一个闭区间
      - 例: WHERE points BETWEEN 1000 AND 3000 等同于 WHERE points >= 1000 AND points <= 3000 
  - 如何检索遵循特定字符串模式的行:
    - LIKE:
      - WHERE last_name LIKE ‘%b%‘ (b不区分大小写,%表示任意字符)
      - WHERE last_name LIKE ‘_y‘  (_表示一个字符)
      - WHERE address LIKE ‘%trail%‘ OR
	          address LIKE ‘%avenue%‘
  - REGEXP:
    - 正则表达式regular expression缩写 
    - 搜索字符串时极其强大
    - "^"表示字符串的开头
      - WHERE last_name REGEXP ‘^my|se‘ 
    - "$"表示字符串的末尾
      - WHERE last_name REGEXP ‘ey$|on$‘
    - "|"表示: 或(OR)多个搜寻模式
      - WHERE first_name REGEXP ‘elka|ambur‘
    - "[]":匹配任意在括号里列举的单子符
      - WHERE last_name REGEXP ‘b[ru]‘
    - "[a-f]":闭区间a到f的任意字母
  - 如何搜索缺失了属性的记录(值为null的记录):
    - IS NULL 
      - WHERE  shipped_date IS NULL
  - ORDER BY:
    - 默认列升序排序 
    - DESC(降序)  ORDER BY first_name DESC
    - 可以用别名:
      - SELECT * 
        FROM order_items
        WHERE order_id = 2
        ORDER BY quantity * unit_price DESC 
      - SELECT * , quantity * unit_price AS total_price
        FROM order_items
        WHERE order_id = 2
        ORDER BY total_price DESC  
  - 如何限定查询返回的记录:
    - LIMIT
      - LIMIT字句永远要放在最后 
      - LINIT 3 (显示前三行记录)
      - 可以用于分页
        -  --page 1: 1-3
           --page 2: 4-6
           --page 3: 7-9
           LIMIT 6, 3(6为偏移量,跳过前六个记录,获取3个记录)


- 连接:
  - 内连接:
    - INNER JOIN  (INNER关键字 可写可不写)
    - SELECT order_id, order.customer_id, first_name, last_name
      FROM orders
      JOIN customers ON orders.customer_id = customers.customer_id
    - 给表起别名:
      SELECT order_id, o.customer_id, first_name, last_name
      FROM orders o
      JOIN customers c ON o.customer_id = c.customer_id

    - 跨数据库连接:
      - 要给不在当前数据库的表加 前缀(数据库名称)
        - USE sql_inventory;
            SELECT *
            FROM sql_store.order_items oi
            JOIN sql_inventory.products p
                 ON oi.product_id = p.product_id

    - 自连接:
      - USE sql_hr;
        SELECT e.employee_id,
               e.first_name,
               m.first_name AS manager
        FROM employees e
        JOIN employees m
             ON e.reports_to = m.employee_id
    - 多表连接:
      - USE sql_store;
        SELECT 
            o.order_,
            o.order_date,
            c.firet_name,
            c.last_name,
            os.name AS status
        FROM orders o
        JOIN customers c
            ON o.customer_id = c.customer_id
        JOIN order_statuses os
            ON o.status = os.order_status_id
   
    - 复合连接:
      - 用于有复合主键表的连接
        - SELECT *
          FROM order_items oi
          JOIN order_items_notes oin
               ON oi.order_id = oin.order_id
               AND oi.product_id = oin.product_id
  - 隐式连接语法 :
    - 不建议用,如果忘了WHERE字句会变成交叉连接 
    - SELECT *
      FROM orders o, customers c
      WHERE o.customer_id = c.customer_id
      等同于
      SELECT *
      FROM orders o
      JOIN customers c
           ON o.customer_id = c.customer_id
  - 外连接: 
    - OUTER JOIN(OUTER可省去)
    - FROM 里的是左表,JOIN里的是右表
    - 尽量使用左连接
    - 左连接(LEFT JOIN):
      - 所有左表的记录,会被返回(不管条件ON正确不正确)。
        - SELECT  p.product_id,
		          name,
                  oi.quantity
          FROM products p
          LEFT JOIN order_items oi
	                ON p.product_id = oi.product_id 
    - 右连接(RIGHT JOIN):
      - 所有右表的记录,会被返回(不管条件ON正确不正确)。 

    - USING
      - ON条件两个表中名称相同时使用
      - 内连接和外连接都可以使用
      - USING(customer_id)
        等同于 ON o.customer_id = c.customer_id

  - 自然连接:
    - NATURAL JOIN 
    - 危险:
      - 没有ON条件,基于共同的列连接 
      - 让数据库引擎自己猜该怎么连接,我们无法控制它 (不建议使用)
  - 交叉连接:
    - CROSS JOIN 
    - 用途:
      - 例:衣服的各种尺寸和各种颜色 
    - 结合或者连接第一个表的每条记录和第二个表的每条记录 
      - SELECT *
  
        FROM customers c
        CROSS JOIN products p(显示连接更清楚)
        等同于
        FROM customers c,products p(隐式连接)
        顾客表里的每条记录都会和产品表里的每条记录结合

- 结合:
  - UNION:
    - 合并的列数必须相同,否则会报错
    - 合并后的列名取于第一个 
  - 合并多个查询的结果 
    - SELECT customer_id,
		first_name,
        points,
        ‘Bronze‘ AS type
      FROM customers
      WHERE points < 2000
      UNION
      SELECT customer_id,
		first_name,
        points,
        ‘Silcer‘ AS type
      FROM customers
      WHERE points BETWEEN 2000 AND 3000 
      UNION
      SELECT customer_id,
		first_name,
        points,
        ‘gold‘ AS type
      FROM customers
      WHERE points > 3000
      ORDER BY first_name 
  - 行连接:
    - 合并多段查询的记录 
      - 有效的和无效的相连接 
  - 不同表和相同表都可以使用

- 插入、更新、删除:
  - 插入:
    - 单行: 
      - INSERT INTO customers(列名)
        VALUES(
          值
        )  
    - 多行:
     -  INSERT INTO customers(列名)
        VALUES(),
              (),
              ()  
    - 插入分层行:
      - 往多表插入数据
        - 父子关系表
    - 复制数据:
      - 复制的新表没有主键和自增 
      - CREATE TABLE orders_archived AS
        SELECT * FROM orders(子查询)
        子查询实例:
          - INSERT INTO orders_archived
            SELECT * FROM orders WHERE order_date < ‘2019-01-01‘

          - CREATE TABLE invoices_archived AS
            SELECT i.invoice_id,
  		        i.number,
                  c.name AS client,
                  i.invoice_total,
                  i.payment_total,
                  i.invoice_date,
                  i.payment_date,
                  i.due_date
            FROM invoices i
            JOIN clients c 
            USING (client_id)
            WHERE payment_date IS NOT null

  - 更新:
    - UPDATE 
    - 更新单行:
      - UPDATE invoices
        SET payment_total = 10, payment_date = ‘2019-01-05‘(指定一列或者多列的新值)
        WHERE invoice_id = 1 (需要被更新的记录)
    - 更新多行:
      - UPDATE customers
        SET points = points + 50
        WHERE birth_date < ‘1990-01-01‘customers 
    - 使用子查询(很强大):
      - 子查询(另一段SQL语句里的选择语句)    
      - 可以作为WHERE语句里的筛选条件
        - UPDATE invoices
          SET 
             payment_total = invoice_total * 0.5,
             payment_date = due_date
          WHERE client_id  IN
                        (  SELECT client_id
                           FROM clients
                           WHERE state IN (‘CA‘, ‘NY‘)
                        )
        - UPDATE orders
          SET commments = ‘gold customer‘
          WHERE customer_id IN

            (SELECT customer_id
            FROM customers
            WHERE points > 3000)

  - 删除:
    - DELETE FROM invoices
      WHERE client_id = (
          SELECT *
          FROM clients
          WHERE name = ‘Myworks‘
      )
  - 恢复数据库:
    - file里sql-script

- 数据汇总:
  - 聚合函数:
    - 使用了聚合函数应该要进行分组 
    - 只运行非空值 
    - SELECT
	MAX(invoice_total) AS highest,
    MIN(invoice_total) AS lowest,
    AVG(invoice_total) AS average,
    SUM(invoice_total) AS total,
    COUNT(invoice_total) AS number_of_invoices
    COUNT(*) AS total_records (全部记录,包含非空值)
    FROM invoices   
    - DISTINCT(去重取唯一的)
      - COUNT(DISTINCT client_id) AS total_records

    - SELECT
        ‘First half of 2019‘ AS data_range,
        SUM(invoice_total) AS total_sales,
        SUM(payment_total) AS total_payments,
        SUM(invoice_total - payment_total) AS what_we_expect
    FROM invoices
    WHERE invoice_date
        BETWEEN ‘2019-01-01‘ AND ‘2019-06-30‘
    UNION    
    SELECT
        ‘Last half of 2019‘ AS data_range,
        SUM(invoice_total) AS total_sales,
        SUM(payment_total) AS total_payments,
        SUM(invoice_total - payment_total) AS what_we_expect
    FROM invoices
    WHERE invoice_date
        BETWEEN ‘2019-06-31‘ AND ‘2019-12-31‘
    UNION
    SELECT
        ‘all of 2019‘ AS data_range,
        SUM(invoice_total) AS total_sales,
        SUM(payment_total) AS total_payments,
        SUM(invoice_total - payment_total) AS what_we_expect
    FROM invoices
    WHERE invoice_date
        BETWEEN ‘2019-01-01‘ AND ‘2019-12-31‘
    - GROUP BY(分组查询):
      - SELECT
            client_id,
            SUM(invoice_total) AS total_sales
        FROM invoices
        GROUP BY client_id
        ORDER BY total_sales DESC

    - HAVING:
      - 可以在分组(GROUP UP)后筛选--充当WHERE
    - WITH ROLLUP(汇总数据): 
      - GROUP BY client_id WITH ROLLUP
      - 只在MYSQL中可以使用

- 复杂查询:
  - 子查询(内查询)是在另一个sql语句中的选择语句
    - SELECT *
        FROM products
        WHERE unit_price > 
        (SELECT unit_price
        FROM products
        WHERE product_id = 3)

    - IN运算符写子查询:
      - SELECT *
        FROM products
        WHERE product_id NOT IN
        (
        SELECT DISTINCT product_id
        FROM order_items
        )
      - SELECT *
        FROM clients
        WHERE client_id NOT IN
        (
        SELECT DISTINCT client_id
        FROM invoices
        ) 

  - 子查询vs连接 
    - 用JOIN重写子查询或取反 
      - 用子查询:
        - SELECT customer_id,
                first_name,
                last_name
        FROM customers
        WHERE customer_id IN 
        (
        SELECT customer_id
        FROM order_items oi
        JOIN orders o
        USING (order_id)
        WHERE product_id = 3
        )
      - 用连接:(优选)
        - SELECT DISTINCT customer_id,
				first_name,
				last_name
        FROM customers c
        JOIN orders o USING (customer_id)
        JOIN order_items oi USING (order_id)
        WHERE oi.product_id = 3 
  - ALL关键字:
    - 用于子查询返回一列值,和每一个比较都要符合(大于最大,小于最小)
    - 可以和聚合函数max、min转换
      - SELECT *
        FROM invoices
        WHERE invoice_total > ALL (
                SELECT invoice_total
                FROM invoices
                WHERE client_id = 3
        )
      - SELECT *
        FROM invoices
        WHERE invoice_total > (
                SELECT MAX(invoice_total)
                FROM invoices
                WHERE client_id = 3
        ) 
  - ANY:
    - = ANY相当于IN 
  - 相关子查询:
    - 父表里的每一个员工,它都会执行一次子查询 
    - 执行的慢 、占用更多存储
    - 子查询和父查询存在相关性  --子查询引用了父查询里表的别名
      - SELECT *
        FROM employees e
        WHERE salary > (
            SELECT AVG(salary)
            FROM employees
            WHERE office_id = e.office_id
        ) 
  - EXISTS运算符:
    - SELECT *
    FROM products 
    WHERE product_id NOT IN (
            SELECT product_id
            FROM order_items
    )

    - SELECT *
    FROM products p
    WHERE NOT EXISTS (
            SELECT product_id
            FROM order_items
            WHERE product_id = p.product_id
    )

  - SELECT子句中获得子查询:
    - 表达式中不能使用列的别名
      - SELECT 
            client_id,
            c.name,
            (SELECT SUM(invoice_total)
                FROM invoices
                WHERE client_id = c.client_id) AS total_sales,
            (SELECT AVG(invoice_total)
                FROM invoices) AS average,    
            (SELECT total_sales - average) AS difference
        FROM clients c 

  - FROM子句中使用子查询:
    - 变得复杂,可以作为视图存储在数据库中 
    - SELECT *
        FROM(
            SELECT 
                client_id,
                c.name,
                (SELECT SUM(invoice_total)
                    FROM invoices
                    WHERE client_id = c.client_id) AS total_sales,
                (SELECT AVG(invoice_total)
                    FROM invoices) AS average,    
                (SELECT total_sales - average) AS difference
            FROM clients c
        ) AS sales_summary
        WHERE total_sales IS NOT NULL

- 内置函数:
  - 用于应对数值、日期时间、字符串值
    - 数值: 
      - ROUND(5.7366,2) 四舍五入函数(值,精确精度)
      - TRUNCATE()--截断函数
      - CEILING(2.6)--取整-返回大于或等于这个数的最小整数
      - FLOOR(2.6)--地板函数-返回小于或等于这个数字的最大整数
      - ABS(-2.6)--计算绝对值
      - RAND()--用来生成0-1之间的随机浮点数
    - 字符串:
      - LENGTH(‘asdas‘)--得到字符串中的字符数 
      - UPPER(‘asdas‘)--将字符串转化成大写字母
      - LOWER(‘ASDAS‘)--将字符串转化成小写字母
      - 去除字符串中不需要的空格:
        - LTRIM(左修整-left trim) LTRIM(‘  ASDAD‘)--去除字符串左侧的空白字符
        - RTRIM(‘ASDAS    ‘)--去除字符串左侧的空白字符
        - TRIM(‘  asd   ‘)--去除所有空白字符
      - LEFT(‘sdfdaadasfrs‘,4)--返回字符串左侧的几个字符 
      - RIGHT(‘asdasdqasd‘, 6)--返回字符串右侧的几个字符 
      - SUBSTRING(‘afdasdasa‘,起始位置,长度)--字符截取函数--可以得到一个字符串中任何位置的字符
        - 第三个参数为可选,默认为返回到末尾 
      - LOCATE(‘b‘,‘sadasfbkl‘ )-- 返回第一个字符或者一串字符匹配位置
        - 不区分大小写,没有返回0
      - REPLACE(‘zhangziyang‘, ‘yang‘, ‘hai‘ )--替换一段字符串  
      - CONCAT(‘first‘, ‘LAST‘)--串联字符串
    - 日期和时间:
      - NOW()--电脑当前日期和时间
      - CURDATE()--当前日期
      - CURTIME()--当前时间
      - YEAR(NOW())--MONTH、DAY、HOUR、MINUTE、SWCOND截取当前年、月、日、时、分、秒--返回整数值
      - DAYNAME(NOW())--MONTHNAME获取星期数、月份数--返回字符串格式
      - EXTRCT(YEAR FROM NOW())---返回2019---(想获取的单位 FROM 时间日期值)--返回字符串格式
      - 例:返回当前年下的订单
        - SELECT *
          FROM orders
          WHERE YEAR(order_date) = YEAR(NOW())
  
      - 日期格式函数:DATE_FORMAT()
        - DATE_FORMAT(NOW(), ‘%M %d %Y‘)
      - 时间格式函数:TIME_FORMAT()
        - TIME_FORMAT(NOW(), ‘%H:%i %p‘)
      - 计算日期和时间的函数:
        - DATE_ADD(NOW(), INTERVAL 1/-1 DAY/YEAR)--增加或减少
        - DATEDIFF(‘2021-07-04‘,‘2021-06-17‘)--计算日期间隔,返回天数--来上海了17天
        - TIME_TO_SEC()---返回从零点计算的秒数--TIME_TO_SEC(‘09:02‘) - TIME_TO_SEC(‘09:00‘)
  - IFNULL:
    - SELECT 
        order_id,
        IFNULL(shipper_id, ‘未分配‘) AS shipper---如果shipper_id为空(null),改为未分配---替换空值
      FROM orders  
  - COALESCE(shipper_id, comments, ‘未分配‘):---如果如果shipper_id为空(null),返回comments列的值,
        如果comments列的值为空,shipper_id改为未分配---提供一堆值,返回第一个非空值
    - 例:SELECT 
            CONCAT(first_name,‘ ‘, last_name) AS customer,
            COALESCE(phone,‘UNKNOW‘) AS phone
          FROM customers
  - IF函数: 用来检验--允许单一的表达式
    - IF(表达式,为真时返回的值,为假时返回的值) ---可以返回任何值
        - 例:SELECT 
                order_id,
                order_date,
                IF(YEAR(order_date) = YEAR(NOW()), ‘活跃‘, ‘归档‘) AS category
              FROM orders
        - 例:SELECT 
                    product_id,
                    name,
                    COUNT(*) AS orders,
                    IF(COUNT(*) > 1, ‘many times‘, ‘once‘) AS Frequence
              FROM products p
              JOIN order_items oi USING(product_id)
              GROUP BY product_id, name

  - CASE运算符:
    - CASE
          WHEN          THEN      --每个when子句都有一个测试表达式,满足就返回THEN后的值
          END                     --关闭通道
      -例: 
        - SELECT 
              order_id,
              CASE
                  WHEN YEAR(order_date) = YEAR(NOW()) THEN ‘ACTIVE‘
                  WHEN YEAR(order_date) = YEAR(NOW()) - 1 THEN ‘LAST_year‘
                  WHEN YEAR(order_date) < YEAR(NOW()) - 1 THEN ‘Archived‘
                  ELSE ‘Future‘
              END AS category
          FROM orders 
        - SELECT 
            CONCAT(first_name, ‘ ‘, last_name)	AS customer,
            points,
            CASE
                WHEN points > 3000 THEN ‘Gold‘
                WHEN points >= 2000 THEN ‘Silver‘
		        ELSE ‘Bronze‘
	        END AS category
            FROM customers
            ORDER BY points DESC

- 视图:
  - 解决繁琐的查询---相当于一张虚拟表
  - 把查询或子查询存到视图里
  - 视图不存储数据,数据存在表中
  - CREATE VIEW      AS
    - 例:
      - CREATE VIEW sales_by_client AS
        SELECT
            c.client_id,
            c.name,
            SUM(invoice_total) AS total_sales
        FROM clients c
        JOIN invoices i USING (client_id)
        GROUP BY client_id, name 

      - CREATE VIEW clients_balance AS
        SELECT
            c.client_id,
            c.name,
            SUM(i.invoice_total - i.payment_total) AS blance
        FROM clients c
        JOIN invoices i USING (client_id)
        GROUP BY client_id, name 
  - 删除视图:
    - DROP VIEW 视图名
  - 重建视图:
    - CREATE OR REPLACE VIEW 视图名  AS
  - 可更新视图: 
    - 没有:DISTINCT、聚合函数、GROUP/HAVING、UNION就为可更新视图
    - 末尾加:WITH CHECK OPTION 防止UPDATE和DELETE语句将行从视图中删除
## 进阶
- 触发器(triggers)

- 事件(events)

- 事务和并发(transactions)

- 并发(concurrency)


## 高阶
- 设计数据库(Designing Databases)

- 索引(Indexing for high performance)

- 数据库安全(Securing Databases)


SQL学习

上一篇:MySQL源码解读之数据结构-LF_DYNARRAY


下一篇:MySql数据库的增删改查