day20 - 關係數據庫入門

關係數據庫入門

關係數據庫概述

  1. 數據持久化 - 將數據保存到(在掉電狀況下)可以長久保存數據的存儲介質中。html

  2. 數據庫發展史 - 網狀數據庫、層次數據庫、關係數據庫、NoSQL數據庫。前端

    1970年,IBM的研究員E.F.Codd在Communication of the ACM上發表了名爲A Relational Model of Data for Large Shared Data Banks的論文,提出了關係模型的概念,奠基了關係模型的理論基礎。後來Codd又陸續發表多篇文章,論述了範式理論和衡量關係系統的12條標準,用數學理論奠基了關係數據庫的基礎。python

  3. 關係數據庫特色。mysql

    • 理論基礎:集合論和關係代數。git

    • 具體表象:用二維表(有行和列)組織數據。程序員

    • 編程語言:結構化查詢語言(SQL)。github

  4. E-R圖。web

    • 實體 - 矩形框
    • 屬性 - 橢圓框
    • 關係 - 菱形框
    • 重數 - 1:1 / 1:N / M:N
  5. 關係數據庫產品。算法

    • Oracle - 目前世界上使用最爲普遍的數據庫管理系統,做爲一個通用的數據庫系統,它具備完整的數據管理功能;做爲一個關係數據庫,它是一個完備關係的產品;做爲分佈式數據庫,它實現了分佈式處理的功能。在Oracle最新的12c版本中,還引入了多承租方架構,使用該架構可輕鬆部署和管理數據庫雲。
    • DB2 - IBM公司開發的、主要運行於Unix(包括IBM自家的AIX)、Linux、以及Windows服務器版等系統的關係數據庫產品。DB2歷史悠久且被認爲是最先使用SQL的數據庫產品,它擁有較爲強大的商業智能功能。
    • SQL Server - 由Microsoft開發和推廣的關係型數據庫產品,最初適用於中小企業的數據管理,可是近年來它的應用範圍有所擴展,部分大企業甚至是跨國公司也開始基於它來構建本身的數據管理系統。
    • MySQL - MySQL是開放源代碼的,任何人均可以在GPL(General Public License)的許可下下載並根據個性化的須要對其進行修改。MySQL由於其速度、可靠性和適應性而備受關注。
    • PostgreSQL - 在BSD許可證下發行的開放源代碼的關係數據庫產品。

