信安週報-第02周:SQL基礎

信安之路

第02周

Code:https://github.com/lotapp/BaseCode/tree/master/safehtml

前言

本週須要自行研究學習的任務貼一下:前端

目標

1.概念(推薦)

數據庫系列去年就開始陸陸續續的發文,這周任務簡單帶過,概念部分我更新了一下,其餘部分看擴展吧~mysql

1.1.關係型數據庫

引用百科的一段抽象描述:git

「關係型數據庫,是指採用了關係模型來組織數據的數據庫,其以行和列的形式存儲數據,以便於用戶理解,關係型數據庫這一系列的行和列被稱爲表,一組表組成了數據庫。用戶經過查詢來檢索數據庫中的數據,而查詢是一個用於限定數據庫中某些區域的執行代碼。關係模型能夠簡單理解爲二維表格模型,而一個關係型數據庫就是由二維表及其之間的關係組成的一個數據組織。」github

通俗講就是:現實中的東西抽象成一個個關係,而後存儲在一張張行列組成的表中,這些表就組成了關係型數據庫面試

PS:重點就是各數據之間的關係(Join)redis

1.1.1.表明

最經典的莫過於:MySQLSQLServerPostgreSQLSQLiteOraclesql

1.1.2.特性

先看看傳統數據庫的好處:數據庫

  1. 經過事務保持數據一致
  2. 能夠Join等複雜查詢
  3. 社區完善(遇到問題簡單搜下就ok了)

最典型的特徵就是:事物的ACID特性express

PS:抽象的就不說了,舉個例子來講明ACID

  1. A:原子性(Atomic)
    • 小明轉帳1000給小張:小明-=1000 => 小張+=1000,這個 (事務)是一個不可分割的總體,若是小明-1000後出現問題,那麼1000得退給小明
    • PS:化學裏面原子的定義是啥? ==> 化學反應不可再分的基本微粒
  2. C:一致性(Consistent)
    • 小明轉帳1000給小張,必須保證小明+小張的總額不變(假設不受其餘轉帳(事務)影響)
    • PS:能夠用初三化學中的能量守恆來理解
  3. I:隔離性(Isolated)
    • 小明轉帳給小張的同時,小潘也轉錢給了小張,須要保證他們相互不影響(主要是併發狀況下的隔離)
    • PS:理想各個國家互不干涉內政
  4. D:持久性(Durable)
    • 小明轉帳給小張,銀行要留有記錄,即便之後扯皮也能夠拉流水帳【事務執行成功後進行的持久化(就算數據庫以後掛了也能經過Log恢復)】
    • PS:理想銀行每次交易留的底單,這個就是持久化的表現

1.1.3.不足

  1. 修改表結構(包括創建索引)致使長時間不能更新數據(上了表鎖)
  2. 列不固定時新增或刪除表很繁瑣(一樣面臨表鎖的問題)
    • PS:通常設計數據庫不可能那麼完善,都是後期愈來愈完善,就算本身預留了保留字段也容易由於預留名的不肯定性容易出錯
  3. 簡單查詢不能快速返回(須要先解析SQL,而後還有諸如表進行加鎖解鎖這些額外開銷,而後才能查找)
  4. 大量數據寫入比較麻煩
    1. 數據量不大還好,批量寫入便可
    2. 但是自己數據量就挺大的,進行了主從複製,讀數據在Salver進行到沒啥事,可是大量寫數據庫懟到Master上去就吃不消了,必須得加主數據庫了。
    3. 加完又出問題了:雖然把主數據庫一分爲二,可是容易發生數據不一致(一樣數據在兩個主數據庫更新成不同的值),這時候得結合分庫分表,把表分散在不一樣的主數據庫中。
    4. 完了嗎?NoNoNo,想想表之間的Join咋辦?豈不是要跨數據庫和跨服務器join了?簡直就是拆東牆補西牆的節奏啊,因此各類中間件就孕育而生了

擴充:如今對修改表結構是這麼的解決方案

PS:雖然都是藉助工具來操做但原理仍是要了解下:先建立新表,而後舊錶中建立一個觸發器(處理產生的新數據),而後把一條條數據轉移到新表中,接着刪除舊錶,更名新表(和舊錶名同樣)


1.2.非關係型數據庫

引用百科一段抽象描述:

「非關係型數據庫,又被稱爲NoSQL(Not Only SQL ),主要是指非關係型、分佈式、不提供ACID的數據庫設計模式」

通俗化講就是:爲了彌補SQL的不足而建立的(能夠逆推NoSQL的優點)