MySQL簡介

  1. 安裝和配置(以CentOS Linux環境爲例)。sql

    • Linux下有一個MySQL的分支版本,名爲MariaDB,它由MySQL的一些原始開發者開發,有商業支持,旨在繼續保持MySQL數據庫在GNU GPL下開源(由於你們擔憂MySQL被甲骨文收購後會再也不開源)。若是決定要直接使用MariaDB做爲MySQL的替代品,可使用下面的命令進行安裝。

      yum install mariadb mariadb-server
    • 若是要安裝官方版本的MySQL,能夠在MySQL官方網站下載安裝文件。首先在下載頁面中選擇平臺和版本,而後找到對應的下載連接。下面以MySQL 5.7.26版本和Red Hat Enterprise Linux爲例,直接下載包含全部安裝文件的歸檔文件,解歸檔以後經過包管理工具進行安裝。

      wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.26-1.el7.x86_64.rpm-bundle.tar
      tar -xvf mysql-5.7.26-1.el7.x86_64.rpm-bundle.tar

      若是系統上有MariaDB相關的文件,須要先移除MariaDB相關的文件。

      yum list installed | grep mariadb | awk '{print $1}' | xargs yum erase -y

      接下來能夠按照以下所示的順序用RPM(Redhat Package Manager)工具安裝MySQL。

      rpm -ivh mysql-community-common-5.7.26-1.el7.x86_64.rpm
      rpm -ivh mysql-community-libs-5.7.26-1.el7.x86_64.rpm
      rpm -ivh mysql-community-client-5.7.26-1.el7.x86_64.rpm
      rpm -ivh mysql-community-server-5.7.26-1.el7.x86_64.rpm

      可使用下面的命令查看已經安裝的MySQL相關的包。

      rpm -qa | grep mysql
    • 啓動MySQL服務。

      先修改MySQL的配置文件(/etc/my.cnf)添加一行skip-grant-tables,能夠設置不進行身份驗證便可鏈接MySQL服務器,而後就能夠以超級管理員(root)身份登陸。

      vim /etc/my.cnf
      [mysqld]
      skip-grant-tables
      
      datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock symbolic-links=0 log-error=/var/log/mysqld.log pid-file=/var/run/mysqld/mysqld.pid

      接下來可使用下面的命令來啓動MySQL。

      service mysqld start

      在CentOS 7中建議使用下面的命令來啓動MySQL。

      systemctl start mysqld
    • 使用MySQL客戶端工具鏈接服務器。

      命令行工具:

      mysql -u root

      修改超級管理員(root)的訪問口令爲i_LOVE_macos_123。

      use mysql;
      update user set authentication_string=password('i_LOVE_macos_123') where user='root'; flush privileges;

      將MySQL配置文件中的skip-grant-tables去掉,而後重啓服務器,從新登陸。這一次須要提供用戶名和口令才能鏈接MySQL服務器。

      systemctl restart mysqld
      mysql -u root -p

      也能夠選擇圖形化的客戶端工具來鏈接MySQL服務器,能夠選擇下列工具之一:

      • MySQL Workbench(官方提供的工具)
      • Navicat for MySQL(界面簡單優雅,功能直觀強大)
      • SQLyog for MySQL(強大的MySQL數據庫管理員工具)
  2. 經常使用命令。

    • 查看服務器版本。

      select version();
    • 查看全部數據庫。

      show databases;
    • 切換到指定數據庫。

      use mysql;
    • 查看數據庫下全部表。

      show tables;
    • 獲取幫助。

      ? contents;
      ? functions;
      ? numeric functions;
      ? round;
      
      ? data types;
      ? longblob;

SQL詳解

  1. DDL

    -- 若是存在名爲school的數據庫就刪除它 drop database if exists school; -- 建立名爲school的數據庫並設置默認的字符集和排序方式 create database school default charset utf8 collate utf8_bin; -- 切換到school數據庫上下文環境 use school; -- 建立學院表 create table tb_college ( collid int not null auto_increment comment '編號', collname varchar(50) not null comment '名稱', collmaster varchar(20) not null comment '院長', collweb varchar(511) default '' comment '網站', primary key (collid) ); -- 建立學生表 create table tb_student ( stuid int not null comment '學號', stuname varchar(20) not null comment '姓名', stusex bit default 1 comment '性別', stubirth date not null comment '出生日期', stuaddr varchar(255) default '' comment '籍貫', collid int not null comment '所屬學院', primary key (stuid), foreign key (collid) references tb_college (collid) ); -- alter table tb_student add constraint fk_student_collid foreign key (collid) references tb_college (collid); -- 建立教師表 create table tb_teacher ( teaid int not null comment '工號', teaname varchar(20) not null comment '姓名', teatitle varchar(10) default '助教' comment '職稱', collid int not null comment '所屬學院', primary key (teaid), foreign key (collid) references tb_college (collid) ); -- 建立課程表 create table tb_course ( couid int not null comment '編號', couname varchar(50) not null comment '名稱', coucredit int not null comment '學分', teaid int not null comment '授課老師', primary key (couid), foreign key (teaid) references tb_teacher (teaid) ); -- 建立選課記錄表 create table tb_score ( scid int auto_increment comment '選課記錄編號', stuid int not null comment '選課學生', couid int not null comment '所選課程', scdate datetime comment '選課時間日期', scmark decimal(4,1) comment '考試成績', primary key (scid), foreign key (stuid) references tb_student (stuid), foreign key (couid) references tb_course (couid) ); -- 添加惟一性約束(一個學生選某個課程只能選一次) alter table tb_score add constraint uni_score_stuid_couid unique (stuid, couid);
  2. DML

    -- 插入學院數據 insert into tb_college (collname, collmaster, collweb) values ('計算機學院', '左冷禪', 'http://www.abc.com'), ('外國語學院', '嶽不羣', 'http://www.xyz.com'), ('經濟管理學院', '風清揚', 'http://www.foo.com'); -- 插入學生數據 insert into tb_student (stuid, stuname, stusex, stubirth, stuaddr, collid) values (1001, '楊逍', 1, '1990-3-4', '四川成都', 1), (1002, '任我行', 1, '1992-2-2', '湖南長沙', 1), (1033, '王語嫣', 0, '1989-12-3', '四川成都', 1), (1572, '嶽不羣', 1, '1993-7-19', '陝西咸陽', 1), (1378, '紀嫣然', 0, '1995-8-12', '四川綿陽', 1), (1954, '林平之', 1, '1994-9-20', '福建莆田', 1), (2035, '東方不敗', 1, '1988-6-30', null, 2), (3011, '林震南', 1, '1985-12-12', '福建莆田', 3), (3755, '項少龍', 1, '1993-1-25', null, 3), (3923, '楊不悔', 0, '1985-4-17', '四川成都', 3), (4040, '隔壁老王', 1, '1989-1-1', '四川成都', 2); -- 刪除學生數據 delete from tb_student where stuid=4040; -- 更新學生數據 update tb_student set stuname='楊過', stuaddr='湖南長沙' where stuid=1001; -- 插入老師數據 insert into tb_teacher (teaid, teaname, teatitle, collid) values (1122, '張三丰', '教授', 1), (1133, '宋遠橋', '副教授', 1), (1144, '楊逍', '副教授', 1), (2255, '範遙', '副教授', 2), (3366, '韋一笑', '講師', 3); -- 插入課程數據 insert into tb_course (couid, couname, coucredit, teaid) values (1111, 'Python程序設計', 3, 1122), (2222, 'Web前端開發', 2, 1122), (3333, '操做系統', 4, 1122), (4444, '計算機網絡', 2, 1133), (5555, '編譯原理', 4, 1144), (6666, '算法和數據結構', 3, 1144), (7777, '經貿法語', 3, 2255), (8888, '成本會計', 2, 3366), (9999, '審計學', 3, 3366); -- 插入選課數據 insert into tb_score (stuid, couid, scdate, scmark) values (1001, 1111, '2017-09-01', 95), (1001, 2222, '2017-09-01', 87.5), (1001, 3333, '2017-09-01', 100), (1001, 4444, '2018-09-03', null), (1001, 6666, '2017-09-02', 100), (1002, 1111, '2017-09-03', 65), (1002, 5555, '2017-09-01', 42), (1033, 1111, '2017-09-03', 92.5), (1033, 4444, '2017-09-01', 78), (1033, 5555, '2017-09-01', 82.5), (1572, 1111, '2017-09-02', 78), (1378, 1111, '2017-09-05', 82), (1378, 7777, '2017-09-02', 65.5), (2035, 7777, '2018-09-03', 88), (2035, 9999, curdate(), null), (3755, 1111, date(now()), null), (3755, 8888, date(now()), null), (3755, 9999, '2017-09-01', 92);
  3. DQL

    -- 查詢全部學生信息 select * from tb_student; -- 查詢全部課程名稱及學分(投影和別名) select couname, coucredit from tb_course; select couname as 課程名稱, coucredit as 學分 from tb_course; -- 查詢全部學生的姓名和性別(條件運算) select stuname as 姓名, case stusex when 1 then '男' else '女' end as 性別 from tb_student; select stuname as 姓名, if(stusex, '男', '女') as 性別 from tb_student; -- 查詢全部女學生的姓名和出生日期(篩選) select stuname, stubirth from tb_student where stusex=0; -- 查詢全部80後學生的姓名、性別和出生日期(篩選) select stuname, stusex, stubirth from tb_student where stubirth>='1980-1-1' and stubirth<='1989-12-31'; select stuname, stusex, stubirth from tb_student where stubirth between '1980-1-1' and '1989-12-31'; -- 查詢姓"楊"的學生姓名和性別(模糊) select stuname, stusex from tb_student where stuname like '楊%'; -- 查詢姓"楊"名字兩個字的學生姓名和性別(模糊) select stuname, stusex from tb_student where stuname like '楊_'; -- 查詢姓"楊"名字三個字的學生姓名和性別(模糊) select stuname, stusex from tb_student where stuname like '楊__'; -- 查詢名字中有"不"字或"嫣"字的學生的姓名(模糊) select stuname, stusex from tb_student where stuname like '%不%' or stuname like '%嫣%'; -- 查詢沒有錄入家庭住址的學生姓名(空值) select stuname from tb_student where stuaddr is null; -- 查詢錄入了家庭住址的學生姓名(空值) select stuname from tb_student where stuaddr is not null; -- 查詢學生選課的全部日期(去重) select distinct scdate from tb_score; -- 查詢學生的家庭住址(去重) select distinct stuaddr from tb_student where stuaddr is not null; -- 查詢男學生的姓名和生日按年齡從大到小排列(排序) -- asc (ascending) - 升序(從小到大)/ desc (descending) - 降序(從大到小) select stuname as 姓名, year(now())-year(stubirth) as 年齡 from tb_student where stusex=1 order by 年齡 desc; -- 聚合函數:max / min / count / sum / avg -- 查詢年齡最大的學生的出生日期(聚合函數) select min(stubirth) from tb_student; -- 查詢年齡最小的學生的出生日期(聚合函數) select max(stubirth) from tb_student; -- 查詢男女學生的人數(分組和聚合函數) select stusex, count(*) from tb_student group by stusex; -- 查詢課程編號爲1111的課程的平均成績(篩選和聚合函數) select avg(scmark) from tb_score where couid=1111; -- 查詢學號爲1001的學生全部課程的平均分(篩選和聚合函數) select avg(scmark) from tb_score where stuid=1001; -- 查詢每一個學生的學號和平均成績(分組和聚合函數) select stuid as 學號, avg(scmark) as 平均分 from tb_score group by stuid; -- 查詢平均成績大於等於90分的學生的學號和平均成績 -- 分組之前的篩選使用where子句 / 分組之後的篩選使用having子句 select stuid as 學號, avg(scmark) as 平均分 from tb_score group by stuid having 平均分>=90; -- 查詢年齡最大的學生的姓名(子查詢/嵌套的查詢) select stuname from tb_student where stubirth=( select min(stubirth) from tb_student ); -- 查詢年齡最大的學生姓名和年齡(子查詢+運算) select stuname as 姓名, year(now())-year(stubirth) as 年齡 from tb_student where stubirth=( select min(stubirth) from tb_student ); -- 查詢選了兩門以上的課程的學生姓名(子查詢/分組條件/集合運算) select stuname from tb_student where stuid in ( select stuid from tb_score group by stuid having count(stuid)>2 ) -- 查詢學生姓名、課程名稱以及成績(鏈接查詢) select stuname, couname, scmark from tb_student t1, tb_course t2, tb_score t3 where t1.stuid=t3.stuid and t2.couid=t3.couid and scmark is not null; -- 查詢學生姓名、課程名稱以及成績按成績從高到低查詢第11-15條記錄(內鏈接+分頁) select stuname, couname, scmark from tb_student t1 inner join tb_score t3 on t1.stuid=t3.stuid inner join tb_course t2 on t2.couid=t3.couid where scmark is not null order by scmark desc limit 5 offset 10; select stuname, couname, scmark from tb_student t1 inner join tb_score t3 on t1.stuid=t3.stuid inner join tb_course t2 on t2.couid=t3.couid where scmark is not null order by scmark desc limit 10, 5; -- 查詢選課學生的姓名和平均成績(子查詢和鏈接查詢) select stuname, avgmark from tb_student t1, (select stuid, avg(scmark) as avgmark from tb_score group by stuid) t2 where t1.stuid=t2.stuid; select stuname, avgmark from tb_student t1 inner join (select stuid, avg(scmark) as avgmark from tb_score group by stuid) t2 on t1.stuid=t2.stuid; -- 內鏈接(inner join)- 只有知足鏈接條件的記錄纔會被查出來 -- 外鏈接(outer join)- 左外鏈接(left outer join) / 右外鏈接(right outer join) / 全外鏈接 -- 查詢每一個學生的姓名和選課數量(左外鏈接和子查詢) select stuname, ifnull(total, 0) from tb_student t1 left outer join (select stuid, count(stuid) as total from tb_score group by stuid) t2 on t1.stuid=t2.stuid;
  4. DCL

    -- 建立名爲hellokitty的用戶 create user 'hellokitty'@'%' identified by '123123'; -- 將對school數據庫全部對象的全部操做權限授予hellokitty grant all privileges on school.* to 'hellokitty'@'%'; -- 召回hellokitty對school數據庫全部對象的insert/delete/update權限 revoke insert, delete, update on school.* from 'hellokitty'@'%';