PS:你能夠理解爲:NoSQL就是對原來SQL的擴展補充(NewSQL = SQL + NoSQL

1.2.1.特性

先說說優點:

  1. 易於數據分散:NoSQL不支持Join,各個數據設計都是獨立的,很容易就把數據分散到多個服務器上
  2. 輕鬆支持大量數據寫入:沒有那些亂七八糟的關係,橫向擴展很輕鬆
  3. 多樣性:NoSQL根據需求拆分紅不少種類,使用很是靈活方便

NoSQL的特性就是CAP

PS:CAP是分佈式系統須要考慮的三個指標,數據共享只能知足兩個而不可兼得:

CAP

  1. C:一致性(Consistency)
    1. 全部節點訪問同一份最新的數據副本(在分佈式系統中的全部數據備份,在同一時刻是否一樣的值)
    2. eg:分佈式系統裏更新公告後,某個用戶都應該讀取最公告
  2. A:可用性(Availability)
    1. 在集羣中一部分節點故障後,集羣總體是否還能響應客戶端的讀寫請求。(對數據更新具有高可用性)
    2. eg:分佈式系統裏每一個操做總能在必定時間內返回結果(掛幾個服務器也不影響)
  3. P:分區容錯性(Partition Toleranc)
    1. 以實際效果而言,分區至關於對通訊的時限要求。系統若是不能在時限內達成數據一致性,就意味着發生了分區的狀況,必須就當前操做在C和A之間作出選擇。
    2. eg:分佈式系統裏,存在網絡延遲的狀況下依舊能夠接受知足一致性和可用性的請求
CA

表明:傳統關係型數據庫

若是想避免分區容錯性問題的發生,一種作法是將全部的數據(與事務相關的)都放在一臺機器上。雖然沒法100%保證系統不會出錯,但不會碰到由分區帶來的負面效果(會嚴重的影響系統的擴展性)

做爲一個分佈式系統,放棄P,即至關於放棄了分佈式,一旦併發性很高,單機服務根本不能承受壓力。像不少銀行服務,確確實實就是捨棄了P,只用高性能的單臺小型機保證服務可用性。(全部NoSQL數據庫都是假設P是存在的

CP

表明:ZookeeperRedis(分佈式數據庫、分佈式鎖)

PS:NoSQL大部分都是CP

相對於放棄「分區容錯性」來講,其反面就是放棄可用性。一旦遇到分區容錯故障,那麼受到影響的服務須要等待數據一致等待數據一致性期間系統沒法對外提供服務

AP

表明:DNS數據庫IP和域名相互映射的分佈式數據庫,聯想修改IP後爲何TTL須要10分鐘左右保證全部解析生效)

DNS

擴展

放棄強一致,保證最終一致性。全部的NoSQL數據庫都是介於CPAP之間,儘可能往AP

PS:傳統關係型數據庫注重數據一致性,而對NoSQL來講可用性和分區容錯性優先級高於數據一致性

不一樣數據對一致性要求是不同的,eg:

  1. 用戶評論、彈幕這些對一致性是不敏感的,很長時間不一致性都不影響用戶體驗
  2. 像商品價格等對一致性有很高要求,容忍度鐵定低於10s,就算使用了緩存在訂單裏面價格也是最新的
    • PS:平時注意下JD商品下面的緩存說明,JD尚且如此,其餘的就不用說了

CAP

傳統關係型數據庫通常都是使用悲觀鎖的方式,可是例如秒殺這類的場景就玩不轉了,這時候每每就使用樂觀鎖了(CAS機制),CAP不能同時知足的時候也就剩下這兩個方案了:

  1. 強一致性:不管更新在哪一個副本上,以後對操做都要可以獲取最新數據。多副本數據就須要分佈式事物來保證數據一致性了(這就是問什麼項目裏面常常提到的緣由)
  2. 最終一致性:在這種約束下保證用戶最終能讀取到最新數據。舉幾個例子:
    1. 因果一致性:A、B、C三個獨立進程,A修改了數據並通知了B,這時候B獲得的是最新數據。由於A沒通知C,因此C不是最新數據
    2. 會話一致性:用戶本身提交更新,他能夠在會話結束前獲取更新數據,會話結束後(其餘用戶)可能不是最新的數據(提交後JQ修改下本地值,不能保證數據最新)
    3. 讀自寫一致性:和上面差很少,只是不侷限在會話中了。用戶更新數據後他本身獲取最新數據,其餘用戶可能不是最新數據(有必定的延遲)
    4. 單調讀一致性:用戶讀取某個數值,後續操做不會讀取到比這個數據還早的版本(新的程度>=讀取的值)
    5. 單調寫一致性(時間軸一致性):全部數據庫的全部副本按照相同順序執行全部更新操做(有點像RedisAOF

1.2.2.表明

經常使用的已經加粗:

  1. 鍵值數據庫Redis、MemCached...
    • PS:如今LevelDBRocksDB也是比較高效的解決方案
    • PS:360開發的pika(redis的補充)能夠解決Redis容量過大的問題
  2. 文檔數據庫MongoDB、ArangoDB、CouchBase、CouchDB、RavenDB...
    • PS:Python有一款基於json存儲的輕量級數據庫:tinydb
  3. 列式數據庫:CassandraHBase、BigTable...
    • PS:小米的Pegasus計劃取代HBase
  4. 搜索引擎系Elasticsearch、Solr、Sphinx...
    • PS:這幾年用Rust編寫的輕量級搜索引擎sonic很火
  5. 圖形數據庫:Neo4JFlockdbArangoDB、OrientDB、Infinite Graph、InfoGrid...
    • PS:基於Redis有一款圖形數據庫用的也挺多:RedisGraph
    • PS:隨着Go的興起,這些圖形數據庫很火:CayleyDgraphBeam

PS:項目中最經常使用的其實就是RedisElasticsearchMongoDB

2.基礎

最早接觸的是SQLServer,然後纔是MySQL系,下面我儘可能使用通用SQL

2.1.傳統概念

來講說傳統概念:

  1. 關係型數據庫中的關係:表(行、列)
  2. 設計範式
    • 第1範式:字段是原子性的
    • 第2範式:每一個表須要一個主鍵
    • 第3範式:任何表都不該該有依賴於其餘非主鍵表的字段
  • DDL:數據定義語言(Data Defination Language)
    • create、drop、alter
  • DML:數據操做語言(Data Manipulation Language)
    • insert、delete、update、select
  • DCL:數據庫控制語言(Data Control Language)
    • grant(受權)、revoke(回收)

PS:CURD(定義了用於處理數據的基本原子操做):建立(Create)更新(Update)讀取(Retrieve)刪除(Delete)操做


2.2.字段類型(含異同)

官方文檔:

MariaDB爲例,簡單列舉下經常使用類型:(傾體說明和MySQL不同)

  1. 字符型:
    1. 定長字符型:
      • char():不區分字符大小寫類型的字符串,max:255個字符
      • binary():區分字符大小寫類型的二進制字符串
    2. 變長字符型:
      • varchar(): 不區分字符大小寫類型的字符串
        • max:65535(2^16 - 1)個字節(utf8編碼下最多支持21843個字符
        • 能夠理解爲SQLServernvarchar
      • varbinary():區分字符的大小寫類型的二進制字符串
    3. 對象存儲:
      • text:不區分字符大小寫的不限長字符串
        • 最大長度爲65,535(2^16 - 1)個字符
        • 若是值包含多字節字符,則有效最大長度減小
      • blob:區分字符大小寫的不限長二進制字符串
    4. 內建類型:(不推薦使用)
      • enum:單選字符串數據類型,適合表單中的單選值
      • set:多選字符串數據類型,適合表單的多選值
      • PS:MySQL系獨有,SQLServer沒有
  2. 數值型:
    1. 精確數值型:
      • 整型:int
        1. bool:布爾類型(MySQL沒有)
          • PS:SQLServerbit
          • 至關於MySQLtinyint(1)
        2. tinyint:微小整型(1字節,8位)
          • [-2^7, 2^7)(-128 ~ 127)
          • 無符號:[0, 2^8)(0 ~ 255)
        3. smallint(2bytes,16bit):小整型
          • 無符號:0 ~ 65535
        4. mediumint(3bytes,24位):中等整型
          • PS:SQLServer中沒這個類型
        5. int(4bytes,32bit)
          • [-2^31, 2^31)[-2147483648,2147483648)
          • 無符號:[0, 2^32)[0,4294967296)
        6. bigint(8bytes,64bit)
          • [-2^63, 2^63)
          • 無符號:[0, 2^64)
    2. 浮點類型:
      • float:單精度浮點數(4字節)
      • double:雙精度浮點數(8字節)
        • PS:SQLServerfloat類型至關於MySQLdouble
      • decimal:精確小數類型(比double更精確)
        • 錢相關用的比較多:decimal(位數,小數點位數)
        • eg:decimal(3,2) => x.xx
  3. 日期和時間類型:(和MySQL同樣)
    1. date:日期(3bytes)
    2. time:時間(3bytes)
    3. year:年
      • eg:year(2)00~99(1bytes)
      • eg:year(4)0000~9999(1bytes)
      • PS:SQLServer沒有這個類型
    4. datetime:既有時間又有日期(8bytes)
    5. timestamp:時間戳(4bytes)【精度更高】
  4. 修飾符:
    • 全部類型都適用:
      • 是否爲null:null | not null
      • 默認值:default xxx_value
      • 主 鍵:primary key
      • 惟一鍵:unique key
    • 數值類型適用:
      • 無符號:unsigned(MySQL系獨有)
      • 自增加:auto_increment (通常只用於整型,MSSQL是identity
        • 獲取ID:last_insert_id()
    • PS:多列設置
      1. 主鍵:primary key(xx,...)
      2. 惟一鍵:unique key(xx,...)
      3. 索引:index index_name (xx,...)

PS:如今新版本數據庫兼容了SQLServer的nvarchar寫法(執行成功後數據類型變成varchar)【不推薦使用】

課後拓展:

MySQL:char、varchar、text的區別


3.建表

3.1.知識歸納

  1. 建立數據庫:
    • create database [if not exists] db_name;
  2. 刪除數據庫:
    • drop database [if exists] db_name;
  3. 建立表:
    • create table [if not exists] tb_name(列名1,數據類型 修飾符,列名2,數據類型 修飾符);
  4. 刪除表:
    • drop table [if exists] db_name.tb_name;
  5. 修改表:
    1. 字段
      • 添加字段:add
        • alter table tb_name add 列名 數據類型 修飾符 [first | after 列名];
        • PS:SQLServer沒有[first | after 列名]
      • 修改字段:alter、change、modify
        • 修改字段名:alter table tb_name change 舊列名 新列名 類型 類型修飾符
        • 修改字段類型:alter table tb_name modify 列名 類型 類型修飾符
        • 添加默認值:alter table tb_name alter 列名 set default df_value
      • 刪除字段:drop
        • alter table tb_name drop 字段名
    2. 索引
      • 添加索引:add(經常使用:create index index_name on tb_name(列名,...);
        • alter table tb_name add index [ix_name] (列名,...);
        • 添加惟一鍵:alter table tb_name add unique [uq_name] (列名,列名2...);
        • PS:不指定索引名字,默認就是第一個字段名
      • 刪除索引:drop(經常使用:drop index index_name on tb_name
        • alter table tb_name drop index index_name;
        • 刪除惟一鍵:alter table tb_name drop index uq_name;
        • PS:惟一鍵的索引名就是第一個列名
      • PS:通常在常常用作查詢條件的列設置索引

3.2.修改SQL_Mode(MySQL系)

通俗講:SQL_Mode就是設置SQL語法檢測的級別

3.2.1.默認值

MariaDB 5.x的SQLMode通常是空(沒什麼約束)

PS:select version();能夠查看DB的版本,select @@sql_mode;能夠查看SQL_Mode的值

MySQL 5.7.27的SQLMode默認是:ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

簡單解析一下:

  1. ONLY_FULL_GROUP_BY:使用group by時查詢的字段必須是group by子句的列
    • eg:select count(url),name from file_records group by url;
    • 使用了name字段,name不是聚合函數,那必須在group by中寫一下(否則就報錯)
  2. STRICT_TRANS_TABLES:對全部支持事物類型的表作嚴格約束(線上版本通常都是這個)
  3. NO_ZERO_IN_DATENO_ZERO_DATE:防止0000-00-00插入日期中
  4. ERROR_FOR_DIVISION_BY_ZERO:分母爲0則報錯(默認是警告)
  5. NO_AUTO_CREATE_USER:建立用戶的時候必須identified by 'pass'來指定密碼
    • eg:create user bryan@'%' identified by '含大小寫字母+數字的密碼';
  6. NO_ENGINE_SUBSTITUTION:建表的時候指定不可用存儲引擎會報錯

3.2.2.開發經驗

PS:項目開發通常設置爲traditional(使用傳統模型,不容許對非法值作插入操做),線上通常使用strict_trans_tables來提升併發(阿里雲默認值)

3.2.3.配置修改

以Ubuntu爲例:sudo vi /etc/mysql/mysql.conf.d/mysqld.cnfsql_mode='strict_trans_tables'

而後重啓mysql使配置生效:sudo systemctl restart mysql

sql_mode

效果:

ok

PS:從MySQL8開始,可經過set persist命令將全局變量的修改持久化到(/var/lib/mysql/mysqld-auto.cnf)配置文件中

eg:set persist sql_mode='strict_trans_tables';


3.3.MySQL

3.3.1.建立、刪除數據庫

-- 若是存在就刪除數據庫
drop database if exists safe_db;

-- 建立數據庫
create database if not exists safe_db;

3.3.2.建立、刪除表

-- 若是存在就刪除表
drop table if exists safe_db.users;

-- mysql> help create table(低版本的默認值不支持函數)
-- 建立表 create table users(字段名 類型 修飾符,...)
create table if not exists safe_db.users
(
    id         int unsigned auto_increment,             -- 主鍵,自增加【獲取ID:last_insert_id()】
    username   varchar(20) not null,
    password   char(40)    not null,                    -- sha1:40
    email      varchar(50) not null,
    ucode      char(36)    not null,                    -- uuid
    createtime datetime    not null default now(),
    updatetime datetime    not null default now(),
    datastatus tinyint     not null default 0,          -- 默認值爲0
    primary key (id),                                   -- 主鍵可多列
    unique uq_users_email (email),
    index ix_users_createtime_updatetime (createtime, updatetime) -- 索引,不指定名字默認就是字段名
)
--   表選項
--   engine = 'innodb', -- 引擎
--   character set utf8, -- 字符集
--   collate utf8_general_ci, -- 排序規則
;

3.3.3.修改表

-- 修改表 mysql> help alter table

-- 3.1.添加一列 alter table tb_name add 列名 數據類型 修飾符 [first | after 列名]
alter table safe_db.users
    add uid bigint not null unique first; -- MSSQL沒有[first | after 列名]

-- 在email後面添加手機號碼列
-- 手機號不會用來作數學運算,varchar能夠模糊查詢(eg:like ‘138%’)
-- 牽扯到國家代號時,可能出現+、-、()等字符,eg:+86
alter table safe_db.users
    add tel varchar(20) not null after email;

-- 3.2.刪除一列 alter table tb_name drop 字段名
alter table safe_db.users
    drop uid;

-- 3.3.添加索引 alter table tb_name add index [ix_name] (列名,...)
alter table safe_db.users
    add index ix_users_ucode (ucode); -- 不指定名字默認就是字段名
-- add index (ucode, tel); -- 不指定索引名字,默認就是第一個字段名

-- 添加惟一鍵 alter table tb_name add unique [uq_name] (列名,列名2...)
alter table safe_db.users
    add unique uq_users_tel_ucode (tel, ucode);
-- add unique (tel, ucode);-- 不指定索引名字,默認就是第一個字段名

-- 3.4.刪除索引 alter table tb_name drop index ix_name
alter table safe_db.users
    drop index ix_users_ucode;

-- 刪除索引(惟一鍵) alter table tb_name drop index uq_name
alter table safe_db.users
    drop index uq_users_tel_ucode;
-- drop index tel; -- 惟一鍵的索引名就是第一個列名

-- 3.5.修改字段
-- 1.修改字段名:`alter table tb_name change 舊列名 新列名 類型 類型修飾符`
-- 此時必定要從新指定該列的類型和修飾符
alter table safe_db.users
    change ucode usercode char(36);

-- 2.修改字段類型
alter table safe_db.users
    modify username varchar(25) not null;

-- 3.添加默認值:`alter table tb_name alter 列名 set default df_value`
alter table safe_db.users
    alter password set default '7c4a8d09ca3762af61e59520943dc26494f8941b';

desc

show

3.4.SQLServer

示例服務器:SQLServer 2014

3.4.1.建立、刪除數據庫

use master

--存在就刪除
if exists(select *
          from sysdatabases
          where Name = N'safe_db')
  begin
    drop database safe_db
  end

--建立數據庫(簡化版:create database safe_db)
create database safe_db
  on primary --數據庫文件,主文件組
  (
    name ='safe_db_Data', --邏輯名
    size =10 mb, --初始大小
    filegrowth =10%, --文件增加
    maxsize =1024 mb, --最大值
    filename =N'D:\Works\SQL\safe_db_data.mdf'--存放路徑(包含文件後綴名)
    )
  log on --日記
  (
    name ='safe_db_Log',
    size =5 mb,
    filegrowth =5%,
    filename =N'D:\Works\SQL\safe_db_log.ldf'
    );

-- 切換數據庫
use safe_db;

3.4.2.建立、刪除表

--存在就刪除表
if exists(select *
          from sysobjects
          where name = N'users')
  begin
    drop table users
  end

-- safe_db.dbo.users
create table users
(
  id         int identity,                                      -- 主鍵,自增加
  username   nvarchar(20) not null,
  email      varchar(50)  not null,
  password   char(40)     not null,                             -- sha1
  ucode      char(36)     not null default newid(),             -- guid
  createtime datetime     not null default getdate(),
  updatetime datetime     not null default getdate(),
  datastatus tinyint      not null default 0,                   -- 默認值爲0
  primary key (id),                                             -- 主鍵可多列
  unique (email),
  index ix_users_createtime_updatetime (createtime, updatetime) -- 索引
);

3.4.3.修改表

-- 3.1.添加一列 alter table tb_name add 列名 數據類型 修飾符
-- 在email後面添加手機號碼列
alter table users
  add tel varchar(20) not null;

-- 3.1.1.添加含惟一鍵的列
-- 先添加列
alter table users
  add uid bigint not null
-- 再添加約束 alter table tb_name add constraint uq_name
alter table users
  add constraint uq_users_uid unique (uid); -- 自定義名稱

-- 3.1.2.定義和約束一步走(系統設置名字)
-- alter table users
--   add uid bigint not null unique; -- 默認名稱

-- 3.2.含惟一鍵的列
-- 3.2.1.刪除約束 alter table tb_name drop constraint uq_name
if exists(select *
          from sysobjects
          where name = 'uq_users_uid')
alter table users
  drop constraint uq_users_uid;

-- 3.2.2.刪除列 alter table tb_name drop column 字段名
alter table users
  drop column uid;

-- 3.3.修改字段
-- 3.3.1.修改列名:exec sp_rename '表名.舊列名','新列名';
exec sp_rename 'users.ucode', 'usercode';

-- 3.3.2.修改字段類型
alter table users
    alter column username varchar(25) not null;

-- 3.3.3.添加默認值:`alter table tb_name alter 列名 set default df_value`
alter table users
  add default '7c4a8d09ca3762af61e59520943dc26494f8941b' for password;

PS:SQL Server默認端口爲TCP 1433

3.5.區別

簡單列舉下上面的區別(歡迎補充):

  1. MySQL自增加是auto_increment,MSSQL是identity
  2. MySQL能夠設置無符號unsigned,MSSQL不能夠直接設置無符號整型,須要經過約束之類的來限制
  3. alter table的時候,MSSQL沒有[first | after 列名],並且語法差異也挺大

4.CURD

4.1.MySQL

4.1.1.執行流程

select語句執行流程

  1. from 表
  2. [inner|left|right] join 表 on 條件
  3. where 條件
    • 對select的結果進行過濾
  4. group by 字段
    • 根據指定條件把查詢結果進行分組,以用作聚合運算
  5. having 條件
    • 對分組聚合運算(group by)後的結果進行過濾
  6. order by 字段 [asc|desc]
    • 根據指定字段對查詢結果進行排序(默認升序asc
  7. select 字段
  8. limit [偏移量,]顯示數量
    • 顯示多少條數據 | 分頁顯示

4.1.2.增刪改

-- 4.1.插入 help insert
-- 自增加主鍵和默認值的字段能夠不寫
insert into safe_db.users(username, password, email, tel, usercode, createtime, updatetime, datastatus)
values ('test', '7c4a8d09ca3762af61e59520943dc26494f8941b', 'test@qq.com', '18738002038', uuid(), now(), now(), 1);

-- 批量插入
insert into safe_db.users(username, password, email, tel, usercode, createtime, updatetime, datastatus)
values('xxx', '7c4a8d09ca3762af61e59520943dc26494f8942b', 'xxx@qq.com', '13738002038', uuid(), now(), now(), 0),('mmd', '7c4a8d09ca3762af61e59520943dc26494f8941b', 'mmd@qq.com', '13718002038', uuid(), now(), now(), 1),('小張', '7c4a8d09ca3762af61e59520943dc26494f8941b', 'zhang@qq.com', '13728002038', uuid(), now(), now(), 1);

-- 4.2.修改 help update
update safe_db.users
set datastatus=99,
    updatetime = now()
where username = 'mmd'; -- 必定要有where條件!開發中通常都是先寫where條件再寫update

-- 4.3.刪除
-- 刪除數據(自增加不重置)help delete;
delete
from safe_db.users
where datastatus = 0;

-- 刪除所有數據(自增加重置)help truncate;
truncate table safe_db.users;

4.1.3.查詢

file_records的建表語句我放在附錄了,這邊只談語法

PS:查詢相關的幫助文檔:help select

1.查詢來源url

use safe_db;

-- 查詢來源url(去重後)
select distinct url
from file_records;

-- 查詢來源url(分組方式)
select url
from file_records
group by url;

r1

2.統計url出現的次數

-- 分別統計一下url出現的次數(分組+聚合)
-- 分組通常都和聚合函數一塊兒使用
select url, count(*) as count
from file_records
group by url;

-- 分別統計一下url出現的次數大於1次的url(刪除的不統計)
select url, count(*) as count
from file_records
where datastatus = 1 -- 99表明刪除的數據
group by url
having count > 1; -- 在group by的結果上篩選

r2

r3

3.統計一下url出現的次數並查出對應的id

-- 分別統計一下url出現的次數並查出對應的id
select group_concat(id) as ids, url
from file_records
group by url;

r4

4.查詢上傳文件的用戶詳細信息,並按文件名降序排序

-- 內鏈接查詢 innet join tb_name on 關聯條件
select file_records.id,
       users.id                   as uid,
       users.username,
       users.email,
       file_records.file_name,
       file_records.md5,
       inet_ntoa(file_records.ip) as ip,
       file_records.url
from users
         inner join file_records on file_records.user_id = users.id -- 鏈接條件
where users.datastatus = 1
  and file_records.datastatus = 1
order by file_records.file_name desc; -- 文件名降序排序

r5

5.查詢前五條文件上傳信息

-- MySQL沒有`select top n`語法,可使用 limit來實現,eg:top 5
select *
from file_records
limit 5; -- limit 0,5

r6

6.分頁查詢相關

-- 分頁查詢
-- page:1,count=5 ==> 0,5 ==> (1-1)*5,5
-- page:2,count=5 ==> 5,5 ==> (2-1)*5,5
-- page:3,count=5 ==> 10,5 ==> (3-1)*5,5
-- 推理:limit (page-1)*count,count
select file_records.id,
       users.id                   as uid,
       users.username,
       users.email,
       file_records.file_name,
       file_records.md5,
       inet_ntoa(file_records.ip) as ip,
       file_records.url
from file_records
         inner join users on file_records.user_id = users.id
limit 0,5;

-- limit後面跟表達式就會報錯
select file_records.id,
       users.id                   as uid,
       users.username,
       users.email,
       file_records.file_name,
       file_records.md5,
       inet_ntoa(file_records.ip) as ip,
       file_records.url
from file_records
         inner join users on file_records.user_id = users.id
limit 5,5;
-- limit (2-1)*5,5; -- limit錯誤寫法

-- limit要放在最後
select file_records.id,
       users.id                   as uid,
       users.username,
       users.email,
       file_records.file_name,
       file_records.md5,
       inet_ntoa(file_records.ip) as ip,
       file_records.url
from file_records
         inner join users on file_records.user_id = users.id
order by username desc, file_name desc
limit 10,5; -- 先order by排完序,而後再取第三頁的5個數據

r7

7.鏈接查詢

-- 查找一下歷來沒上傳過文件的用戶
-- right join:以右邊表(users)爲基準鏈接
select file_records.id            as fid,
       users.id                   as uid,
       users.username,
       users.email,
       file_records.file_name,
       file_records.md5,
       inet_ntoa(file_records.ip) as ip,
       file_records.url
from file_records
         right join users on file_records.user_id = users.id
where users.datastatus = 1
  and file_records.id is null
order by username desc, file_name desc;

-- 自鏈接案例:
-- 二級聯動 p:province,c:city,a:area
-- 前端通常都會顯示省級信息,用戶選擇後能夠得到對應的二三級信息
select c.name, a.name
from city_infos as c
         inner join city_infos as a on a.pcode = c.code
where c.pcode = '320000'; -- pcode設置爲索引

-- 經過省名稱查詢
select p.name, c.name, a.name
from city_infos as c
         inner join city_infos as p on c.pcode = p.code
         inner join city_infos as a on a.pcode = c.code
where p.name = '江蘇省';

4.1.4.視圖

-- 簡單提一下視圖:
-- 建立視圖
create view view_userinfo as
select id, username, password, email, tel, datastatus
from safe_db.users;

-- 查詢視圖
select id, username, password, email, tel, datastatus
from safe_db.view_userinfo;

-- 刪除視圖
drop view if exists view_userinfo;

view

附錄

知識點:

-- 把ip轉換成int
select inet_aton('43.226.128.3'); -- inet6_aton()
-- 把int轉換成ip
select inet_ntoa('736264195'); -- inet6_ntoa() ipv6

-- 將多個字符串鏈接成一個字符串
select concat(user_id, ',', file_name, ',', ip, ',', url) as concat_str
from file_records;

-- 將多個字符串鏈接成一個字符串+能夠一次性指定分隔符
select concat_ws(',', user_id, file_name, ip, url) as concat_str
from file_records;

-- 在有group by的查詢語句中,select指定的字段要麼就包含在group by語句的後面,做爲分組的依據,要麼就包含在聚合函數中
-- group_concat():將group by產生的同一個分組中的值鏈接起來,返回一個字符串結果
select group_concat(file_name) as file_name, url, count(*)
from file_records
group by url;

-- having通常對group by的結果進行篩選,where是對原表進行篩選
select group_concat(file_name) as file_name, group_concat(url) as url, count(*) as count
from file_records
group by url
having count >= 3;

-- 四捨五入到指定位數
select round(3.12345, 4);
-- 存小數數據爲了避免損傷精讀通常都是轉成整數,eg:3.1415 ==> 整數:31415,倍數:10000

數據構造

-- 編號,文件名,文件MD5,Meta(媒體類型),當前用戶,請求IP,來源地址,請求時間,數據狀態
drop table if exists safe_db.file_records;
create table if not exists safe_db.file_records
(
    id         int unsigned auto_increment primary key,
    file_name  varchar(100)     not null,
    md5        char(32)         not null,
    meta_type  tinyint unsigned not null default 1,
    user_id    int unsigned     not null,
    ip         int unsigned     not null,
    url        varchar(200)     not null default '/',
    createtime datetime         not null, -- default now(),
    datastatus tinyint          not null default 0
);

-- 能夠插入2~3次(方便下面演示)
insert into safe_db.file_records(file_name, md5, meta_type, user_id, ip, url, createtime, datastatus)
values ('2.zip', '3aa2db9c1c058f25ba577518b018ed5b', 2, 1, inet_aton('43.226.128.3'), 'http://baidu.com', now(), 1),
       ('3.rar', '6f401841afd127018dad402d17542b2c', 3, 3, inet_aton('43.224.12.3'), 'http://qq.com', now(), 1),
       ('7.jpg', 'fe5df232cafa4c4e0f1a0294418e5660', 4, 5, inet_aton('58.83.17.3'), 'http://360.cn', now(), 1),
       ('9.png', '7afbb1602613ec52b265d7a54ad27330', 5, 4, inet_aton('103.3.152.3'), 'http://cnblogs.com', now(), 1),
       ('1.gif', 'b5e9b4f86ce43ca65bd79c894c4a924c', 6, 3, inet_aton('114.28.0.3'), 'http://qq.com', now(), 1),
       ('大馬.jsp', 'abbed9dcc76a02f08539b4d852bd26ba', 9, 4, inet_aton('220.181.108.178'), 'http://baidu.com', now(),
        99);

4.2.SQLServer

4.2.1.執行流程

select語句執行流程:

  1. from 表
  2. join類型 join 表 on 條件
  3. where 條件
    • 對select的結果進行過濾
  4. group by 字段
    • 根據指定條件把查詢結果進行分組,以用作聚合運算
  5. having 條件
    • 對分組聚合運算(group by)後的結果進行過濾
  6. select distinct 字段
  7. order by 字段 [asc|desc]
    • 根據指定字段對查詢結果進行排序(默認升序asc
  8. top 多少行
    • 類比limit

4.2.2.增刪改

-- 4.1.插入 help insert
-- 自增加主鍵和默認值的字段能夠不寫
insert into safe_db.dbo.users(username, password, email, tel, usercode, createtime, updatetime, datastatus)
values ('mmd', '7c4a8d09ca3762af61e59520943dc26494f8941b', 'mmd@qq.com', '18738002038', newid(), getdate(), getdate(),
        1);

-- 批量插入 SQLServer一次批量插入最多1000行左右
insert into safe_db.dbo.users(username, password, email, tel, usercode, createtime, updatetime, datastatus)
values ('xxx', '7c4a8d09ca3762af61e59520943dc26494f8942b', 'xxx@qq.com', '13738002038', newid(), getdate(), getdate(), 0),
       ('mmd', '7c4a8d09ca3762af61e59520943dc26494f8941b', 'mmd@qq.com', '13738002038', newid(), getdate(), getdate(), 1),
       ('小明', '7c4a8d09ca3762af61e59520943dc26494f8941b', 'xiaoming@qq.com', '13718002038', newid(), getdate(), getdate(), 1),
       ('小張', '7c4a8d09ca3762af61e59520943dc26494f8941b', 'zhang@qq.com', '13728002038', newid(), getdate(), getdate(), 1),
       ('小潘', '7c4a8d09ca3762af61e59520943dc26494f8941b', 'pan@qq.com', '13748002038', newid(), getdate(), getdate(), 1),
       ('小周', '7c4a8d09ca3762af61e59520943dc26494f8941b', 'zhou@qq.com', '13758002038', newid(), getdate(), getdate(), 1),
       ('小羅', '7c4a8d09ca3762af61e59520943dc26494f8941b', 'luo@qq.com', '13768002038', newid(), getdate(), getdate(), 1);

-- 4.2.修改 help update
update safe_db.dbo.users
set datastatus=99,
    updatetime = getdate()
where username = 'mmd'; -- 必定要有where條件!開發中通常都是先寫where條件再寫update

-- 4.3.刪除
-- 刪除數據(自增加不重置)help delete;
delete
from safe_db.dbo.users
where datastatus = 0;

-- 刪除所有數據(自增加重置)help truncate;
truncate table safe_db.dbo.users;

4.2.3.查詢

-- 查詢來源url(去重後)
select distinct url
from file_records;

-- 查詢來源url(分組方式)
select url
from file_records
group by url;

-- 分別統計一下url出現的次數(分組+聚合)
-- 分組通常都和聚合函數一塊兒使用
select url, count(*) as count
from file_records
group by url;

-- 分別統計一下url出現的次數,已經刪除的文件不算進去
select url, count(*) as count
from file_records
group by url
having count(*) > 3; -- 在group by的結果上篩選,★寫成count就不行了★

-- 分別統計一下url出現的次數並查出對應的id
-- SQLServer2017新增string_agg
select ids =(select stuff((select ',' + cast(id as varchar(20)) from file_records as f
 where f.url = file_records.url for xml path ('')), 1, 1, '')),url from file_records
group by url;

-- 內鏈接查詢 innet join tb_name on 關聯條件
select file_records.id,
       users.id                   as uid,
       users.username,
       users.email,
       file_records.file_name,
       file_records.md5,
       file_records.ip,
       file_records.url
from users
         inner join file_records on file_records.user_id = users.id -- 鏈接條件
where users.datastatus = 1
  and file_records.datastatus = 1
order by file_records.file_name desc; -- 文件名降序排序

-- 顯示前5個數據
select top 5 * from file_records;

-- 分頁查詢 第3頁,每頁5條
select *
from (select row_number() over (order by username desc, file_name desc) as id,
             file_records.id                                            as fid,
             users.id                                                   as uid,
             users.username,
             users.email,
             file_records.file_name,
             file_records.md5,
             file_records.ip,
             file_records.url
      from file_records
               inner join users on file_records.user_id = users.id) as temp
where id > (3 - 1) * 5 and id <= 3 * 5;

-- 簡單提一下視圖:
-- 存在就刪除
if exists(select *
          from sysobjects
          where name = N'view_userinfo')
    begin
        drop view view_userinfo
    end
-- 建立視圖
create view view_userinfo as
select id, username, password, email, tel, datastatus
from users;

-- 查詢視圖
select id, username, password, email, tel, datastatus
from view_userinfo;

附錄2

知識點:

select getdate() as datatime, newid() as uuid;

-- 相似於concat的效果
select cast(id as varchar(20)) + ','
from file_records for xml path ('');

-- 移除多餘的字符
-- STUFF(<character_expression>,<開始>,<長度>,<character_expression>)
-- 將字符串插入到另外一個字符串中。它會刪除開始位置第一個字符串中的指定長度的字符,而後將第二個字符串插入到開始位置的第一個字符串中
select stuff((select ',' + cast(id as varchar(20))
              from file_records for xml path ('')), 1, 1, '');

數據構造:

--存在就刪除表
if exists(select *
          from sysobjects
          where name = N'file_records')
    begin
        drop table file_records
    end
-- 由於SQLServer的int沒有unsigned,因此推薦使用bigint
create table file_records
(
    id         bigint identity (1,1) primary key,
    file_name  varchar(100) not null,
    md5        char(32)     not null,
    meta_type  tinyint      not null default 1,
    user_id    int          not null,
    ip         bigint       not null, -- 在程序中自行轉換
    url        varchar(200) not null default '/',
    createtime datetime     not null default getdate(),
    datastatus tinyint      not null default 0
);

-- 能夠插入3次(方便下面演示)
insert into file_records(file_name, md5, meta_type, user_id, ip, url, createtime, datastatus)
values ('2.zip', '3aa2db9c1c058f25ba577518b018ed5b', 2, 1, 736264195, 'http://baidu.com', getdate(), 1),
       ('3.rar', '6f401841afd127018dad402d17542b2c', 3, 3, 736103427, 'http://qq.com', getdate(), 1),
       ('7.jpg', 'fe5df232cafa4c4e0f1a0294418e5660', 4, 5, 978522371, 'http://360.cn', getdate(), 1),
       ('9.png', '7afbb1602613ec52b265d7a54ad27330', 5, 4, 1728288771, 'http://cnblogs.com', getdate(), 1),
       ('1.gif', 'b5e9b4f86ce43ca65bd79c894c4a924c', 6, 3, 1914437635, 'http://qq.com', getdate(), 1),
       ('大馬.jsp', 'abbed9dcc76a02f08539b4d852bd26ba', 9, 4, 3702877362, 'http://baidu.com', getdate(), 99);

5.擴展

5.1.MySQL

聊聊數據庫~1.開篇(主要是NoSQL)

http://www.javashuo.com/article/p-kmvgjgda-eh.html

聊聊數據庫~2.SQL環境篇

http://www.javashuo.com/article/p-uxkudhsn-dw.html

聊聊數據庫~3.SQL基礎篇

http://www.javashuo.com/article/p-rkyvbstc-q.html

聊聊數據庫~4.SQL優化篇

http://www.javashuo.com/article/p-clalizvn-e.html

聊聊數據庫~5.SQL運維上篇

http://www.javashuo.com/article/p-xmusedzr-q.html

聊聊數據庫~6.SQL運維中篇

http://www.javashuo.com/article/p-kivzmehu-k.html

other

CentOS7安裝MySQL8.0小計

http://www.javashuo.com/article/p-obvapnmm-k.html

MySQL的SQL_Mode修改小計http://www.javashuo.com/article/p-vmgtwffb-u.html

MySQL5.7.26 忘記Root密碼小計

http://www.javashuo.com/article/p-heuahocu-cm.html

小計:協同辦公衍生出的需求:

http://www.javashuo.com/article/p-wsuexebj-br.html

IDE相關:JetBrains全家桶破解思路(最新更新:2019-08-01):

https://www.cnblogs.com/dotnetcrazy/p/9711763.html

5.3.SQLServer

SQLServer性能優化之----強大的文件組----分盤存儲(水平分庫)

http://www.javashuo.com/article/p-qacbcyiu-g.html

SQLServer性能優化之---水平分庫擴展

http://www.javashuo.com/article/p-oxsqvnei-u.html

SQLServer性能優化之---分表分庫技術(同義詞+連接服務器)

http://www.javashuo.com/article/p-etvdnreo-cd.html

SQLServer性能優化之---數據庫級日記監控(XEVENT)

http://www.javashuo.com/article/p-acbzaqte-q.html

PS:逆天之前整理的SQLServer腳本

https://github.com/lotapp/BaseCode/tree/master/database/SQL/SQLServer

others

我爲NET狂官方面試題-數據庫篇答案

http://www.javashuo.com/article/p-qtlyxpab-g.html

牛逼的OSQL----大數據導入

http://www.javashuo.com/article/p-hnrlywdf-ka.html

數據庫更名系列(數據庫名,邏輯名,物理文件名)

http://www.javashuo.com/article/p-hmuirath-hx.html

SQLServer文件收縮-圖形化+命令

http://www.javashuo.com/article/p-ndkoinmb-ho.html

數據庫分離附加(附日記丟失的處理)

http://www.javashuo.com/article/p-zhpatzvi-hw.html

數據庫備份相關

http://www.javashuo.com/article/p-sxsyzrnx-mc.html

利用SQLServer數據庫發送郵件

http://www.javashuo.com/article/p-btuevyov-gn.html

恢復掛起的解決方案】附加文件時候的提示「沒法從新生成日誌,緣由是數據庫關閉時存在打開的事務/用戶,該數據庫沒有檢查點或者該數據庫是隻讀的。」【數據庫恢復】

http://www.javashuo.com/article/p-ezhcunmr-ku.html

相關文章
相關標籤/搜索