相關知識

範式理論 - 設計二維表的指導思想

  1. 第一範式:數據表的每一個列的值域都是由原子值組成的,不可以再分割。
  2. 第二範式:數據表裏的全部數據都要和該數據表的鍵(主鍵與候選鍵)有徹底依賴關係。
  3. 第三範式:全部非鍵屬性都只和候選鍵有相關性,也就是說非鍵屬性之間應該是獨立無關的。

數據完整性

  1. 實體完整性 - 每一個實體都是獨一無二的
    • 主鍵(primary key) / 惟一約束 / 惟一索引(unique)
  2. 引用完整性(參照完整性)- 關係中不容許引用不存在的實體
    • 外鍵(foreign key)
  3. 域完整性 - 數據是有效的
    • 數據類型及長度
    • 非空約束(not null)
    • 默認值約束(default)
    • 檢查約束(check)

數據一致性

  1. 事務:一系列對數據庫進行讀/寫的操做。

  2. 事務的ACID特性

    • 原子性:事務做爲一個總體被執行,包含在其中的對數據庫的操做要麼所有被執行,要麼都不執行
    • 一致性:事務應確保數據庫的狀態從一個一致狀態轉變爲另外一個一致狀態
    • 隔離性:多個事務併發執行時,一個事務的執行不該影響其餘事務的執行
    • 持久性:已被提交的事務對數據庫的修改應該永久保存在數據庫中

Python數據庫編程

咱們用以下所示的數據庫來演示在Python中如何訪問MySQL數據庫。

drop database if exists hrs; create database hrs default charset utf8; use hrs; drop table if exists tb_emp; drop table if exists tb_dept; create table tb_dept ( dno int not null comment '編號', dname varchar(10) not null comment '名稱', dloc varchar(20) not null comment '所在地', primary key (dno) ); insert into tb_dept values (10, '會計部', '北京'), (20, '研發部', '成都'), (30, '銷售部', '重慶'), (40, '運維部', '深圳'); create table tb_emp ( eno int not null comment '員工編號', ename varchar(20) not null comment '員工姓名', job varchar(20) not null comment '員工職位', mgr int comment '主管編號', sal int not null comment '員工月薪', comm int comment '每個月補貼', dno int comment '所在部門編號', primary key (eno) ); alter table tb_emp add constraint fk_emp_dno foreign key (dno) references tb_dept (dno); insert into tb_emp values (7800, '張三丰', '總裁', null, 9000, 1200, 20), (2056, '喬峯', '分析師', 7800, 5000, 1500, 20), (3088, '李莫愁', '設計師', 2056, 3500, 800, 20), (3211, '張無忌', '程序員', 2056, 3200, null, 20), (3233, '丘處機', '程序員', 2056, 3400, null, 20), (3251, '張翠山', '程序員', 2056, 4000, null, 20), (5566, '宋遠橋', '會計師', 7800, 4000, 1000, 10), (5234, '郭靖', '出納', 5566, 2000, null, 10), (3344, '黃蓉', '銷售主管', 7800, 3000, 800, 30), (1359, '胡一刀', '銷售員', 3344, 1800, 200, 30), (4466, '苗人鳳', '銷售員', 3344, 2500, null, 30), (3244, '歐陽鋒', '程序員', 3088, 3200, null, 20), (3577, '楊過', '會計', 5566, 2200, null, 10), (3588, '朱九真', '會計', 5566, 2500, null, 10);

在Python 3中,咱們一般使用純Python的三方庫PyMySQL來訪問MySQL數據庫,它應該是目前最好的選擇。

  1. 安裝PyMySQL。

    pip install pymysql
  2. 添加一個部門。

    import pymysql
    
    
    def main(): no = int(input('編號: ')) name = input('名字: ') loc = input('所在地: ') # 1. 建立數據庫鏈接對象 con = pymysql.connect(host='localhost', port=3306, database='hrs', charset='utf8', user='root', password='123456') try: # 2. 經過鏈接對象獲取遊標 with con.cursor() as cursor: # 3. 經過遊標執行SQL並得到執行結果 result = cursor.execute( 'insert into tb_dept values (%s, %s, %s)', (no, name, loc) ) if result == 1: print('添加成功!') # 4. 操做成功提交事務 con.commit() finally: # 5. 關閉鏈接釋放資源 con.close() if __name__ == '__main__': main()
  3. 刪除一個部門。

    import pymysql
    
    
    def main(): no = int(input('編號: ')) con = pymysql.connect(host='localhost', port=3306, database='hrs', charset='utf8', user='root', password='123456', autocommit=True) try: with con.cursor() as cursor: result = cursor.execute( 'delete from tb_dept where dno=%s', (no, ) ) if result == 1: print('刪除成功!') finally: con.close() if __name__ == '__main__': main()
  4. 更新一個部門。

    import pymysql
    
    
    def main(): no = int(input('編號: ')) name = input('名字: ') loc = input('所在地: ') con = pymysql.connect(host='localhost', port=3306, database='hrs', charset='utf8', user='root', password='123456', autocommit=True) try: with con.cursor() as cursor: result = cursor.execute( 'update tb_dept set dname=%s, dloc=%s where dno=%s', (name, loc, no) ) if result == 1: print('更新成功!') finally: con.close() if __name__ == '__main__': main()
  5. 查詢全部部門。

    import pymysql
    from pymysql.cursors import DictCursor def main(): con = pymysql.connect(host='localhost', port=3306, database='hrs', charset='utf8', user='root', password='123456') try: with con.cursor(cursor=DictCursor) as cursor: cursor.execute('select dno as no, dname as name, dloc as loc from tb_dept') results = cursor.fetchall() print(results) print('編號\t名稱\t\t所在地') for dept in results: print(dept['no'], end='\t') print(dept['name'], end='\t') print(dept['loc']) finally: con.close() if __name__ == '__main__': main()
  6. 分頁查詢員工信息。

    import pymysql
    from pymysql.cursors import DictCursor class Emp(object): def __init__(self, no, name, job, sal): self.no = no self.name = name self.job = job self.sal = sal def __str__(self): return f'\n編號:{self.no}\n姓名:{self.name}\n職位:{self.job}\n月薪:{self.sal}\n' def main(): page = int(input('頁碼: ')) size = int(input('大小: ')) con = pymysql.connect(host='localhost', port=3306, database='hrs', charset='utf8', user='root', password='123456') try: with con.cursor() as cursor: cursor.execute( 'select eno as no, ename as name, job, sal from tb_emp limit %s,%s', ((page - 1) * size, size) ) for emp_tuple in cursor.fetchall(): emp = Emp(*emp_tuple) print(emp) finally: con.close() if __name__ == '__main__': main()
相關文章
相關標籤/搜索