精华内容
下载资源
问答
  • 大型网站架构关键技术介绍 1. pv值(page views),访问量大 带来问题 a. 流量大 10000000*2m ->解决方案 买带宽 ,优化程序(处理图片) b. 并发量,同时访问网站的人多.,解决方案 对程序的架构重新设计.->服务器集群...

    大型网站关键技术介绍

    1. pv值(page views),访问量大 

    带来问题

    a. 流量大 10000000*2m ->解决方案 买带宽 ,优化程序(处理图片)

    b. 并发量,同时访问网站的人多.,解决方案

    对程序的架构重新设计.->服务器集群  示意图:

    1. 数据量大->10亿记录

    解决方法是

    a. 表的设计合理 

    b. 分表技术(垂直分割,水平分割) c. 建立索引 d. 读写分离 e. mysql配置优化(调整最大并发量,定时对数据库碎片整理,备份 crontab) f.硬件升级) 

    c. 页面静态化 

    d. 缓存技术(memcached)


    页面静态化

    简单解释一下页面静态化:




    看几个概念,然后写代码:

    静态网址: 比如 http://localhost/abc.html  即,如果我们访问的页面是静态页面,我们把这个url称为静态网站.

    特点: 1. 利用seo (search engine optimization) 搜索引擎优化2. 访问速度快 3. 防止sql注入

    http://localhost/index.php?u=xx&p=/*11288 */

    如果我们写程序 loginCheck.php

    <?php
    	$id=$_GET[‘id’];
    	$pwd=$_GET[‘pwd’];
    	$sql=”select  pwd  from users where id=$id”;
    	$res=mysql_query($sql);
    	if($pwd= = =从数据库中取出的密码){
    		//说明该用户存在
      }else{
      	//说明用户密码错误!
      }

    动态网址 :比如 http://localhost/news.php?id=112 , 即,访问的是一个PHP页面,可以传入参数.称为动态网之.

    特点: 1. 不利用SEO 2. 访问速度慢 3. 有被注入sql可能

     

    伪静态网址 : 在实际开发中,我们希望达到这样目的,把下面的网址

    http://localhost/ news.php?lang=cn&class=sprot&id=2

    修改成如下网址

    http://localhost/news-cn-sport-id2.html 

    上面的网址,我们称为伪静态网址 :

    特点: 1. 利用SEO 2. 防止注入  3. 他任然要访问数据库,速度没有变化

     

     页面静态化技术分类:

    从方式看(1. 真静态 2.伪静态)

    从范围看(1.全局静态2. 局部静态化[ajax+jquery])

     介绍一款测试压力的工具 ab.exe  该工具程序是apache自带的, 大家在工作中可以使用该工具来测试自己的网站并发量大小,和某个页面的访问时间

     基本用法,进入到cmd 控制台

    ab.exe –n 访问的总次数 –c  有多少人访问(并发量) 访问的页面url

    举例说明:

    ab.exe –n 10000 –c 100 http://locahost/test.php

    当我们把 –c 调整到1000时,发现apache瘫痪. 给大家说下如何调整apache的最大并发量.

    MPM (多路处理模块, 即 apache采用怎样的方式来处理并发.), 主要有三种方式 

    1. perfork 预处理进程方式

    2. worker 工作模式

    3. winnt  这个一般说是windows采用的.

    原理示意图:

    u 如何设置我们的apache的最大并发数 ,步骤如下:

    (1) 在httpd.conf 文件中 修改

    # Server-pool management (MPM specific)

    Include conf/extra/httpd-mpm.conf

    (2) 确定当前的apahce是什么MPM模式

    进入到 apache/bin 

    httpd.exe –l

    说明: 看 mpm_xxx.c 如果xxx是 winnt 说明是winnt  ,另外还可能是 perfork 或者  worker

    (1) 修改httpd-mpm.conf 文件.

    <IfModule mpm_winnt_module>

        ThreadsPerChild      1000  

        MaxRequestsPerChild    0

    </IfModule>

    (2) 重启apahce ,测试

     

     

    因为在linux下,一般说采用的MPM是 perfork模式,我们看看如何配置.

     

    <IfModule mpm_prefork_module>

        StartServers          5

        MinSpareServers       5

        MaxSpareServers      10

        MaxClients          150   #并发量

      MaxRequestsPerChild   0  #一个进程对应的线程数,对 worker更用.

    </IfModule>

     

    给大家一个合理的建议配置. 对大部分网站,中型网站,配置:


    <IfModule mpm_prefork_module>
                  StartServers         5      #预先启动
                  MinSpareServers      5
                  MaxSpareServers      10  #最大空闲进程
                  ServerLimit          1500   #用于修改apache编程参数
                  MaxClients           1000   #最大并发数
                  MaxRequestsPerChild  0

    </IfModule>

     

    如果你的网站pv值  百万 

    ServerLimit          2500   #用于修改apache编程参数
    MaxClients           2000   #最大并发数

     

     

    最后有一个关于html和php的访问效率图 :


    分享一下页面seo技巧:

    如果一个图片希望被百度到

    <img alt=’小狗’ src=’’/> 

    在网站前台,我们建议 不要使用frame框架,不利用seo

    如果我们给图片或者视频取名字,尽量简短.

     

     

    页面静态化的技术实现有两种方式

    1. 使用PHP自己的缓存机制

     先说明一下OB缓存的机制.

    ob1.php 代码:说明的ob的各个用法->项目中

    ob2.php 代码,说明了浏览器缓存存在.



    说明: 在php5.2这个版本 在php.ini有一个配置 output_buffering  ,默认是关闭,如果是关闭,这刚才的代码就会警告.

     

    ☞ 如何打开ob缓存

    ① 配置php.ini 文件 output_buffering = 4096

    ② 直接在程序中  ob_start();

     

    1. 使用模板替换技术实现(正则表达式)

    使用ob缓存机制,完成一个简单的新闻管理系统-页面静态化(目标是实现全站静态化)


     

    开发步骤:

    (1) 做的页面:


    (1)  简单的分析页面

     

    (2) 创建数据表

     

    create table news(

    id int unsigned primary key auto_increment, /*新闻id,做成自增*/

    title varchar(128) not null default ‘’, /*标题*/

    content varchar(256) not null default ‘’, /*新闻内容*/

    filename varchar(32) not null default ‘’ /*将来这个新闻对应静态页面*/

    ) engine=MyISAM charset utf8

     

    测试数据:

    insert into news (title,content) values('hello1','北京你好');

    insert into news (title,content) values('hello2','四川你好');

     

     

    (3) 走码

    到此,我们已经实现了传统的查询任务, 

    分析代码后,我们发现问题是,因为新闻内容相对稳定,所以没有必要每次都查询。优化的思路是: 当第一人查看某个新闻时,我们就生成一个对应的静态页面,当后面的人在查看,直接返回该静态页面即可.

    代码实现: 

    newsList.php 代码

    showNews.php代码


    再想想,上面的代码缺点是什么?

    cms(内容管理系统,新闻,软件发布, 文章管理) 内容就固定不变,我们不能接受.

    解决方案:

    1 我设一个超时,30s, 我们保证30内不去修改,超过30秒,就更新一把.

    走码:

    2 上面的解决方案有时间延时,所以如果我们希望静态化没有时间延时,就应该使用模板提换技术来搞定.

    思路: 图.





    代码实现:整理

    newslist.php 



    addnews.html页面



    newsAction.php 页面



    思考题:

    ① 请大家完成更新新闻的这个页面静态处理

    ② 如何把newsList.php 也做成一个静态页面.->思想

     

      任务是: 把新闻管理系统的首页面也静态化.

    通用的CMS系统的示意图:


    对我们的新闻管理系统首页静态化

     

    思考: 我们能不能再添加新闻后,就直接静态化首页面, 如果你的确希望只要首页的内容有变化,就立即更新,最后的代理整理:

    把所有的动态页面,放入到manage文件夹:

    manage.html

    <html>
    <head>
    <meta http-equiv='content-type' content='text/html;charset=utf-8'/>
    </head>
    <h1>管理新闻</h1>
    <hr/>
    <a href='addNews.html'>添加新闻</a>|
    <a href='newsList.php'>更新首页</a>|
    <a href='xxx.php'>列出所有信息</a>
    </html>
    
    addNews.html
    <head>
    <title>新闻标题</title>
    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
    </head>
    
    <!--我们在添加新闻时,就同时生成一个对应的新闻页面(比如你设计好的一个新闻内容显示模板)-->
    
    <form action="newsAction.php" method="post">
    <table>
    <tr><td>新闻标题</td><td><input type="text" name="title"/></td></tr>
    <tr><td>新闻内容</td><td><textarea cols="50" rows="10" name="content"></textarea></td></tr>
    <tr><td><input type="submit" value="添加"/></td><td><input type="reset" value="重新填写"/></td></tr>
    <!--隐藏区-->
    <input type='hidden' name='oper' value='add'/>
    </table>
    </form>
    </html>

    newsAction.php

    <?php
    
    	//处理用户的添加/更新/删除...请求
    	//先获取 oper值
    	$oper=$_POST['oper'];
    	if($oper==='add'){
    		//接收用户的新闻的各个信息
    		$title=$_POST['title'];
    		$content=$_POST['content'];
    		//把新闻添加到数据库
    		//这里大家可以使用工具类完成.
    		$con=mysql_connect("localhost","root","root");
    		if(!$con){
    			die("连接失败");
    		}
    		mysql_select_db("newssys",$con);
    		$sql="insert into news values(null,'$title','$content','')";
    		//echo $sql;
    		if(mysql_query($sql,$con)){
    			//生成静态文件.
    			$id=mysql_insert_id();
    			$html_filename='news-id'.$id.'.html';
    			$html_fp=fopen("../".$html_filename,'w');
    			//把模板文件读取.
    			$fp=fopen('news.tpl','r');
    			//循环读取
    			//如果没有读到文件的最后,就一直读取
    			while(!feof($fp)){
    				//一行行读.
    				$row=fgets($fp);
    				//把占位符替换掉->小函数 myreplace
    				//问题?
    				$row=str_replace('%title%',$title,$row);
    				$row=str_replace('%content%',$content,$row);
    				fwrite($html_fp,$row);
    			}
    			//关闭文件
    			fclose($html_fp);
    			fclose($fp);
    			echo "恭喜你,添加成功<a href='manage.html'>管理新闻</a>";
    			//怎样让首页面立即更新.
    			include "newsList.php";
    
    		}else{
    			die('添加失败');
    		}
    	}else if($oper==='update'){
    	
    	}else if($oper==='delete'){
    	
    	}

    newsList.php

    <?php
    	//列出新闻列表
    	//这里,我使用最简单的方法来操作,没有使用mvc模式
    	//这里你们可以使用工具类完成. db.class.php
    	$con=mysql_connect("localhost","root","root");
    	if(!$con){
    		die("连接失败");
    	}
    	mysql_select_db("newssys",$con);
    	$sql="select * from news";
    	$res=mysql_query($sql,$con);
    		ob_start();
    		echo "<head><meta http-equiv='content-type' content='text/html;charset=utf-8' /></head>";
    		echo "<h1>新闻列表</h1>";
    		echo "<table>";
    		echo "<tr><td>id</td><td>标题</td><td>查看详情</td><td>修改新闻</td></tr>";
    		//循环的取出新闻列表
    		while($row=mysql_fetch_assoc($res)){
    		echo '<tr><td>'.$row['id'].'</td><td>'.$row['title'].'</td><td><a href="news-id'.$row['id'].'.html">查看详情</a></td><td><a href="#">修改页面</a></td></tr>';
    		}
    		echo "</table>";
    		$str_ob=ob_get_contents();
    		file_put_contents('../index.html',$str_ob);
    		//这里关闭资源.
    		//清空ob
    		ob_clean();
    		echo "恭喜你,首页面更新成功<a href='../index.html'>点击查看最新新闻列表</a>";
    
    		mysql_free_result($res);
    		mysql_close($con);
    

    完毕,大家可以进一步深入的完成修改和删除.

     真静态的优缺点分析

    优点: 1. 利用SEO  2. 访问速度快. 3. 防止sql注入

    缺点: 1. 因为真静态会生成大量的html文件,占用磁盘空间, 如果你把所有的静态页,都放在同一文件夹,寻找文件的速度随着文件的增多,速度变慢,因此可以考虑创建子文件夹来放.

     

    在以下情况不建议使用真静态:

    1.实时性要求高的网站或者页面.(股票、基金)

    2.数据量大,同时查询一次后,以后很少查询(国家学历认证网,电信话费查询系统.)

    3.不愿意被seo到的页面或者网站

     

     

     

     伪静态技术

    在我们实际开发中,有需要,不希望使用真静态., 但是有希望利于SEO,可以考虑使用伪静态.

    http://localhost/news.php?type=music&id=100   

    我们希望这个地址可以用下面的访问url来替换

    http://localhost/news-music-id100.html

     

     

    上面的问题可以使用伪静态

     

    1. 实现方式有 直接使用正则表达式来完成

    2. 使用apache自带的rewrite机制来完成

     

     

    看需求:

    http://localhost/content.php/1,122,8912.html

     

    我希望上面的地址

    http://localhost/content.php?a=1&b=122&c=8912

     

    实现思路: 

    1. 我们可以使用str 函数,来进行分割处理->可行,但是不够灵活

    2. 使用正则来处理

     

    代码是:

    $path_info=$_SERVER['PATH_INFO'];
    	$reg='/(\d+),(\d+),(\d+)\.html$/i';
    
    	preg_match($reg,$path_info,$res);
    
    	echo "<pre>";
    	print_r($res);
    	echo "</pre>";

    在实际开发中,我们实现伪静态,用的更多的还是rewrite 机制,但是他任然是以正则技术为基础的.

     

    我们看看如何实现:

     

    说明: 

    比如 http://www.hsp.com/news.php?type=music&id=100  

    希望上面的url 变成 

    http://www.hsp.comt/news-music-id100.html 

     

     

    原理图


    步骤开始:

    (1) 启用rewrite模块,在默认情况下,没有启用

    修改httpd.conf文件

    #启动rewrite模块

    LoadModule rewrite_module modules/mod_rewrite.so

     

    确认是否启动成功

    <?php phpinfo();?>

    (2) 配置我们的虚拟主机

    httpd.conf 打开虚拟主机的配置文件

     

    # Virtual hosts

    Include conf/extra/httpd-vhosts.conf

     

    修改 httpd-vhost.conf

    <VirtualHost *:80>
        DocumentRoot "C:/myenv/apache/htdocs/static2"
        #Directory配置节点,用于指定该目录下的文件或是图片.的访问权限
        #设置虚拟主机的错误页面,欢迎页面 
        <Directory "C:/myenv/apache/htdocs/static2">
        </Directory>
    </VirtualHost>

    (1) 在hosts文件中,配置ip和主机的对应关系

    127.0.0.1 www.hsp.com

    (2) 这时我们访问 http//www.hsp.com/news.php

    我们可以访问到该页面.

    ☞ 一个重要的知识点:

    在apache服务器中,如果某个文件夹,没有指定访问权限,则以上级目录的权限为准,如果他自己指定了访问权限,则以自己的为准.

    请注意,在配置访问权限的时候,顺序很重要:

    #Order allow,deny 表示先看allow ,在看deny,留下的就是可以访问

        Order deny,allow

        Deny from all

        allow from 127.0.0.1

     

    (3) 关于<Directory> 节点配置必须掌握

    比较完整的配置文件

    第一种配置方式

    <VirtualHost *:80>

        DocumentRoot "C:/myenv/apache/htdocs/static2"

        #Directory配置节点,用于指定该目录下的文件或是图片.的访问权限

        #设置虚拟主机的错误页面,欢迎页面 

        ServerName www.hsp.com

        <Directory "C:/myenv/apache/htdocs/static2">

    #这里可以指定是否让人访问

    #Allow from all

    #是否列出文件目录结构

    # 如果希望列出 indexes 不希望 none

    #Options indexes

    #如何配置网站的首页面

    DirectoryIndex abc.html abc2.html

    #如何配置404错误页面,引导用户引入新页面

    errorDocument 404 /404.html

    #配置我们的rewrite规则

    RewriteEngine On

    #rewrite的规则 如果 aaa.html 就跳转到news.php 

    #$1 表示反向引用,第一个子表达式的内容

    #说明如果在正则规范中直接引用子表达式的内容,则使用\n

    #如果是在后面因为,则使用$n

    RewriteRule news-([a-zA-Z]+)-id(\d+)\.html$  news.php?type=$1&id=$2

        </Directory>

    </VirtualHost>

     

    特别说明: 容易犯的错误,一定要记住启用rewrite模块.


    思考: 上面我们配置都要去修改 httpd-vhost.文件,但管理员不给你这个权限,怎么办?

    思路: 可以把配置,写到 .htaccess文件.

     

    第二种配置方式: 即把一部分配置放在 http-vhost.conf 文件, 把rewrite 规则放在 .htaccess

    <VirtualHost *:80>

        DocumentRoot "C:/myenv/apache/htdocs/static2"

        #Directory配置节点,用于指定该目录下的文件或是图片.的访问权限

        #设置虚拟主机的错误页面,欢迎页面 

        ServerName www.hsp.com

        <Directory "C:/myenv/apache/htdocs/static2">

    #这里可以指定是否让人访问

    #Allow from all

    #是否列出文件目录结构

    # 如果希望列出 indexes 不希望 none

    #Options indexes

    #如何配置网站的首页面

    DirectoryIndex abc.html abc2.html

    #如何配置404错误页面,引导用户引入新页面

    errorDocument 404 /404.html

    #如果你配置了allowoverride all 这表示到对应的目录的.htaccess去匹配规则

    allowoverride all

        </Directory>

    </VirtualHost>

     

    在对应的文件下 .htaccess文件

    <IfModule rewrite_module>

    #如果rewrite 模块启用

    #配置我们的rewrite规则

    RewriteEngine On

    #rewrite的规则 如果 aaa.html 就跳转到news.php 

    #$1 表示反向引用,第一个子表达式的内容

    #说明如果在正则规范中直接引用子表达式的内容,则使用\n

    #如果是在后面因为,则使用$n

    RewriteRule news-([a-zA-Z]+)-id(\d+)\.html$  news.php?type=$1&id=$2

    #RewriteRule aaa.html  news.php

    </IfModule>

     

    请注意: 项目中的 .htaccess文件的配置也是继承管理

     

    第三种配置方法:

    http-vhost.conf

    <VirtualHost *:80>

        DocumentRoot "C:/myenv/apache/htdocs/static2"

        #Directory配置节点,用于指定该目录下的文件或是图片.的访问权限

        #设置虚拟主机的错误页面,欢迎页面 

        ServerName www.hsp.com

        <Directory "C:/myenv/apache/htdocs/static2">

    #如果你配置了allowoverride all 这表示到对应的目录的.htaccess去匹配规则

    allowoverride all

        </Directory>

    </VirtualHost>

     

    .htacces文件 

     

    #这里可以指定是否让人访问

    #Allow from all

    #是否列出文件目录结构

    # 如果希望列出 indexes 不希望 none

    #Options indexes

    #如何配置网站的首页面

    DirectoryIndex abc.html abc2.html

    #如何配置404错误页面,引导用户引入新页面

    errorDocument 404 /404.html

    <IfModule rewrite_module>

    #如果rewrite 模块启用

    #配置我们的rewrite规则

    RewriteEngine On

    #rewrite的规则 如果 aaa.html 就跳转到news.php 

    #$1 表示反向引用,第一个子表达式的内容

    #说明如果在正则规范中直接引用子表达式的内容,则使用\n

    #如果是在后面因为,则使用$n

    RewriteRule news-([a-zA-Z]+)-id(\d+)\.html$  news.php?type=$1&id=$2

    #RewriteRule aaa.html  news.php

    </IfModule>


     伪静态的实际运用

    1. 在一个项目中有两个文件夹,public 和 private ,public文件夹的图片可以被所有人访问.

    private只能自己访问.请问如何实现.

     

    第一个方法是. 在 public 和 private 下创建 .htaccess 文件,对于private 只让127.0.0.1访问 public 文件都可以访问

    private 文件夹下的配置 .htaccess


    Order deny, allow

    deny from all

    allow from 127.0.0.1

     

    这个的缺点是,没有办法去控制,本apahce的另外一个虚拟主机请求., 另外不能精细化控制,于是我们就换了一个思路使用 http的 referer头信息搞定这个防盗链需求.

     

    第二方法.在private 文件夹下 .htaccess

    <ifModule rewrite_module>

    RewriteEngine On

    #你怎么知道,这个请求就是www.hsp.com发来的. referer

    #如果你请求的是一个jpg图片, 就禁止

    RewriteCond %{HTTP_REFERER} !www.hsp.com  

    RewriteRule .*\.jpg -[F]

    </ifModule>

     

    ☞ 说一下如果你的RewriteCond有多个条件

    <ifModule rewrite_module>

    RewriteEngine On

    #你怎么知道,这个请求就是www.hsp.com发来的. referer

    #如果你请求的是一个jpg图片, 就禁止

    RewriteCond %{HTTP_REFERER} !www.hsp.com 

    #RewriteCond %{ REQUEST_FILENAME} !-f

    RewriteRule .*\.jpg -[F]

    </ifModule>

    1. 关于404的说明  就是对于ie浏览器而言,404页面的内容大小不能低于512b字节

    2. 如果你在<Directory>节点配置了 Options Indexes , 同时你有启用了 rewriteEngine On你会看到403 的禁用提示., 原因是因为 我们的父级目录的Options 本身就配好 ,

    Options Indexes FollowSymLinks  ,所以,我们要么就不配,要么就一样.

     伪静态实际运用案例2



    www.hsp.com 的主目录下,我们编写了 .htaccess文件,并增加规则


    #增加规则

    [img=http://img.bbs.csdn.net/upload/201403/07/1394181458_228087.png][/img]
    [img=http://img.bbs.csdn.net/upload/201403/07/1394181464_304731.png][/img]

     伪静态的实际运用3->引出rewriteCond的使用

    思考: 可能有条件指令.

    网上找到方案:

    最后的配置在 .htaccess 

    #如果请求的不是一个文件

    RewriteCond %{REQUEST_FILENAME} !-f

    #并且不是一个目录

    RewriteCond %{REQUEST_FILENAME} !-d

    RewriteRule ccc.html index.php

     

    伪静态的实际运用案例4:

    在TP框架中使用rewrite简化url地址,目的是SEO, 步骤

    1. 在httpd.conf 文件中启用rewrite模块

    2. 在虚拟主机中配置 allowoverride all

    3. 在tp的配置文件中 conf/config 文件把 URL_MODEL 配置2

    4. 在tp框架的入口目录增加 .htaccess文件 写入规则



    u 伪静态的特点


    u 如何选用伪静态还是真静态,还是不静态?




    展开全文
  • MySQL 视图技术

    千次阅读 2016-06-11 14:03:56
    是不是和创建普通表结构很相似啊 关键就在于AS语句咯。因为那是视图数据的来源。 我们需要关注的就是 AS 后面的实现,和正常写SQL语句很类似,就不过多的叙述了。 使用创建好的视图 使用创建好的视图也是...

    以前也只是知道数据库中有视图这么个概念,但是没有去深究,今天正好有时间,就来总结一下吧。

    视图的定义

    视图就是从一个或多个表中,导出来的表,是一个虚拟存在的表。视图就像一个窗口(数据展示的窗口),通过这个窗口,可以看到系统专门提供的数据(也可以查看到数据表的全部数据),使用视图就可以不用看到数据表中的所有数据,而是只想得到所需的数据。 在数据库中,只存放了视图的定义,并没有存放视图的数据,数据还是存储在原来的表里,视图的数据是依赖原来表中的数据的,所以原来的表的数据发生了改变,那么显示的视图的数据也会跟着改变,例如向数据表中插入数据,那么在查看视图的时候,会发现视图中也被插入了同样的数据。 视图在外观上和表很相似,但是它不需要实际上的物理存储,视图实际上是由预定义的查询形式的表所组成的。 视图可以包含表的全部或者部分记录,也可以由一个表或者多个表来创建,当我们创建一个视图的时候,实际上是在数据库里执行了SELECT语句,SELECT语句包含了字段名称、函数、运算符,来给用户显示数据。 在数据库中,视图的使用方式与表的使用方式一致,我们可以像操作表一样去操作视图,或者去获取数据。 一般来说,我们只是利用视图来查询数据,不会通过视图来操作数据。

    注意:基于已经存在的视图,我们可以创建新的视图。
    视图和表的主要的区别就在于是否占用物理的存储空间。表是占用实际空间的,视图是基于表的更高层的存在。

    视图释义

    我们可以把视图看做一个连接着黑盒子的管道,而表就可以比喻成那个黑盒子。有了视图,我们就可以不关心底层的表是怎么实现的,我们只需要关注怎么能从这个管道获取数据。底层表结构的变化可以通过修改视图来达到不更改应用,同样反过来也成立。

    创建表

    视图很普通,我们没必要将它想象的多么复杂,看成是一个虚拟存在的表结构就行了。在建立视图之前,我们需要有物理存在的表结构才行。

    • 建表:
    mysql> show tables;
    +--------------------+
    | Tables_in_practice |
    +--------------------+
    | course             |
    | sc                 |
    | student            |
    +--------------------+
    3 rows in set (0.00 sec)
    
    mysql> create table author(
        -> id int(10) not null primary key auto_increment,
        -> name varchar(30) not null
        -> );
    Query OK, 0 rows affected (0.03 sec)
    
    mysql> create table book(
        -> id int(10) not null primary key auto_increment,
        -> title varchar(30) not null);
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> show tables;
    +--------------------+
    | Tables_in_practice |
    +--------------------+
    | author             |
    | book               |
    | course             |
    | sc                 |
    | student            |
    +--------------------+
    5 rows in set (0.00 sec)
    
    

    填充数据

    表结构已经创建完成,接下来就先填充点数据进去吧。

    mysql> insert into author(name) values('fenger'),('zhangsan'),('lisi'),('wangwu'),('zhaoliu');
    Query OK, 5 rows affected (0.00 sec)
    Records: 5  Duplicates: 0  Warnings: 0
    
    mysql> insert into book(title) values('Programming in C'),('C++ Primer'),('Thinking in Java'),('Effective Python'),('The King ,PHP');
    Query OK, 5 rows affected (0.00 sec)
    Records: 5  Duplicates: 0  Warnings: 0
    

    创建视图

    下面就在这些表的基础上创建一个最简单的视图吧。作用是用于连接两张表。

    mysql> create view myview as
        -> select name , title from author,book;
    Query OK, 0 rows affected (0.01 sec)
    

    是不是和创建普通表结构很相似啊 关键就在于AS语句咯。因为那是视图数据的来源。

    我们需要关注的就是AS后面的实现,和正常写SQL语句很类似,就不过多的叙述了。

    使用创建好的视图

    使用创建好的视图也是很简单的,类似于使用正常的表。如下:

    mysql> select * from myview ;
    +----------+------------------+
    | name     | title            |
    +----------+------------------+
    | fenger   | Programming in C |
    | zhangsan | Programming in C |
    | lisi     | Programming in C |
    | wangwu   | Programming in C |
    | zhaoliu  | Programming in C |
    | fenger   | C++ Primer       |
    | zhangsan | C++ Primer       |
    | lisi     | C++ Primer       |
    | wangwu   | C++ Primer       |
    | zhaoliu  | C++ Primer       |
    | fenger   | Thinking in Java |
    | zhangsan | Thinking in Java |
    | lisi     | Thinking in Java |
    | wangwu   | Thinking in Java |
    | zhaoliu  | Thinking in Java |
    | fenger   | Effective Python |
    | zhangsan | Effective Python |
    | lisi     | Effective Python |
    | wangwu   | Effective Python |
    | zhaoliu  | Effective Python |
    | fenger   | The King ,PHP    |
    | zhangsan | The King ,PHP    |
    | lisi     | The King ,PHP    |
    | wangwu   | The King ,PHP    |
    | zhaoliu  | The King ,PHP    |
    +----------+------------------+
    25 rows in set (0.00 sec)
    

    由于没有SQL语句很简单,没有加约束,所以展示的数据可能有点不太美观。不过视图的功能已经实现了。优化的话还需要我们来自己实现。

    视图算法

    这里看似高深,其实就是创建视图的时候可选的参数而已。
    ALGORITHM=

    - UNDEFINED:MYSQL自动选择要使用的算法
    
    - MERGE:使用视图的语句与视图的定义是合并在一起的,视图定义的某一部分取代语句对应的部分
    
    - TEMPTABLE:临时表,视图的结果存入临时表,然后使用临时表来执行语句
    

    WHIT [CASCADED|LOCAL] CHECK OPTION:表示更新视图的时候,要保证在视图的权限范围之内:

    - CASCADED 默认值,表示更新视图的时候,要满足视图和表的相关条件
    
    - LOCAL:表示更新视图的时候,要满足该视图定义的一个条件即可
    

    使用WITH[CASADED|LOCAL] CHECK OPTION 宣子昂可以保证数据的安全性。

    创建一个完整的视图的小例子:
    CREATE ALGORITHM=UNDEFINED VIEW myview(name,title) AS SELECT

    name,title FROM author,book WITH CASCADED CHECK OPTION;

    查看已经创建好的视图

    查看视图有如下几种方式:


    • DESC “ViewName”
    • DESCRIBE “ViewName”
    • SHOW COLUMNS FROM “ViewName”
    • SHOW TABLE STATUS LIKE “ViewName”
    • SHOW CREATE VIEW “ViewName”

    详细性的介绍可以参考下面的这篇文章,我就不在这重复的造轮子了。http://www.cnblogs.com/4php/p/4108157.html

    总结

    视图就是一个特殊的不占用物理存储的表,基于表而又高于表的存在。我们要充分的利用视图的优点,来优化我们的应用程序的开发!

    展开全文
  • 数据库MySQL详解

    万次阅读 多人点赞 2018-07-24 20:03:47
    全网最详细MySQL教程,2021.1再次更新70%的内容,MySQL 8.0 + Navicat 15

     

    目录

    第1章 数据库

    1.1 数据库概述

    1.2 数据库表

    1.3 表数据

    第2章 MySql数据库

    2.1 MySql安装

    2.2 登录MySQL数据库

    2.3 SQLyog(MySQL图形化开发工具,我个人用的Navicat)

    2.4 MySQL配置文件

    第3章 SQL语句

    3.1 SQL语句

    3.2 SQL通用语法

    3.3 数据库操作:database

    3.4 表结构相关语句

    3.4.1 创建表

    3.4.2 查看表

    3.4.3 删除表

    3.4.4 修改表结构格式

    3.5 DOS操作数据乱码解决

    第4章 字段属性

    4.1 主键

    4.1.1增加主键

    4.1.2 主键约束

    4.1.3 更新主键 & 删除主键

    4.1.4 主键分类

    4.2 自动增长

    4.2.1 新增自增长

    4.2.2 自增长使用

    4.2.3 修改自增长

    4.2.4 删除自增长

    4.3 唯一键

    4.3.1 增加唯一键

    4.3.2 唯一键约束

    4.3.3 更新唯一键 & 删除唯一键

    4.4 外键

    4.4.1 增加外键

    4.4.2 修改外键&删除外键

    4.4.3 外键作用

    4.4.4 外键条件

    4.4.5 外键约束

    4.4.6 创建外键约束的要求

    4.4.7 外键约束的闭环问题

    4.5 索引

    4.5.1 创建索引

    4.5.2 添加索引

    4.5.3 查询索引

    4.5.4 删除索引

    4.5.5 索引的使用原则

    4.5.6 索引的意义

    第5章 关系

    5.1 一对一

    5.2 一对多

    5.3多对多

    第6章 范式

    6.1 1NF

    6.2 2NF

    6.3 3NF

    6.4 逆规范化

    第7章 数据高级操作

    7.1 新增数据

    7.1.1 IGNORE关键字

    7.1.2 主键冲突

    7.1.3 蠕虫复制

    7.2 更新数据

    7.2.1 UPDATE语句中的内连接

    7.2.2 UPDATE语句中的外连接

    7.3 删除数据

    7.3.1 DELETE语句中的内连接

    7.3.2 DELETE语句中的外连接

    7.3.3 快速删除数据表全部记录

    7.4 查询数据

    7.4.1 Select语句

    7.4.2 去重查询

    7.4.3 字段别名

    7.4.4 数据源

    7.4.5 Where子句

    7.4.6 聚合函数

    7.4.7 Group by子句

    7.4.8 Having子句

    7.4.9 Order by子句

    7.4.10 Limit子句

    第8章 连接查询

    8.1 连接查询分类

    8.2 交叉连接

    8.3 内连接

    8.4 外连接

    8.5 自然连接

    8.6 子查询

    8.6.1 子查询分类

    8.6.2 单行子查询和多行子查询

    8.6.3 WHERE子句中的多行子查询

    8.6.4 子查询的EXISTS关键字

    第9章 视图

    9.1 创建视图

    9.2 查看视图

    9.3 使用视图

    9.4 修改视图

    9.5 删除视图

    9.6 视图意义

    9.7 视图数据操作

    9.7.1 新增数据

    9.7.2 删除数据

    9.7.3 更新数据

    9.8 视图算法

    第10章 数据备份与还原

    10.1 数据表备份

    10.2 单表数据备份

    10.3 SQL备份与还原

    10.4 增量备份

    10.5 大文件备份和还原(图形化操作,推荐!)

    第11章 事务安全

    11.1 事务操作

    11.2 自动事务处理

    11.3 事务原理

    11.4 回滚点

    11.5 事务ACID属性

    11.6 事务的隔离级别

    11.6.1 read uncommitted

    11.6.2 read committed

    11.6.3 repeatable read

    11.6.4 serializable

    第12章 触发器

    12.1 创建触发器

    12.2 查看触发器

    12.3 使用触发器

    12.4 修改触发器&删除触发器

    12.5 触发器记录

    第13章 函数

    13.1 数字函数

    13.2 日期函数

    13.2.1 获取系统时间函数

    13.2.2 日期格式化函数

    13.2.3 日期偏移计算

    13.2.4 计算日期之间相隔的天数

    13.3 字符函数

    13.4 条件函数

    13.4.1 简单条件判断

    13.4.2 复杂条件判断

    13.5 自定义函数

    13.5.1 创建函数

    13.5.2 查看函数

    13.5.3 修改函数&删除函数

    13.5.4 函数参数

    13.5.5 作用域

    第14章 存储过程

    14.1 创建过程

    14.2 查看过程

    14.3 调用过程

    14.4 修改过程&删除过程

    14.5 过程参数


     

     

     

    第1章 数据库

    1.1 数据库概述

    什么是数据库

    数据库就是存储数据的仓库,其本质是一个文件系统,数据按照特定的格式将数据存储起来,用户可以对数据库中的数据进行增加,修改,删除及查询操作。

    什么是数据库管理系统

    数据库管理系统(DataBase Management System,DBMS):指一种操作和管理数据库的大型软件,用于建立、使用和维护数据库,对数据库进行统一管理和控制,以保证数据库的安全性和完整性。用户通过数据库管理系统访问数据库中表内的数据。

     

    常见的数据库管理系统

    MYSQL :开源免费的数据库,小型的数据库.已经被Oracle收购了.MySQL6.x版本也开始收费。

    Oracle :收费的大型数据库,Oracle公司的产品。Oracle收购SUN公司,收购MYSQL。

    DB2 :IBM公司的数据库产品,收费的。常应用在银行系统中.

    SQLServer:MicroSoft 公司收费的中型的数据库。C#、.net等语言常使用。

    SyBase :已经淡出历史舞台。提供了一个非常专业数据建模的工具PowerDesigner。

    SQLite : 嵌入式的小型数据库,应用在手机端。

    Java相关的数据库:MYSQL,Oracle.

    这里使用MySQL数据库。MySQL中可以有多个数据库,数据库是真正存储数据的地方。

    数据库与数据库管理系统的关系

     

    1.2 数据库表

    数据库中以表为组织单位存储数据。

    表类似我们的Java类,每个字段都有对应的数据类型。

    那么用我们熟悉的java程序来与关系型数据对比,就会发现以下对应关系。

    类----------表

    类中属性----------表中字段

    对象----------记录

    1.3 表数据

    根据表字段所规定的数据类型,我们可以向其中填入一条条的数据,而表中的每条数据类似类的实例对象。表中的一行一行的信息我们称之为记录。

    表记录与java类对象的对应关系

     

    第2章 MySql数据库

    2.1 MySql安装

    安装

    自行百度

    安装后,MySQL会以windows服务的方式为我们提供数据存储功能。开启和关闭服务的操作:右键点击我的电脑→管理→服务→可以找到MySQL服务开启或停止。

     

    也可以在DOS窗口,通过命令完成MySQL服务的启动和停止(必须以管理运行cmd命令窗口)

     

    2.2 登录MySQL数据库

    MySQL是一个需要账户名密码登录的数据库,登陆后使用,它提供了一个默认的root账号,使用安装时设置的密码即可登录。

    格式1:cmd>  mysql –u用户名 –p密码

    例如:mysql -uroot –proot

     

    格式2:cmd>  mysql --host=ip地址 --user=用户名 --password=密码

    例如:mysql --host=127.0.0.1  --user=root --password=root

     

    2.3 SQLyog(MySQL图形化开发工具,我个人用的Navicat)

    安装:

    提供的SQLyog软件为免安装版,可直接使用。【其实我建议使用Navicat,具体怎么破解得百度,公司都用的这个,学习阶段用SQLyog也没关系】

    使用:

    输入用户名、密码,点击连接按钮,进行访问MySQL数据库进行操作

     

    在Query窗口中,输入SQL代码,选中要执行的SQL代码,按F8键运行,或按执行按钮运行。

    2.4 MySQL配置文件

    看到你的C:\ProgramData\MySQL\MySQL Server 8.0目录,注意ProgramData是隐藏目录,你需要设置查看隐藏文件才能看得到。

    发现下面有个my.ini,这就是MySQL数据库的配置文件,比如字符集、端口号、目录地址等信息都可以在这里配置。

    从大体上我们可以看到,my.ini里面有3个部分。

    [client]和[mysql]是客户端配置信息,[mysqld]是数据库配置信息

    提示:[mysql]中默认no-beep表示当数据库发生错误的时候,不要让主板发出蜂鸣器的声音

    [mysqld]大致说明如下(已去掉默认注释,不然篇幅太长)

     

     

    第3章 SQL语句

    数据库是不认识JAVA语言的,但是我们同样要与数据库交互,这时需要使用到数据库认识的语言SQL语句,它是数据库的代码。

    结构化查询语言(Structured Query Language)简称SQL,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统。

    创建数据库、创建数据表、向数据表中添加一条条数据信息均需要使用SQL语句。

    3.1 SQL语句

    SQL分类:

    数据定义语言:简称DDL(Data Definition Language),用来定义数据库对象:数据库,表,列等。关键字:create,alter,drop等

    数据操作语言:简称DML(Data Manipulation Language),用来对数据库中表的记录进行更新。关键字:insert,delete,update等

    数据控制语言:简称DCL(Data Control Language),用来定义数据库的访问权限和安全级别,及创建用户。

    数据查询语言:简称DQL(Data Query Language),用来查询数据库中表的记录。关键字:select,from,where等

    3.2 SQL通用语法

    1.SQL语句可以单行或多行书写,以分号结尾

    2.可使用空格和缩进来增强语句的可读性

    3.MySQL数据库的SQL语句不区分大小写,建议使用大写,例如:SELECT * FROM user。

    4.同样可以使用/**/的方式完成注释

    5.MySQL中的我们常使用的数据类型如下

    详细的数据类型如下

    分类

    类型名称

    说明

    整数类型

    tinyInt

    很小的整数,1字节

    smallint

    小的整数,2字节

    mediumint

    中等大小的整数,3字节

    int(integer)

    普通大小的整数,4字节

    bigint 大整数,8字节

    小数类型

    float

    单精度浮点数,4字节

    double

    双精度浮点数,8字节

    decimal(m,d)

    压缩严格的定点数, m表示数字总位数,d表示保留到小数点后d位,不足部分就添0,如果不设置m、d,默认保存精度是整型

    日期类型

    year

    年份 YYYY  1901~2155,1字节

    time

    时间 HH:MM:SS  -838:59:59~838:59:59,3字节

    date

    日期 YYYY-MM-DD 1000-01-01~9999-12-3,3字节

    datetime

    日期时间 YYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00~ 9999-12-31 23:59:59,8字节

    timestamp

    时间戳 YYYY-MM-DD HH:MM:SS  1970~01~01 00:00:01 UTC~2038-01-19 03:14:07UTC,4字节

    文本、二进制类型

    CHAR(M)

    M为0~255之间的整数,固定长度为M,不足后面补全空格

    VARCHAR(M)

    M为0~65535之间的整数

    TINYBLOB

    允许长度0~255字节

    BLOB

    允许长度0~65535字节

    MEDIUMBLOB

    允许长度0~167772150字节

    LONGBLOB

    允许长度0~4294967295字节

    TINYTEXT

    允许长度0~255字节

    TEXT

    允许长度0~65535字节

    MEDIUMTEXT

    允许长度0~167772150字节

    LONGTEXT

    允许长度0~4294967295字节

    VARBINARY(M)

    允许长度0~M个字节的变长字节字符串

    BINARY(M)

    允许长度0~M个字节的定长字节字符串

    需要注意的是:

    > BOOLEAN在数据库保存的是tinyInt类型,false为0,true就是1

    > char是定长,varchar是变长,char存储时,如果字符数没有达到定义的位数,后面会用空格填充到指定长度,而varchar没达到定义位数则不会填充,按实际长度存储。

    > char长度固定,char存取速度还是要比varchar要快得多,方便程序的存储与查找;但是char也为此付出的是空间的代价,因为其长度固定,所以会占据多余的空间,可谓是以空间换取时间效率。varchar则刚好相反,以时间换空间。

     

    3.3 数据库操作:database

    创建数据库

    格式:

    create database 数据库名;

    create database 数据库名 character set 字符集;

    例如:

    #创建数据库 数据库中数据的编码采用的是安装数据库时指定的默认编码 utf8

    CREATE DATABASE day21_1;

    #创建数据库 并指定数据库中数据的编码

    CREATE DATABASE day21_2 CHARACTER SET gbk;

      

    #如果创建之后 修改数据库编码

    ALTER DATABASE day21_2 CHARACTER SET=utf8;

     

    查看数据库

    查看数据库MySQL服务器中的所有的数据库:

    show databases;

    查看某个数据库的定义的信息:

    show create database 数据库名;

    例如:

    show create database day21_1;

     

    删除数据库

    drop database 数据库名称;

    例如:

    drop database day21_2;

     

    其他的数据库操作命令

    切换数据库:

    格式:use 数据库名;

    例如:

    use day21_1;

     

    查看正在使用的数据库:

    select database();

     图形化结果类似于下图

     

     

    3.4 表结构相关语句

    3.4.1 创建表

    格式:

    create table 表名(
       字段名 类型(长度) 约束,
       字段名 类型(长度) 约束
    );

    例如:

    创建分类表

    CREATE TABLE sort (
      sid INT, #分类ID
      sname VARCHAR(100) #分类名称
    );
    
    

    温馨提示:你创建了数据库,就创建了一块逻辑空间,实际在磁盘上创建了一个文件夹,你创建了一个表,实际磁盘生成了一个.ibd文件,你可以在C:\ProgramData\MySQL\MySQL Server 8.0\Data目录下验证一下,路径中的ProgramData是隐藏文件夹。

    举个例子,你创建了test数据库,然后你执行建表语句如下

    CREATE TABLE temp(/*实验精度丢失问题*/
    	id INT UNSIGNED PRIMARY KEY,
    	num DECIMAL(20, 10) /*数字总位数20,保留小数点后10位*/
    )

    实际在你的磁盘上是这样存储的

     

    3.4.2 查看表

    查看数据库中的所有表:

    格式:

    show tables;

     图形化结果类似于下图

    这里的命名就告诉了你是 test 数据库里面的表

     

    查看表结构:

    有两种方式

    方法一: desc 表名;

    方法二: SHOW COLUMNS FROM 表名;

    例如:

    DESC student;
    
    SHOW COLUMNS FROM student;
    
    /* 这两种方式结果一模一样,第一种更常见,显然命令更短你也更愿意用 */

     图形化结果类似于下图

     

    3.4.3 删除表

    格式:drop table 表名;

    例如:

    drop table sort;

     

    3.4.4 修改表结构格式

    alter table 表名 add 列名 类型(长度) 约束;

    作用:修改表添加列.

    例如:

    #1,为分类表添加一个新的字段为 分类描述 varchar(20)

    ALTER TABLE sort ADD sdesc VARCHAR(20);

    当然,想添加多个字段分类怎么做呢?

    /*添加多个列方法一*/
    ALTER TABLE student
    ADD address VARCHAR(200) NOT NULL,
    ADD home_tel CHAR(11) NOT NULL;
    /*add语句之间用逗号分隔,最后用分号结束*/
    
    /*添加多个列方法二*/
    ALTER TABLE student
    ADD (address VARCHAR(200) NOT NULL,home_tel CHAR(11) NOT NULL);

    值得注意的是:

    如果表需要添加多列,而有一列字段home_tel之前已经添加过了,结果会显示Duplicate column name 'home_tel',那么你本次添加的多列字段都是无效的,即全部添加失败

     

     

    alter table 表名 modify 列名 类型(长度) 约束;

    作用:修改表修改列的类型长度及约束.

    例如:

    #2, 为分类表的分类名称字段进行修改,类型varchar(50) 添加约束 not null

    ALTER TABLE sort MODIFY sname VARCHAR(50) NOT NULL; /* 添加约束NOT NULL */
    
    ALTER TABLE student
    MODIFY home_tel VARCHAR(20) NOT NULL; /*CHAR(11)修改为VARCHAR(200)*/

    同理,和add类似,需要修改多个列的类型长度及约束,那么modify语句之间用逗号分隔,最后一句的末尾用分号结束。

     

    alter table 表名 change 旧列名 新列名 类型(长度) 约束;

    作用:修改表修改列名.

    例如:

    #3, 为分类表的分类名称字段进行更换 更换为 snamesname varchar(30)

    ALTER TABLE sort CHANGE sname snamename VARCHAR(30);

    同理,和add类似,需要修改多个列的字段名,那么change语句之间用逗号分隔,最后一句的末尾用分号结束。

    直接来个例题:

    假设有2个选项, 选择哪一个

    A. ALTER TABLE cource CHANGE cname VARCHAR(30) NOT NULL FIRST;

    B. ALTER TABLE cource MODIFY  cname VARCHAR(30) NOT NULL FIRST;

    请注意CHANGE和MODIFY的区别, MODIFY可以修改字段类型、字段属性,而CHANGE可修改字段名称,并且CHANGE需要旧列名和新列名,答案是B

     

    alter table 表名 drop 列名;

    作用:修改表删除列.

    例如:

    #4, 删除分类表中snamename这列

    ALTER TABLE sort DROP snamename;
    
    ALTER TABLE student
    DROP home_address,
    DROP home_tel;

    同理,和add类似,需要删除多列,那么drop语句之间用逗号分隔,最后一句的末尾用分号结束。

    来一道选择题,题目是:删除数据表中多余的列的语句是哪些,有同学上去就选了个B,认为删除就是DELETE,这里的答案是AC。

     

    rename table 表名 to 新表名;

    作用:修改表名

    例如:

    #5, 为分类表sort 改名成 category

    RENAME TABLE sort TO category;

     

    alter table 表名 character set 字符集;

    作用:修改表的字符集

    例如:

    #6, 为分类表 category 的编码表进行修改,修改成 gbk

    ALTER TABLE category CHARACTER SET gbk;

     

    3.5 DOS操作数据乱码解决

    我们在dos命令行操作中文时,会报错

    insert into user(username,password) values(‘张三’,’123’);

    ERROR 1366 (HY000): Incorrect string value: '\xD5\xC5\xC8\xFD' for column 'username' at row 1

    原因:因为mysql的客户端编码的问题我们的是utf8,而系统的cmd窗口编码是gbk

    解决方案(临时解决方案):修改mysql客户端编码。

    show variables like 'character%'; 查看所有mysql的编码

     

    在图中与客户端有关的编码设置:

    client connetion result 和客户端相关

    database server system 和服务器端相关

    将客户端编码修改为gbk.

    set character_set_results=gbk; / set names gbk;

    以上操作,只针对当前窗口有效果,如果关闭了服务器便失效。如果想要永久修改,通过以下方式:

    在mysql安装目录下有my.ini文件

    default-character-set=gbk 客户端编码设置

    character-set-server=utf8 服务器端编码设置

    注意:修改完成配置文件,重启服务

     

     

     

    第4章 字段属性

    主键, 唯一键和自增长.

    4.1 主键

    主键: primary key,主要的键. 一张表只能有一个字段可以使用对应的键, 用来唯一的约束该字段里面的数据, 不能重复: 这种称之为主键.

    一张表只能有最多一个主键, 主键请尽量使用整数类型而不是字符串类型

    4.1.1增加主键

    SQL操作中有多种方式可以给表增加主键: 大体分为三种.

    方案1: 在创建表的时候,直接在字段之后,跟primary key关键字(主键本身不允许为空)

    优点: 非常直接; 缺点: 只能使用一个字段作为主键

     

    方案2: 在创建表的时候, 在所有的字段之后, 使用primary key(主键字段列表)来创建主键(如果有多个字段作为主键,可以是复合主键)

     

    方案3: 当表已经创建好之后, 额外追加主键: 可以通过修改表字段属性, 也可以直接追加.

    Alter table 表名  add primary key(字段列表);

    前提: 表中字段对应的数据本身是独立的(不重复)

     

    4.1.2 主键约束

    创建约束的目的就是保证数据的完整性和一致性。

    主键对应的字段中的数据必须唯一,且不能为NULL, 一旦重复,数据操作失败(增和改)

    建议主键使用数字类型,因为数字的检索速度非常快,并且主键如果是数字类型,还可以设置自动增长。

    主键的原理其实就是一个计数器。

     

    4.1.3 更新主键 & 删除主键

    没有办法更新主键: 主键必须先删除,才能增加.

    Alter table 表名 drop primary key;

     

    4.1.4 主键分类

    在实际创建表的过程中, 很少使用真实业务数据作为主键字段(业务主键,如学号,课程号); 大部分的时候是使用逻辑性的字段(字段没有业务含义,值是什么都没有关系), 将这种字段主键称之为逻辑主键.

    Create table my_student(
    
    Id int primary key auto_increment comment ‘逻辑主键: 自增长’, -- 逻辑主键
    
    Number char(10) not null  comment ‘学号’,
    
    Name varchar(10) not null
    
    )
    
    

     

    4.2 自动增长

    自增长: 当对应的字段,不给值,或者说给默认值,或者给NULL的时候, 会自动的被系统触发, 系统会从当前字段中已有的最大值再进行+1操作,得到一个新的在不同的字段.

    自增长的字段必须定义为主键,默认起始值是1而不是0

     

    4.2.1 新增自增长

    自增长特点: 

       任何一个字段要做自增长必须前提是本身是一个索引(key一栏有值),auto_increment表示自动编号

     

       自增长字段必须是数字(整型)

     

      一张表最多只能有一个自增长

     

    4.2.2 自增长使用

    当自增长被给定的值为NULL或者默认值的时候会触发自动增长.

     

    自增长如果对应的字段输入了值,那么自增长失效: 但是下一次还是能够正确的自增长(从最大值+1)

     

    如何确定下一次是什么自增长呢? 可以通过查看表创建语句看到.

     

    4.2.3 修改自增长

    自增长如果是涉及到字段改变: 必须先删除自增长,后增加(一张表只能有一个自增长)

     

    修改当前自增长已经存在的值: 修改只能比当前已有的自增长的最大值大,不能小(小不生效)

    Alter table 表名 auto_increment  = 值;

     

    向上修改可以

     

    思考: 为什么自增长是从1开始?为什么每次都是自增1呢?

    所有系统的变现(如字符集,校对集)都是由系统内部的变量进行控制的.

    查看自增长对应的变量: show variables like ‘auto_increment%’;

     

    可以修改变量实现不同的效果: 修改是对整个数据修改,而不是单张表: (修改是会话级)

    Set auto_increment_increment = 5; -- 一次自增5

     

    测试效果: 自动使用自增长

     

    4.2.4 删除自增长

    自增长是字段的一个属性: 可以通过modify来进行修改(保证字段没有auto_increment即可)

    Alter table 表名 modify 字段 类型;

     

    4.3 唯一键

    一张表往往有很多字段需要具有唯一性,数据不能重复: 但是一张表中只能有一个主键: 唯一键(unique key)就可以解决表中有多个字段需要唯一性约束的问题.

    唯一键的本质与主键差不多: 唯一键默认的允许自动为空,而且可以多个为空(空字段不参与唯一性比较)

     

    4.3.1 增加唯一键

    基本与主键差不多: 三种方案

    方案1: 在创建表的时候,字段之后直接跟unique/ unique key

     

    方案2: 在所有的字段之后增加unique key(字段列表); -- 复合唯一键

     

    方案3: 在创建表之后增加唯一键

     

    4.3.2 唯一键约束

    唯一键与主键本质相同: 唯一的区别就是唯一键默认允许为空,而且是多个为空.

    如果唯一键也不允许为空: 与主键的约束作用是一致的.

     

    4.3.3 更新唯一键 & 删除唯一键

    更新唯一键

    先删除后新增(唯一键可以有多个: 可以不删除).

    删除唯一键

    Alter table 表名 drop unique key; -- 错误: 唯一键有多个

    Alter table 表名 drop index 索引名字; -- 唯一键默认的使用字段名作为索引名字

     

    4.4 外键

    外键: foreign key, 外面的键(键不在自己表中): 如果一张表中有一个字段(非主键)指向另外一张表的主键,那么将该字段称之为外键.

    4.4.1 增加外键

    外键可以在创建表的时候或者创建表之后增加(但是要考虑数据的问题).

    一张表可以有多个外键.

     

    创建表的时候增加外键: 在所有的表字段之后,使用foreign key(外键字段) references 外部表(主键字段)

     

    在新增表之后增加外键: 修改表结构

    Alter table 表名 add [constraint 外键名字] foreign key(外键字段) references 父表(主键字段);

     

    4.4.2 修改外键&删除外键

    外键不可修改

    只能先删除后新增.

    删除外键语法

    Alter table 表名 drop foreign key 外键名; -- 一张表中可以有多个外键,但是名字不能相同

     

    4.4.3 外键作用

    外键默认的作用有两点: 一个对父表,一个对子表(外键字段所在的表)

     

    对子表约束: 子表数据进行写操作(增和改)的时候, 如果对应的外键字段在父表找不到对应的匹配: 那么操作会失败.(约束子表数据操作)

     

    对父表约束: 父表数据进行写操作(删和改: 都必须涉及到主键本身), 如果对应的主键在子表中已经被数据所引用, 那么就不允许操作

     

    4.4.4 外键条件

    1.外键要存在: 首先必须保证表的存储引擎是innodb(默认的存储引擎): 如果不是innodb存储引擎,那么外键可以创建成功,但是没有约束效果.
    2.外键字段的字段类型(列类型)必须与父表的主键类型完全一致.
    3.一张表中的外键名字不能重复.
    4,增加外键的字段(数据已经存在),必须保证数据与父表主键要求对应.

     

    4.4.5 外键约束

    所谓外键约束: 就是指外键的作用.

    之前所讲的外键作用: 是默认的作用; 其实可以通过对外键的需求, 进行定制操作.

    需要注意的是:外键约束的定义是写在子表上的,但是不推荐使用外键约束

    MySQL字段约束有四种,主键约束,非空约束,唯一约束,外键约束。外键约束是唯一不推荐的约束

    提示:主键约束其实就是非空约束和唯一约束合二为一的情况

     

    外键约束有三种约束模式: 都是针对父表的约束(子表约束父表)

    District: 严格模式(默认的), 父表不能删除或者更新一个已经被子表数据引用的记录

    Cascade: 级联模式: 父表的操作, 对应子表关联的数据也跟着被删除

    Set null: 置空模式: 父表的操作之后,子表对应的数据(外键字段)被置空

     

    通常的一个合理的做法(约束模式): 删除的时候子表置空, 更新的时候子表级联操作

    指定模式的语法

    Foreign key(外键字段) references 父表(主键字段) on delete set null on update cascade;

     

    更新操作: 级联更新

     

    删除操作: 置空

     

    删除置空的前提条件: 外键字段允许为空(如果不满足条件,外键无法创建)

    外键虽然很强大, 能够进行各种约束: 但是对于PHP来讲, 外键的约束降低了PHP对数据的可控性: 通常在实际开发中, 很少使用外键来处理.

     

    4.4.6 创建外键约束的要求

    创建外键约束的目的是保持数据一致性和完整性,以及实现一对一或者一对多的关系。

    创建外键约束要求有以下几点:

    1. 父表和子表必须使用相同的存储引擎,而且禁止使用临时表。

    注意:具有外键列的表称为子表;子表所参照的表称为父表。

    2. 数据表的存储引擎只能是InnoDB。

    3. 外键列和参照列必须具有相似的数据类型。其中数字的长度或是否有符号位必须相同;而字符的长度则可以不同。

    注意:加 FOREIGN KEY 关键字的列称为外键列;外键列所参照的列称为参照列。

    4. 外键列和参照列必须创建索引。如果外键列不存在索引的话,MySQL将自动创建索引。如果参照列不存在索引的话,MySQL不会自动创建索引。

    注意:MySQL会为主键自动创建索引。

     

    4.4.7 外键约束的闭环问题

    比如说我们创建了2张表

    /*先创建父表*/
    CREATE TABLE t_dept(
    	deptno INT UNSIGNED PRIMARY KEY,
    	dname VARCHAR(20) NOT NULL UNIQUE,
    	tel CHAR(4) UNIQUE
    )
    /*再创建子表*/
    CREATE TABLE t_emp(
    	empno INT UNSIGNED PRIMARY KEY,
    	ename VARCHAR(20) NOT NULL,
    	sex ENUM("男", "女") NOT NULL,
    	deptno INT UNSIGNED NOT NULL,
    	hiredate DATE NOT NULL,
    	FOREIGN KEY (deptno) REFERENCES t_dept(deptno)
    );

    父表t_dept加一个数据如下:

    子表t_emp加一个数据如下:

    此时我想删除父表的数据,结果报错

    结果发现有子表t_emp外键约束着父表,删除失败。必须先删除子表的约束数据才能删除父表的数据,那这样就失去了增减改查的灵活性了,并且更严重的是,

    如果形成外键闭环,我们将无法删除任何一张表的数据记录。

    如上图,A约束B,B约束C......,这样每一个表都算作父表,所谓的先删除子表的数据就是不可能的。因为有外键闭环的存在,所以我们不推荐外键约束

     

    4.5 索引

    几乎所有的索引都是建立在字段之上.

    索引: 系统根据某种算法, 将已有的数据(未来可能新增的数据),单独建立一个文件: 文件能够实现快速的匹配数据, 并且能够快速的找到对应表中的记录.

    4.5.1 创建索引

    建表的时候创建索引,也可以在已存在的表上添加索引。

    CREATE TABLE 表名称(
           ......,
           INDEX [索引名称] (字段),
           ......
    );

    CREATE TABLE t_message(
    	id INT UNSIGNED PRIMARY KEY,
    	content VARCHAR(200) NOT NULL,
    	type ENUM("公告", "通报", "个人通知") NOT NULL,
    	create_time TIMESTAMP NOT NULL,
    	INDEX idx_type (type)
    );

    4.5.2 添加索引

    向已存在的表中添加索引的方式如下

    CREATE INDEX 索引名称 ON 表名(字段);  /*添加索引方式1*/

    ALTER TABLE 表名 ADD INDEX 索引名称(字段); /*添加索引方式2*/

    CREATE INDEX idx_type ON t_message(type); /*添加索引方式1*/
    
    ALTER TABLE t_message ADD INDEX idx_type(type);/*添加索引方式2*/

     经常被用来做检索条件的字段需要加上索引,内部是二叉树,所以查询很快。如果是几千条数据,不必加索引,全盘检索也很快

    练习题:

    已有新闻表(tb_news),表中有type字段,下列选中项中能为该字段添加索引的是?

    这个就是记忆题目,记住语法即可,答案是AC

     

    4.5.3 查询索引

    SHOW INDEX FROM 表名;

    /*查看t_message表的索引*/
    SHOW INDEX FROM t_message;

     查出来如下,有添加的普通索引和主键索引

     

    4.5.4 删除索引

    DROP INDEX 索引名称 ON 表名;

    /* 在t_message表中删除idx_type索引 */
    DROP INDEX idx_type ON t_message;

     

    4.5.5 索引的使用原则

    1. 数据量很大,且经常被查询的数据表可以设置索引  (即读多写少的表可以设置索引)

    2. 索引只添加在经常被用作检索条件的字段上 (比如电子商城需要在物品名称关键字加索引)

    3.不要在大字段上创建索引 (比如长度很长的字符串不适合做索引,因为查找排序时间变的很长)

     

    4.5.6 索引的意义

    提升查询数据的效率
    约束数据的有效性(唯一性等)
    增加索引的前提条件: 索引本身会产生索引文件(有时候有可能比数据文件还大) ,会非常耗费磁盘空间.

     

    如果某个字段需要作为查询的条件经常使用, 那么可以使用索引(一定会想办法增加);

    如果某个字段需要进行数据的有效性约束, 也可能使用索引(主键,唯一键)

     

    Mysql中提供了多种索引

    主键索引: primary key
    唯一索引: unique key
    全文索引: fulltext index
    普通索引: index
    全文索引: 针对文章内部的关键字进行索引

    全文索引最大的问题: 在于如何确定关键字

     

    英文很容易: 英文单词与单词之间有空格

    中文很难: 没有空格, 而且中文可以各种随意组合(分词: sphinx)

     

    第5章 关系

    将实体与实体的关系, 反应到最终数据库表的设计上来: 将关系分成三种: 一对一, 一对多(多对一)和多对多.

    所有的关系都是指的表与表之间的关系.

    5.1 一对一

    一对一: 一张表的一条记录一定只能与另外一张表的一条记录进行对应; 反之亦然.

    学生表: 姓名,性别,年龄,身高,体重,婚姻状况, 籍贯, 家庭住址,紧急联系人

    Id(P)

    姓名

    性别

    年龄

    体重

    身高

    婚姻

    籍贯

    住址

    联系人

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    表设计成以上这种形式: 符合要求. 其中姓名,性别,年龄,身高,体重属于常用数据; 但是婚姻,籍贯,住址和联系人属于不常用数据. 如果每次查询都是查询所有数据,不常用的数据就会影响效率, 实际又不用.

     

    解决方案: 将常用的和不常用的信息分离存储,分成两张表

    常用信息表

    Id(P)

    姓名

    性别

    年龄

    体重

    身高

    1

     

     

     

     

     

     

     

     

     

     

     

    不常用信息表: 保证不常用信息与常用信息一定能够对应上: 找一个具有唯一性(确定记录)的字段来共同连接两张表

    Id(P)

    婚姻

    籍贯

    住址

    联系人

    2

     

     

     

     

    1

     

     

     

     

    一个常用表中的一条记录: 永远只能在一张不常用表中匹配一条记录;反过来,一个不常用表中的一条记录在常用表中也只能匹配一条记录: 一对一的关系

     

    5.2 一对多

    一对多: 一张表中有一条记录可以对应另外一张表中的多条记录; 但是返回过, 另外一张表的一条记录只能对应第一张表的一条记录. 这种关系就是一对多或者多对一.

     

    母亲与孩子的关系: 母亲,孩子两个实体

    妈妈表

    ID(P)

    名字

    年龄

    性别

     

     

     

     

     

     

     

     

    孩子表

    ID(P)

    名字

    年龄

    性别

     

     

     

     

     

     

     

     

    以上关系: 一个妈妈可以在孩子表中找到多条记录(也有可能是一条); 但是一个孩子只能找到一个妈妈: 是一种典型的一对多的关系.

     

    但是以上设计: 解决了实体的设计表问题, 但是没有解决关系问题: 孩子找不出妈,妈也找不到孩子.

     

    解决方案: 在某一张表中增加一个字段,能够找到另外一张表的中记录: 应该在孩子表中增加一个字段指向妈妈表: 因为孩子表的记录只能匹配到一条妈妈表的记录.

     

    妈妈表

    ID(P)

    名字

    年龄

    性别

     

     

     

     

     

     

     

     

    孩子表

    ID(P)

    名字

    年龄

    性别

    妈妈ID

     

     

     

     

    妈妈表主键

     

     

     

     

     

     

    5.3多对多

    多对多: 一张表中(A)的一条记录能够对应另外一张表(B)中的多条记录; 同时B表中的一条记录也能对应A表中的多条记录: 多对多的关系

     

    老师教学: 老师和学生

    老师表

    T_ID(P)

    姓名

    性别

    1

    A

    2

    B

     

    学生表

    S_ID(P)

    姓名

    性别

    1

    张三

    2

    小芳

    以上设计方案: 实现了实体的设计, 但是没有维护实体的关系.

    一个老师教过多个学生; 一个学生也被多个老师教过.

     

    解决方案: 在学生表中增加老师字段: 不管在哪张表中增加字段, 都会出现一个问题: 该字段要保存多个数据, 而且是与其他表有关系的字段, 不符合表设计规范: 增加一张新表: 专门维护两张表之间的关系

    老师表

    T_ID(P)

    姓名

    性别

    1

    A

    2

    B

    学生表

    S_ID(P)

    姓名

    性别

    1

    张三

    2

    小芳

    中间关系表: 老师与学生的关系

    ID

    T_ID(老师)

    S_ID(学生)

    1

    1

    1

    2

    1

    2

    3

    2

    1

    4

     

     

     

    增加中间表之后: 中间表与老师表形成了一对多的关系: 而且中间表是多表,维护了能够唯一找到一表的关系; 同样的,学生表与中间表也是一个一对多的关系: 一对多的关系可以匹配到关联表之间的数据.

     

    学生找老师: 找出学生id -> 中间表寻找匹配记录(多条) -> 老师表匹配(一条)

    老师找学生: 找出老师id -> 中间表寻找匹配记录(多条) -> 学生表匹配(一条)

     

    第6章 范式

    范式: Normal Format, 是一种离散数学中的知识, 是为了解决一种数据的存储与优化的问题: 保存数据的存储之后, 凡是能够通过关系寻找出来的数据,坚决不再重复存储: 终极目标是为了减少数据的冗余.

     

    范式: 是一种分层结构的规范, 分为六层: 每一次层都比上一层更加严格: 若要满足下一层范式,前提是满足上一层范式.

     

    六层范式: 1NF,2NF,3NF...6NF, 1NF是最底层,要求最低;6NF最高层,最严格.

     

    Mysql属于关系型数据库: 有空间浪费: 也是致力于节省存储空间: 与范式所有解决的问题不谋而合: 在设计数据库的时候, 会利用到范式来指导设计.

    但是数据库不单是要解决空间问题,要保证效率问题: 范式只为解决空间问题, 所以数据库的设计又不可能完全按照范式的要求实现: 一般情况下,只有前三种范式需要满足.

     

    范式在数据库的设计当中是有指导意义: 但是不是强制规范.

     

    6.1 1NF

    第一范式: 在设计表存储数据的时候, 如果表中设计的字段存储的数据,在取出来使用之前还需要额外的处理(拆分),那么说表的设计不满足第一范式。

    第一范式要求字段的数据具有原子性: 不可再分.

    第一范式是数据库的基本要求,不满足第一范式就不是关系型数据库

    让我们简单化这个问题:

    1NF---原子性

    eg1:

    数据表的每一列都是不可分割的基本数据项,同一列中不能有多个值,也不能存在重复的属性。

    eg2:

    讲师代课表

    讲师

    性别

    班级

    教室

    代课时间

    代课时间(开始,结束)

    朱元璋

    Male

    php0226

    D302

    30天

    2014-02-27,2014-05-05

    朱元璋

    Male

    php0320

    B206

    30天

    2014-03-21,2014-05-30

    李世民

    Male

    php0320

    B206

    15天

    2014-06-01,2014-06-20

    上表设计不存在问题: 但是如果需求是将数据查出来之后,要求显示一个老师从什么时候开始上课,到什么时候节课: 需要将代课时间进行拆分: 不符合1NF, 数据不具有原子性, 可以再拆分.

    解决方案: 将代课时间拆分成两个字段就解决问题.

     

    6.2 2NF

    第二范式: 在数据表设计的过程中,如果有复合主键(多字段主键), 且表中有字段并不是由整个主键来确定, 而是依赖主键中的某个字段(主键的部分): 存在字段依赖主键的部分的问题, 称之为部分依赖: 第二范式就是要解决表设计不允许出现部分依赖.

    定义太绕了,简单点:

    2NF---唯一性

    数据表中的每条记录必须是唯一的。为了实现区分,通常要为表加上一个列来存储唯一标识,这个唯一属性列被称作主键列

    eg1:

    学号为230的学生在2018-07-15考试第一次58没及格,然后当天补考第二次还是58没及格,于是数据库就有了重复的数据。解决办法就是添加一个流水号,让数据变得唯一。

    eg2:

    讲师带课表

    以上表中: 因为讲师没有办法作为独立主键, 需要结合班级才能作为主键(复合主键: 一个老师在一个班永远只带一个阶段的课): 代课时间,开始和结束字段都与当前的代课主键(讲师和班级): 但是性别并不依赖班级, 教室不依赖讲师: 性别只依赖讲师, 教室只依赖班级: 出现了性别和教室依赖主键中的一部分: 部分依赖.不符合第二范式.

     

    解决方案1: 可以将性别与讲师单独成表, 班级与教室也单独成表.

    解决方案2: 取消复合主键, 使用逻辑主键

    ID = 讲师 + 班级(业务逻辑约束: 复合唯一键)

     

    6.3 3NF

    要满足第三范式,必须满足第二范式

    第三范式: 理论上讲,应该一张表中的所有字段都应该直接依赖主键(逻辑主键: 代表的是业务主键), 如果表设计中存在一个字段, 并不直接依赖主键,而是通过某个非主键字段依赖,最终实现依赖主键: 把这种不是直接依赖主键,而是依赖非主键字段的依赖关系称之为传递依赖. 第三范式就是要解决传递依赖的问题.

    定义很绕,我们简单点:

    3NF---关联性

    每列都与主键有直接关系,不存在传递依赖

    eg1:

    根据主键爸爸能关联儿子女儿,但是女儿的玩具、衣服都不是依赖爸爸的,而是依赖女儿的,这些东西不是与爸爸有直接关系,所以拆分两个表。

    儿子女儿依赖于爸爸,女儿的玩具、衣服依赖于女儿。

    满足第三范式后,检索、提取数据非常方便,如果不满足,虽然表也能建成功,但是检索就会花费很多时间,比如如果是第一个表,逻辑上要找女儿的衣服,去查找女儿是找不到的,此时女儿不是主键。数据表拆分之后,根据主键列女儿陈婷婷,可以很快的找到女儿的衣服校服。主键查找是很快的。

    依照第三范式,数据可以拆分到不同的数据表,彼此保持关联

     

    eg2:

    讲师带课表

    以上设计方案中: 性别依赖讲师存在, 讲师依赖主键; 教室依赖班级,班级依赖主键: 性别和教室都存在传递依赖.

     

    解决方案: 将存在传递依赖的字段,以及依赖的字段本身单独取出,形成一个单独的表, 然后在需要对应的信息的时候, 使用对应的实体表的主键加进来.

    讲师代课表

     

       讲师表                                                                               班级表

                                 

               讲师表: ID = 讲师                                                               班级表中: ID = 班级

     

    6.4 逆规范化

    有时候, 在设计表的时候,如果一张表中有几个字段是需要从另外的表中去获取信息. 理论上讲, 的确可以获取到想要的数据, 但是就是效率低一点. 会刻意的在某些表中,不去保存另外表的主键(逻辑主键), 而是直接保存想要的数据信息: 这样一来,在查询数据的时候, 一张表可以直接提供数据, 而不需要多表查询(效率低), 但是会导致数据冗余增加.

     

    如讲师代课信息表

    逆规范化: 磁盘利用率与效率的对抗

     

    第7章 数据高级操作

    数据操作: 增删改查

     

    7.1 新增数据

    基本语法

    Insert into 表名 [字段1,字段2,......] values (值1,值2,......); /*插入单条记录*/

    Insert into 表名 [字段1,字段2,......] values (值1,值2,......), (值1,值2,......); /*插入多条记录*/

    表名后面不写字段列表也可以插入数据,但是会影响速度。Mysql会进行词法分析,找到对应表结构,然后自动给你补上字段列表。所以表名后面不写字段列表,数据库难以高效的操作。

    INSERT INTO t_dept(deptno, dname, loc)
    VALUES(50, "技术部", "北京");

    INSERT INTO t_dept(deptno, dname, loc)
    VALUES(60, "后勤部", "北京"),(70,"保安部","北京");

     

    eg:向技术部添加一条员工记录

    分析:测验insert语句里面子查询的问题,并且这个子查询是单行子查询,不能是多行子查询,还必须是单行单列的。

    INSERT INTO t_emp
    (empno, ename, job, mgr, hiredate, sal, comm, deptno)
    VALUES(8001, "刘娜", "SALESMAN", 8000, "1988-12-20", 2000, NULL,
    (SELECT deptno FROM t_dept WHERE dname="技术部"));

    练习题

    答案选D,A错在scholarship字段没有数据去匹配,数据库会报错,可以填写NULL解决错误,或者删掉INSERT字段列表中的字段。B错在部门编号deptno却写成"食品工程系",C错在人名"赵菲菲"没写成字符串形式,要加上影号。

     

    INSERT语句方言

    MySQL的INSERT语句还有一种方言语法

    INSERT INTO 表名 SET 字段1=值1, 字段2=值2......

    为什么称之为方言语法呢?就是因为这个语法只能在MySQL使用,不能在Oracle使用,当然你只用MySQL就可以使用这种方言语法,很简洁。

    INSERT INTO t_emp
    SET empno=8002,ename="JACK",job="SALESMAN",mgr=8000,
    hiredate="1985-3-14",sal=2500,comm=NULL,deptno=50;

     

    在数据插入的时候, 假设主键对应的值已经存在: 插入一定会失败!

     

    7.1.1 IGNORE关键字

    IGNORE关键字只会插入数据库不存在的记录。比如主键冲突、唯一性冲突,数据库会报错,加上IGNORE之后数据库会忽略这条数据不会报错。

    INSERT [IGNORE] INTO 表名 ......;

    INSERT IGNORE INTO t_dept(deptno, dname, loc)
    VALUES(70, "A", "北京"), (80, "B", "上海"); /*70部门已经存在*/

     

    7.1.2 主键冲突

    当主键存在冲突的时候(Duplicate key),你可以添加ignore关键字选择忽略,数据库不会报错,但是确实非得添加这个记录怎么办呢?可以选择性的进行处理: 更新和替换

    主键冲突:更新操作

    Insert into 表名[(字段列表:包含主键)] values(值列表) on duplicate key update 字段 = 新值; (这个语法sql单独执行没问题,在mybatis会报错,找不到你想要的参数)

    要想兼容mysql和mybatis两者,这里强烈建议不要用等号赋值
    Insert into 表名[(字段列表:包含主键)] values(值列表) on duplicate key update 字段 = values(字段);

    下图例子我忘记改了,应该改为...on duplicate key update room = values(room),而不是room = 'B205',记住不要用等号直接赋值,mybatis会报错。

     

    主键冲突: 替换

    Replace into 表名 [(字段列表:包含主键)] values(值列表);

     

    7.1.3 蠕虫复制

    蠕虫复制: 从已有的数据中去获取数据,然后将数据又进行新增操作: 数据成倍的增加.

     

    表创建高级操作: 从已有表创建新表(复制表结构)

    Create table 表名 like 数据库.表名;

     

    蠕虫复制: 先查出数据, 然后将查出的数据新增一遍

    Insert into 表名[(字段列表)] select 字段列表/* from 数据表名;

    蠕虫复制的意义

    从已有表拷贝数据到新表中
    可以迅速的让表中的数据膨胀到一定的数量级: 测试表的压力以及效率
     

     

    7.2 更新数据

    基本语法

    UPDATE [IGNORE] 表名 SET 字段1=值1, 字段2=值2, ......
    [WHERE 条件1 ......]
    [ORDER BY ......]
    [LIMIT ......];

    注意,如果这里有limit关键字,那么后面只能跟一个参数,即表示取前多少条数据,这里的limit不能有2个参数,ignore表示更新失败就直接忽略而不是报错。

    eg1:把每个员工的编号和他上司的编号+1,用order by子句完成

    UPDATE t_emp SET empno=empno+1, mgr=mgr+1
    ORDER BY empno DESC;

    eg2:把月收入前三名的员工底薪减100元,用LIMIT子句完成

    UPDATE t_emp
    SET sal=sal-100
    ORDER BY sal+IFNULL(comm,0) DESC
    LIMIT 3;

    eg3:把10部门中,工龄达到20年的员工,底薪增加200元

    UPDATE t_emp
    SET sal=sal+200
    WHERE deptno=10 AND DATEDIFF(NOW(),hiredate)/365 >= 20

    eg:更新未排序的前3条数据,前3个出现的name为a的改为name为c

    练习题

    答案选A,因为B是升序排列,应该按照降序才取得到前3名,C项UPDATE子句就是错误的用法,D项LIMIT子句参数只能写一个,LIMIT子句在UPDATE中只能包含有一个参数,代表取前3条数据。

     

     

    7.2.1 UPDATE语句中的内连接

    因为相关子查询效率非常低,所以我们可以利用表连接的方式来改造UPDATE语句

    UPDATE 表1 JOIN 表2 ON 条件
    SET 字段1=值1, 字段2=值2, ......;

    引申出另一种写法

    UPDATE 表1 JOIN 表2
    SET 字段1=值1, 字段2=值2, ......
    WHERE 条件;

    表连接的UPDATE语句可以修改多张表的记录

    eg:把ALLEN调往RESEARCH部门,职务调整为ANALYST

    /*表连接的几种写法*/
    UPDATE t_emp e JOIN t_dept d ON e.ename="ALLEN" AND d.dname="RESEARCH"
    SET e.deptno=d.deptno, e.job="ANALYST"
    
    UPDATE t_emp e JOIN t_dept d
    SET e.deptno=d.deptno, e.job="ANALYST"
    WHERE e.ename="ALLEN" AND d.dname="RESEARCH"
    
    UPDATE t_emp e,t_dept d
    SET e.deptno=d.deptno, e.job="ANALYST"
    WHERE e.ename="ALLEN" AND d.dname="RESEARCH"

     

    分析:其实利用的是笛卡尔积,笛卡尔积一般对于我们连接没什么用,恰恰这里就起了作用,这个例子可以好好推敲一下,表连接的条件直接将ALLEN这个人连接到RESEARCH部门,RESEARCH部门号是20,赋值给ALLEN的部门号就成功修改,接着修改职务即可。

     

    eg:把底薪低于公司平均底薪的员工,底薪增加150元

    sql语句如下

    UPDATE t_emp e JOIN
    (SELECT AVG(sal) avg FROM t_emp) t
    ON e.sal<t.avg
    SET e.sal=e.sal+150;

    执行结果就不演示了,从逻辑上也很好理解。

     

    练习题

    答案选B,和我们上面讲的例子一模一样,即学即用,A项错在标点符号,stu,deptno,这里不是逗号i而是点,C项错在where条件是and而不是or,D项错在update子句不用join的写法连接表,后面条件只能跟where而不是on。

     

    7.2.2 UPDATE语句中的外连接

    UPDATE语句的表连接既可以是内连接,又可以是外连接。

    基本语法

    UPDATE 表1 [LEFT | RIGHT] JOIN 表2 ON 条件
    SET 字段1=值1, 字段2=值2, ......;

    eg:把没有部门的员工,或者SALES部门低于2000元底薪的员工,都调往20部门

    UPDATE t_emp e LEFT JOIN t_dept d ON e.deptno=d.deptno
    SET e.deptno=20
    WHERE e.deptno IS NULL OR (d.dname="SALES" AND e.sal<2000);

    练习题

    答案选C,根据题意,需要保留没有系别的学生,肯定是左连接,A错,B选项的左连接没有写on条件,B错,D选项筛选数据时是and最后会导致没有数据,应该是or。

     

    7.3 删除数据

    基本语法

    DELETE [IGNORE] FROM 表名
    [WHERE 条件1, 条件2, ...]
    [ORDER BY ...]
    [LIMIT ...];

    子句执行顺序:FROM -> WHERE -> ORDER BY -> LIMIT -> DELETE

    ignore表示删除失败就直接忽略而不是报错。

    有了前面新增、更新数据的基础,下面的例子我就不展示数据表的变化了,基本语法比较容易理解。

    eg1:删除10部门中,工龄超过20年的员工记录

    DELETE from t_emp
    WHERE deptno=10 AND DATEDIFF(NOW(),hiredate)/365 >20;

    eg2:删除20部门中工资最高的员工记录

    DELETE FROM t_emp
    WHERE deptno=20
    ORDER BY sal+IFNULL(comm,0) DESC
    LIMIT 1;

    提示:如果表中存在主键自增长,那么当删除之后, 自增长不会还原,下一条数据记录插入会在上一次计数的基础继续增加

    练习题

    答案选A,B错在这里的limit只能写一个参数,C错在删除了奖学金最低的人,应该desc降序排列才对,D错在没有限制条件limit。

     

    7.3.1 DELETE语句中的内连接

    因为相关子查询的效率非常低,所以我们可以利用表连接的方式来改造DELETE语句

    DELETE 表1, ... FROM 表1 JOIN 表2 ON 条件
    [WHERE 条件1, 条件2, ...]
    [ORDER BY ...]
    [LIMIT ...];

    eg1:删除SALES部门该部门的全部员工记录

    DELETE e,d
    FROM t_emp e JOIN t_dept d ON e.deptno=d.deptno
    WHERE d.dname="SALES";

     

    eg2:删除每个低于部门平均底薪的员工记录

    DELETE e
    FROM t_emp e JOIN
    (SELECT deptno, AVG(sal) avg FROM t_emp GROUP BY deptno) t
    ON e.deptno=t.deptno AND e.sal<t.avg;

    eg3:删除员工KING和他的下属的员工记录,用表连接实现

    DELETE e
    FROM t_emp e JOIN
    (SELECT empno FROM t_emp WHERE ename="KING") t
    ON e.mgr=t.empno OR e.empno=t.empno;

    注意,t 这个临时表是不能删除的,表连接出来的记录就是KING的员工下属和KING本身,删除e即可满足要求。数据表的图示操作就不演示了。

     

    练习题

    答案选C,即学即用,A错在没按照deptno条件连接,删除了太多无关记录,B错在delete语句中有表连接却没有指定删除的表名,D错在没有分组,查询出来的平均奖学金作为条件没有意义。

    7.3.2 DELETE语句中的外连接

    基本语法

    DELETE 表1, ... FROM 表1 [LEFT | RIGHT] JOIN 表2 ON 条件
    [WHERE 条件1, 条件2, ...]
    [ORDER BY ...]
    [LIMIT ...]

    eg:删除SALES部门的员工,以及没有部门的员工

    这里注意对比上一小节第一个例题,上一小节是删除SALES部门的员工,这里还要删除没有部门的员工,这就是内连接和外连接在这里使用的区别。

    DELETE e
    FROM t_emp e LEFT JOIN t_dept d ON e.deptno=d.deptno
    WHERE d.dname="SALES" OR e.deptno IS NULL;

     练习题

    答案选B,即学即用,就是上面一个知识点。A错在判断为空是 IS NULL不是=NULL,C错在where条件dept.dept-no拼写错误,D错在删除的食品工程系和没有系别的学生应该是并集而不是交集的关系,所以是or而不是and。

     

    7.3.3 快速删除数据表全部记录

    DELETE语句是在事务机制下删除记录,删除记录之前,先把要删除的记录保存到日志文件里,然后再删除记录。

    TRUNCATE语句在事务机制之外删除记录,速度远超过DELETE语句。

    语法

    TRUNCATE TABLE 表名;

     

    7.4 查询数据

    完整语法

    Select [字段别名]/* from 数据源 [where条件子句] [group by子句] [having子句] [order by子句] [limit 子句];

     

    7.4.1 Select语句

    最基本的查询语句就是SELECT和FROM关键字组成,SELECT语句屏蔽了物理层的操作,用户不必关系数据的真实存储,交互由数据库高效的查询数据。

    All或者*: 默认保留所有的结果

    Distinct: 去重, 查出来的结果,将重复给去除(所有字段都相同)

     

     

    7.4.2 去重查询

    语法格式

    SELECT DISTINCT 字段 FROM 表名;

    假如我们查询员工职业,执行如下语句

    SELECT job FROM t_emp;

    我们发现有很多重复的记录,因为职业是有可能相同的。

    此时我们加上distinct,继续执行

    SELECT DISTINCT job FROM t_emp;

    现在发现查询出来的职业信息就没有重复了。

    注意点:

    1.distinct关键字只能在select子句中使用一次

    SELECT DISTINCT job, DISTINCT ename FROM t_emp;

    写2个distinct直接报错

     

    2.distinct关键字只能写在select子句的第一个字段前面,否则报错,若有多个字段,则distinct失效。

    SELECT job, DISTINCT ename FROM t_emp; 
    /* distinct写在第二个字段前面 */

     

    distinct没有写在第一个字段前面,结果直接报错

    若有多个字段,即使写在第一个字段前面,distinct也失效。

    SELECT DISTINCT job, ename FROM t_emp;

    job并没有想象中的去重,distinct失效了,因为针对了你的所有字段,只要有一个字段不同就算是不同,所以distinct失效了。

     

    3.综上1、2所述,distinct只能存在于select子句查询一个字段的情况,否则要么失效,要么语法报错。

     

    7.4.3 字段别名

    字段别名: 当数据进行查询出来的时候, 有时候名字并不一定就满足需求(多表查询的时候, 会有同名字段). 需要对字段名进行重命名: 别名

    语法

    字段名 [as] 别名;

    再来一个图形化界面的例子

    比如有一个数据表,你想查询员工编号和年收入,你执行结果如下:

    SELECT empno, sal*12 FROM t_emp;

     

    查询的结果集出现了名称为sal*12这一列,语义不明确。添加别名之后

    SELECT empno, sal*12 AS "income" FROM t_emp;

     

    这样就明确多了,这里只是查询的结果集修改了字段,并不会修改底层数据表的字段

    小细节:查询语句的执行顺序是先词法分析与优化,读取SQL语句,然后FROM子句选择数据来源,最后SELECT子句选择输出内容

     

    7.4.4 数据源

    数据源: 数据的来源, 关系型数据库的来源都是数据表。本质上只要保证数据类似二维表,最终都可以作为数据源。

    数据源分为多种: 单表数据源, 多表数据源, 查询语句

     

    单表数据源: select * from 表名;

     

    多表数据源: select* from 表名1,表名2...;

    从一张表中取出一条记录,去另外一张表中匹配所有记录,而且全部保留(记录数和字段数),将这种结果称为笛卡尔积(交叉连接),笛卡尔积没什么用,所以应该尽量避免。

     

    子查询: 数据的来源是一条查询语句(查询语句的结果是二维表)

    Select * from (select 语句) as 表名;

     

    7.4.5 Where子句

    Where子句: 用来判断数据,筛选数据.

    Where子句返回结果: 0或者1, 0代表false,1代表true.

     

    语法格式

    SELECT ... FROM ... WHERE 条件 [AND | OR] 条件 ......;

     

    判断条件:

    比较运算符: >, <, >=, <= ,!= ,<>, =, like, between and, in/not in

    逻辑运算符: &&(and), ||(or), !(not)

     

    Where原理: where是唯一一个直接从磁盘获取数据的时候就开始判断的条件,从磁盘取出一条记录, 开始进行where判断。判断结果如果成立则保存到内存,失败直接放弃.

    条件查询1: 要求找出学生id为1或者3或者5的学生

     

    条件查询2: 查出区间落在180,190身高之间的学生:

     

    Between本身是闭区间。between左边的值必须小于或者等于右边的值

    图形化的例子如下:

    eg1:查询部门编号为10或者20并且收入在2000及以上的记录示例:

    SELECT deptno, empno, ename, sal
    FROM t_emp
    WHERE (deptno=10 OR deptno=20) AND sal >= 2000;

    eg2:查询部门编号为10并且年收入大于15000并且工龄超过20年的职工的一些信息如下

    SELECT deptno, empno, ename, sal, hiredate 
    FROM
    	t_emp 
    WHERE
    	deptno = 10 
    	AND (
    	sal + IFNULL( comm, 0 ))* 12 >= 15000 
    	AND DATEDIFF( NOW(), hiredate )/ 365 >= 20;

     其中IFNULL(comm, 0)表示如果佣金comm为null,则返回0,这里仅仅为了演示IFNULL才加进去的。

    DATEDIFF(NOW(),hiredate)表示当前时间减去入职时间hiredate的天数。

    eg3:查询包含在10,20,30里面的部门编号并且职位不是SALESMAN并且入职日期在1985-01-01以前的员工的一些信息

    SELECT
    empno, ename, sal, deptno, hiredate, job
    FROM t_emp
    WHERE deptno IN(10, 20, 30) AND job != 'SALESMAN'
    AND	hiredate < "1985-01-01";

     

    例子太多了,下面可以不断变换各种比较运算符去举例,由于篇幅原因,这里不一一举例,只写一点需要注意的地方

    例如判断某个字段是NULL就满足条件,是WHERE comm IS NULL而不是WHERE comm = NULL

    如果不为空则满足条件,是WHERE comm IS NOT NULL而不是WHERE comm != NULL

    比如名字我只记得后面是LACK,第一个字母忘了,WHERE ename like "_LACK"

    我只记得是A开头的, WHERE ename LIKE "A%"

    我只记得名字包含字母A,WHERE ename LIKE "%A%"

    名字大部分人都是英文的,有个中文名但是我不记得了,WHERE ename REGEXP "^[\\u4e00-\\9fa5]{2, 4}$"

    汉族人一般名字是2~4个字,汉字Unicode在\\u4e00-\\9fa5之间,^以...开头,$表示以...结尾。这是正则表达式,很强大,感兴趣的小伙伴可以自行下去搜索一下

    来看几道练习题:

    例子1

    答案选择C,题目没有难度,主要熟悉语法

    例子2

    答案选择C,判断null是不能用等号的,而A是查询已经缴纳宿舍费用的学生姓名。

     

    例子3

    答案是AD,总学费是tuition和dorm_money两列之和。主要考察IFNULL和BETWEEN的运用。

    例子4

    答案是A,注意NOT IN的使用。

    例子5

    答案为B,可能有同学的疑问点在A和B两个选项中,A项中,只要名字以赵开头,条件就满足,不再往后继续判断,和C语言的短路语句一个道理。

     

    where语句使用的注意事项:

           WHERE子句中,条件执行的顺序是从左到右的。所以我们应该把索引条件或者筛选掉记录最多的条件写在最左侧。因为索引查询速度快,筛选记录最多的条件更容易触发短路语句的效果,这样就无须执行后续条件就能完成查询。

    小提示:子句的执行顺序是FROM -> WHERE -> SELECT -> ORDER BY -> LIMIT,先选择数据来源,再进行条件筛选,根据筛选完的记录选择输出内容,接着进行排序,最后选择显示的限定条件

     

    7.4.6 聚合函数

    聚合函数在数据查询分析中,应用十分广泛。聚合函数可以对数据求和、求最大值和最小值、求平均值等等。

    比如SQL提供了如下聚合函数

    Count(): 统计分组后的记录数: 每一组有多少记录

    Max(): 统计每组中非空的最大值

    Min(): 统计非空的最小值

    Avg(): 统计平均值

    Sum(): 统计和

    avg()函数:

    eg:比如求公司员工平均月收入是多少?

    SELECT AVG(sal + IFNULL(comm,0)) AS avg FROM t_emp;
    

    这里sal是月收入,comm是佣金。avg()只用来统计数字,不要去统计别的东西

    max()函数:

    eg1:查询10和20部门中,月收入最高的员工?

    SELECT MAX(sal+IFNULL(comm,0)) FROM t_emp
    WHERE deptno IN(10,20)

    eg2:查询员工名字最长的是几个字符?

    SELECT MAX(LENGTH(ename)) FROM t_emp;

    提示:LENGTH()可以统计字符个数

     

    min()函数用法和max()一样

     

    count()函数

    count(*)用于获得包含空值的记录数,count(列名)用于获得包含非空值的记录数

    SELECT COUNT(*), COUNT(comm) FROM t_emp;

    执行结果如上图,表示数据表一共14条数据,而佣金comm不为空的有4条数据

    eg1:查询10和20部门中,底薪超过2000元并且工龄超过15年的员工人数?

    SELECT COUNT(*) FROM t_emp
    WHERE deptno IN(10, 20) AND sal >= 2000
    AND DATEDIFF(NOW(),hiredate)/365 >= 15;

    注意:聚合函数永远不可能出现在where子句里,一定会报错

    练习题:

    答案是D,tuition是学费,dorm_money是宿舍费。都是一些基本语法点的考察。

     

     

    7.4.7 Group by子句

    为什么要分组呢?因为默认情况下汇总函数是对全表范围内的数据做统计。

    Group by:主要用来分组查询, 通过一定的规则将一个数据集划分为若干个小的区域,然后针对每个小区域分别进行数据汇总处理。也就是根据某个字段进行分组(相同的放一组,不同的分到不同的组)

     

    基本语法: group  by 字段名;

    图形化例子:

    eg:根据不同的部门号分组显示平均工资

    SELECT deptno, ROUND(AVG(sal)) FROM t_emp GROUP BY deptno;/*round四舍五入为整数*/

     

    逐级分组

    数据库支持多列分组条件,执行的时候逐级分组

    eg:查询每个部门里,每种职位的人员数量和平均底薪

    SELECT deptno, job, COUNT(*), AVG(sal)
    FROM t_emp
    GROUP BY deptno, job
    ORDER BY deptno;

    这里千万千万要注意一个硬性要求!

    如果查询语句中含有GROUP BY子句,那么SELECT子句中的内容必须遵守如下约定:

           SELECT子句中可以包含聚合函数或者GROUP BY子句的分组列,其余内容均不可以出现在SELECT子句中。否则查询的结果根本没有任何意义,甚至你自己根本看不懂为什么出现这个结果。任何时候看到GROUP BY 马上检查SELECT子句,若有其他字段,不用往下分析,肯定是混乱的查询。

    假如还是上面的例子

    SELECT deptno, job, COUNT(*), AVG(sal)
    FROM t_emp
    GROUP BY deptno /*相比上面的例子,这里没有job分组,但是select子句却出现了job*/
    ORDER BY deptno;
    /* select子句除了分组列字段deptno和聚合函数,还出现了job!查询结果你都看不懂 */

     

           经过对比,部门为10的里面有3条记录,但是job都为MANAGER??明显不对,看上面一个例子就知道了。

     

    对分组结果集再次做汇总计算(回溯统计)

           这里就是WITH ROLLUP的使用

    SELECT deptno, AVG(sal), SUM(sal), MAX(sal), MIN(sal), COUNT(*)
    FROM t_emp
    GROUP BY deptno WITH ROLLUP

     

    使用了WITH ROLLUP之后,你发现最底下还有一行,对应列再次做聚合计算,avg列再次做平均值计算,sum列对上面几个部门数据再次进行sum计算...

     

    GROUP_CONCAT函数

           这个函数可以把分组查询中的某个字段拼接成一个字符串

    eg:查询每个部门内底薪超过2000元的人数和员工姓名

    SELECT deptno, COUNT(*), GROUP_CONCAT(ename)
    FROM t_emp
    WHERE sal >= 2000
    GROUP BY deptno;

    看到ename都是逗号连接的字符串

     

    练习题

    答案选B,单看聚合函数就排除AC,根据含有GROUP BY子句SELECT子句会有硬性要求的问题,SELECT子句除了聚合函数以外的其他字段必须要出现在GROUP BY子句,所以排除D,答案选择B.

     

    小提示:语句的执行顺序如下:

    FROM -> WHERE -> GROUP BY -> SELECT -> ORDER BY -> LIMIT

    FROM 选择数据来源,WHERE选择条件,符合条件的记录留下来,然后经过GROUP BY分组,分完组根据SELECT子句里面聚合函数做计算,然后ORDER BY对结果集排序,最后交给LIMIT挑选返回哪些分页的数据显示。

     

    ====下面几个控制台执行的例子是我之前写的,就不删了,大家也可以对照看一下====

    分组会自动排序: 根据分组字段:默认升序

    Group by 字段 [asc|desc]; -- 对分组的结果然后合并之后的整个结果进行排序

     

    多字段分组: 先根据一个字段进行分组,然后对分组后的结果再次按照其他字段进行分组

     

    有一个函数: 可以对分组的结果中的某个字段进行字符串连接(保留该组所有的某个字段): group_concat(字段);

     

    回溯统计: with rollup: 任何一个分组后都会有一个小组, 最后都需要向上级分组进行汇报统计: 根据当前分组的字段. 这就是回溯统计: 回溯统计的时候会将分组字段置空.

     

    多字段回溯: 考虑第一层分组会有此回溯: 第二次分组要看第一次分组的组数, 组数是多少,回溯就是多少,然后加上第一层回溯即可.

     

    7.4.8 Having子句

    Having子句与where子句一样是进行条件判断的.

    有同学会问了,和where子句功能一样,那还有什么用,多此一举?

    eg1:查询部门平均底薪超过2000的员工数量,你是不是会这样写?

    SELECT deptno, COUNT(*)
    FROM t_emp
    WHERE AVG(sal) >= 2000
    GROUP BY deptno;

    结果运行出错,我们前面也说了,WHERE子句不允许出现聚合函数。而且WHERE优先级高于GROUP BY,在条件筛选的时候不知道按照什么范围去筛选,是全部数据筛选还是分部门数据筛选呢?

    解决方案来了,那就是HAVING子句,HAVING子句的出现主要是为了WHERE子句不能使用聚合函数的问题,HAVING子句不能独立存在,必须依赖于GROUP BY子句而存在,GROUP BY 执行完成就立即执行HAVING子句

    SELECT deptno, COUNT(*)
    FROM t_emp
    GROUP BY deptno HAVING AVG(sal) >= 2000;

    结果就出来了,20部门底薪超过2000的有5人,10部门底薪超过2000的有3人

     

    eg2:查询每个部门中,查询每个部门中,1982年以后入职员工超过2个人的部门编号

    SELECT deptno FROM t_emp
    WHERE hiredate>="1982-01-01"
    GROUP BY deptno HAVING COUNT(*)
    ORDER BY deptno;

     

    可以看到满足条件的有2个部门,10部门和20部门还是有老员工的。

    要注意HAVING子句判断只能和具体数值判断大小,不能和字段以及聚合函数判断,比较要有数值。比如查询工资大于平均工资的人的数量就不能写HAVING sal > AVG(sal),子句判断不是和数值在比较,直接报错。表连接能解决这个问题,后面再讲。

     

    HAVING子句的特殊用法

    如果按照数字1分组,MySQL会按照SELECT子句中的列进行分组,HAVING子句也可以正常使用

    比如按照部门分组,查询各个部门总人数

    SELECT deptno, COUNT(*) FROM t_emp GROUP BY 1;

     

    HAVING的出现是不是可以完全替换WHERE?

    那肯定是不行的,Where是针对磁盘数据进行判断,进入到内存之后会进行分组操作,分组结果就需要having来处理.

    SELECT deptno, COUNT(*) FROM t_emp
    GROUP BY 1
    HAVING deptno IN(10, 30);/*效率低了*/
    
    SELECT deptno, COUNT(*) FROM t_emp 
    WHERE deptno IN(10, 30)
    GROUP BY 1;

     从功能上来说,上面两种写法没有什么区别,但是WHERE优先级在GROUP BY之前,是先把数据按条件筛选完了再分组好呢,还是分完组再去筛选好呢?肯定是前者。所以WHERE能完成的就用WHERE完成,不要放到HAVING中。大量的数据从磁盘读取到内容代价比较大,先筛选完了,再把符合条件的记录读取到内存中显然更好。

     

    Having能做where能做的几乎所有事情, 但是where却不能做having能做的很多事情.

     1.分组统计的结果或者说统计函数都只有having能够使用.

     

    2.Having能够使用字段别名,where不能,where是从磁盘取数据,而名字只可能是字段名,别名是在字段进入到内存后才会产生.

    练习题

    答案选择A,基本语法的运用,看清表是student没有s。

     

    7.4.9 Order by子句

    Order by: 排序, 根据某个字段进行升序或者降序排序, 依赖校对集.

     

    使用基本语法

    单字段排序:

    Order by 字段名 [asc|desc]; -- asc是升序(默认的),desc是降序

    我们再图形化举例示范一下:

    执行如下语句

    SELECT empno, ename, sal, deptno FROM t_emp ORDER BY sal DESC;

    按照sal降序就排好了。

    来个练习题:

    很简单,不用多说就知道答案,估摸着有人在BC里面纠结呢,这不一样吗,答案选B,select选择输出字段之间逗号隔开,细节问题。

     

    多字段排序:

    使用order by 规定首要条件和次要条件排序。数据库会先按照首要条件排序,遇到首要排序内容相同的记录,那么会启用次要条件再次排序。

    使用图形化界面再举一个例子:

    执行如下语句

    SELECT empno, ename, sal, hiredate 
    FROM t_emp ORDER BY sal DESC, hiredate ASC;

    可以看到当首要排序条件sal记录相同时,会按照hiredate进行升序排列

    小提示:

    1.order by 写在 limit前面

    2.子句的执行顺序是FROM -> SELECT -> ORDER BY -> LIMIT,先选择数据来源,再选择输出内容,接着进行排序,最后选择显示的限定条件

    来个练习题:

    A排除,和表不对应,没有name字段,B排除,多字段之间排序用逗号隔开,D排除,升序是ASC或者不写,所以选C。

     

     

    7.4.10 Limit子句

    Limit子句是一种限制结果的语句,用来做数据分页的。

    比如我们看朋友圈,只会加载少量的部分信息,不会一次性加载全部朋友圈,那样只会浪费CPU时间、内存、网络带宽。而结果集的记录可能很多,可以使用limit关键字限定结果集数量。

    Limit有两种使用方式

    方案1: 只用来限制长度(数据量): limit 数据量;

     

    方案2: 限制起始位置,限制数量: limit 起始位置,长度;

     

    Limit方案2主要用来实现数据的分页: 为用户节省时间,提交服务器的响应效率, 减少资源的浪费.

    对于用户来讲: 可以点击的分页按钮: 1,2,3,4

    对于服务器来讲: 根据用户选择的页码来获取不同的数据: limit offset,length;

     

    Length: 每页显示的数据量: 基本不变

    Offset: offset = (页码 - 1) * 每页显示量

    小提示:子句的执行顺序 FROM -> SELECT -> LIMIT,先选择数据来源,再选择输出内容,最后选择显示的限定条件

     

    第8章 连接查询

    连接查询: 将多张表(可以大于2张)进行记录的连接(按照某个指定的条件进行数据拼接): 最终结果是: 记录数有可能变化, 字段数一定会增加(至少两张表的合并)

    连接查询的意义: 在用户查看数据的时候,需要显示的数据来自多张表.

    连接查询: join, 使用方式: 左表 join 右表

    左表: 在join关键字左边的表

    右表: 在join关键字右边的表

    8.1 连接查询分类

    SQL中将连接查询分成四类: 内连接,外连接,自然连接和交叉连接

    8.2 交叉连接

    交叉连接: cross join, 从一张表中循环取出每一条记录, 每条记录都去另外一张表的所有记录逐个进行匹配,并保留所有记录,最终形成的结果叫做笛卡尔积.

    基本语法: 左表 [cross] join 右表。其中cross可以省略

     

    笛卡尔积对于我们的查询没有意义,应该尽量避免(交叉连接没用)

    交叉连接存在的价值: 保证连接这种结构的完整性

     

    8.3 内连接

    内连接: [inner] join, 从左表中取出每一条记录,去右表中与所有的记录进行匹配,匹配必须是某个条件在左表中与右表中相同最终才会保留结果,否则不保留.

    如下,某个条件左右表相同部分的交集

     

    基本语法

    SELECT ...... FROM 表1
    [INNER] JOIN 表2 ON 条件
    [INNER] JOIN 表3 ON 条件
    ......

    内连接其实有多种语法形式,想用哪种看个人喜好,效率上没有区别。

    SELECT ... FROM 表1 JOIN 表2 ON 连接条件;
    SELECT ... FROM 表1 JOIN 表2 WHERE 连接条件;
    SELECT ... FROM 表1, 表2 WHERE 连接条件;

     我们来做个例题,首先我们看到前提条件给出了3张表

    1.员工表t_emp

    2.部门表t_dept

    3.薪资等级表t_salgrade

     

    有人会问了,内连接语法看起来就是交叉连接多了一个ON条件,但是区别可大了,来直观感受一下

    SELECT * FROM t_emp  JOIN t_dept /*交叉连接*/

     

    交叉连接产生笛卡尔积,保留所有结果,导致出现了56条记录

    SELECT * FROM t_emp e JOIN t_dept d ON e.deptno = d.deptno; /*内连接*/

     

    内连接就只针对符合条件的记录去连接,结果集少了很多条记录。

    注意:在查询数据的时候,不同表有同名字段,这个时候需要加上表名才能区分,而表名太长,通常可以使用别名,这里两张表都有deptno,表名也缩短为了一个字母

     

    再来看看具体例题

    eg1:查询每个员工的工号、姓名、部门名称、底薪、职位、工资等级

    分析:工号empno、姓名ename、底薪sal、职位job是在员工表t_emp,部门名称dname是在部门表t_dept,工资等级grade是在薪资等级表t_salgrade。现在就涉及到了3个表的操作,而员工表t_emp和部门表t_dept都有员工编号deptno字段,这个很容易作为筛选条件, 但是工资等级grade却没有相同字段去对应,那么这个就需要找到逻辑关系的对应,用底薪sal去判断薪资等级中的薪水范围即可

    SELECT e.empno, e.ename, d.dname, e.sal, e.job, s.grade
    FROM t_emp e JOIN t_dept d ON e.deptno = d.deptno
    JOIN t_salgrade s ON e.sal BETWEEN s.losal AND s.hisal;

    eg2:查询与SCOTT相同部门的员工

    分析:还是那3张表,要查和某个人相同部门的员工,有人就开始这么做,上去就是一个sql

    SELECT ename
    FROM t_emp
    WHERE deptno=(SELECT deptno FROM t_emp WHERE ename="SCOTT")
    AND ename!="SCOTT";

    括号中的查询我们称为子表,子表中查询到deptno然后把结果集给父表继续查询,写完感觉自我良好,殊不知自己写了一个领导看到就想把你开除的sql。

    FROM先执行,获取了数据表的每条记录,再去WHERE进行筛选,万一有上万条数据呢?WHERE会逐一判断上万条数据是否满足条件的时候都要去查询一个子表,相当于SELECT deptno FROM t_emp WHERE ename="SCOTT"被你执行了上万次,而子表也是上万条数据,每一次父表的条件判断又会执行上万次子表查询,数据量小的时候看不出差异,数据量大了就很明显了。

    这里用表连接的效率远远高于子查询

    SELECT e2.ename
    FROM t_emp e1 JOIN t_emp e2 ON e1.deptno=e2.deptno
    WHERE e1.ename="SCOTT" AND e2.ename!="SCOTT";

    先内连接减少数据源结果集的数量,然后进行筛选。能达到和子查询一样的效果,效率比子查询要高。

     

    eg3:查询底薪超过公司平均底薪的员工信息

    SELECT e.empno, e.ename, e.sal
    FROM t_emp e JOIN
    (SELECT AVG(sal) avg FROM t_emp) t 
    ON e.sal >= t.avg;

    把平均底薪查询结果当作一个表再和员工表t_emp连接,返回FROM子句。之前说过,这个问题是WHERE解决不了的,WHERE里面不能出现聚合函数的,直接写WHERE sal >= AVG(sal)肯定报错,而HAVING子句又只能和数值比较,这里e.sal>=t.avg表达式两边都是变量,HAVING子句无法解决。

     

    eg4:查询RESEARCH部门人数、最高底薪、最低底薪、平均底薪、平均工龄

    SELECT COUNT(*), MAX(e.sal), MIN(e.sal), AVG(e.sal),
    AVG(DATEDIFF(NOW(),e.hiredate)/365)
    FROM t_emp e JOIN t_dept d ON e.deptno=d.deptno
    WHERE d.dname="RESEARCH";

    如果前面的题目都懂了,这题就是语法复习,表连接和聚合函数的使用。

    eg5:查询每种职业的最高工资、最低工资、平均工资、最高工资等级和最低工资等级

    分析:涉及到工资等级,需要薪资等级表t_salgrade,那么就是员工表和薪资等级表的连接,因为同一种职业不同人有不同的收入,所有根据收入等级排工资等级,逻辑要捋清楚。

    SELECT
    e.job,
    MAX(e.sal + IFNULL(e.comm,0)),
    MIN(e.sal + IFNULL(e.comm,0)),
    AVG(e.sal + IFNULL(e.comm,0)),
    MAX(s.grade),
    MIN(s.grade)
    FROM t_emp e JOIN t_salgrade s
    ON (e.sal + IFNULL(e.comm,0)) BETWEEN s.losal AND s.hisal
    GROUP BY e.job;

     

     

    eg6:查询每个底薪超过部门平均底薪的员工信息

    SELECT e.empno, e.ename, e.sal
    FROM t_emp e JOIN
    (SELECT deptno, AVG(sal) avg FROM t_emp GROUP BY deptno) t
    ON e.deptno=t.deptno AND e.sal >= t.avg;

    如果只运行子表查询,得到各个部门平均底薪,可以和上图对比一下

     

    练习一下选择题

    答案选B,都是语法细节,多一个少一个标点符号的问题。A错在别名问题,应该将子表别名写在括号外,C错在没有join,写了个逗号,D错在,select子句少了逗号,这个题目考察眼力哈哈哈。

    答案选择A,考察表连接的另一种写法SELECT ... FROM 表1, 表2 WHERE 连接条件,排除D,因为两个表之间没有逗号,再排除C,因为只从一张表查不出那么多信息,最后排除B,因为NOW()后面没有逗号。

     

     

    8.4 外连接

    外连接分为两种:左(外)连接和右(外)连接。

    左外连接就是保留左表所有记录,与右表做连接。如果右表有符合条件的记录就与左表连接。如果右表没有符合条件的记录,就用NULL与左表连接。右连接也是如此。

    基本语法: 左表 left/right join 右表 on 左表.字段 = 右表.字段;

     

    为什么要有外连接?

           我们还是以内连接中提到的3张数据表为例子。

           如果有一名临时员工,没有固定的部门编号,那么我们查询每名员工和他的部门名称,用内连接就会漏掉临时员工,所以要引入外连接语法才能解决这个问题。外连接与内连接的区别在于,除了符合条件的记录之外,结果集中还会保留不符合条件的记录。

    含有临时员工的员工表t_emp

    部门表t_dept

     

    薪资等级表t_salgrade

    eg1:查询每名员工和他的部门名称

    假设我们使用内连接,我们根本查不到临时员工信息,因为临时员工没有部门编号,如下:

    SELECT e.empno, e.ename, d.dname
    FROM t_emp e JOIN t_dept d
    ON e.deptno=d.deptno;

    当我们使用外连接时,就能够查到临时员工,如下:

    /*左连接*/
    SELECT e.empno, e.ename, d.dname
    FROM t_emp e LEFT JOIN t_dept d
    ON e.deptno=d.deptno;
    
    /*右连接,换一下表的顺序,结果集一样*/
    SELECT e.empno, e.ename, d.dname
    FROM t_dept d RIGHT JOIN t_emp e 
    ON e.deptno=d.deptno;

    左表是员工表,左连接保留所有记录,没有部门编号的临时员工信息也会保留,右表部门编号没有与之匹配,那就用NULL连接。

     

    eg2:查询每个部门的名称和部门的人数

    有人容易写出下面的错误sql语句

    SELECT d.dname, COUNT(*)
    FROM t_dept d LEFT JOIN t_emp e
    ON d.deptno=e.deptno
    GROUP BY d.deptno;/*按部门分组,所以有group by*/

     

    这题很多细节,很多人会出错,40部门的部门名称为dname为OPERATIONS里没有员工,居然还是有一条记录,因为你在连接的时候左表记录全部保留,在右表中没有员工与OPERATIONS部门匹配,连接的是NULL,这也是一条记录,所以这里才会出现1。

    但是你也不要写成COUNT(d.deptno),因为左边部门表记录全保留,d.deptno有40部门,40部门的dname就是OPERATIONS,右表与之连接的都是NULL,道理和上面一样。

    所以你得按照右边员工表计算,COUNT(e.deptno),记录各个部门非空记录数。40部门没有员工,右表e.deptno没有40,NULL不会被COUNT(e.deptno)计算入内,所以是0,符合预期。

    正确的sql语句如下:

    SELECT d.dname, COUNT(e.deptno)
    FROM t_dept d LEFT JOIN t_emp e
    ON d.deptno=e.deptno
    GROUP BY d.deptno;/*按部门分组,所以有group by*/

     

    eg3:查询每个部门的名称和部门的人数,如果是没有部门的临时员工,部门名称用NULL代替

    分析:我们上一个例子已经做到了查询部门名称和部门的人数,现在就差一个临时员工和他的部门的问题,临时员工还在等着被你统计呢。临时员工在t_emp表,所以你要保留这个表的所有内容再把eg2例子的查询语句一起联合查询

    (SELECT d.dname, COUNT(e.deptno)
    FROM t_dept d LEFT JOIN t_emp e
    ON d.deptno=e.deptno
    GROUP BY d.deptno) UNION
    (SELECT d.dname, COUNT(*)
    FROM t_dept d RIGHT JOIN t_emp e
    ON d.deptno=e.deptno
    GROUP BY d.deptno);

    这个部门名称dname为NULL的就是那个临时员工。

    eg4:查询每名员工的编号、姓名、部门名称、月薪、工资等级、工龄、上司编号、上司姓名、上司部门(这个题有点综合,没点基础做不出来)

    分析:要查员工的编号、姓名、部门名称、工龄,涉及到员工表t_emp、部门表t_dept,查工资等级涉及到薪资等级表t_salgrade,有的员工是其他员工的上司,所以我们为员工表再做一次查询连接起来当作领导表,连接条件是员工的领导编号和领导的员工编号相等时,这个领导表查出来的员工,就是员工表里对应员工的领导。sql如下,你细品

    SELECT 
    	e.empno, e.ename, d.dname,
    	e.sal + IFNULL(e.comm,0), s.grade,
    	FLOOR(DATEDIFF(NOW(),e.hiredate)/365),
    	t.empno AS mgrno, t.ename AS mname, t.dname AS mdname
    FROM t_emp e LEFT JOIN t_dept d ON e.deptno=d.deptno
    LEFT JOIN t_salgrade s ON e.sal BETWEEN s.losal AND s.hisal
    LEFT JOIN
    (SELECT	e1.empno, e1.ename, d1.dname
    FROM t_emp e1 JOIN t_dept d1
    ON e1.deptno=d1.deptno
    ) t ON e.mgr=t.empno;

    外连接的注意事项:

    内连接只保留符合条件的记录,所以查询条件写在ON子句和WHERE子句中的效果是相同的。但是外连接里,条件写在WHERE子句里,不符合条件的记录是会被过滤掉的,而不是保留下来。

    我们来看看具体差别

    SELECT e.ename, d.dname, d.deptno
    FROM t_emp e
    LEFT JOIN t_dept d ON e.deptno=d.deptno
    AND e.deptno=10; /*这里是and不是where*/

     分析:左连接保留左表全部,按条件连接右表,不仅要部门编号相同,还要部门编号为10,不满足的用NULL连接,所以总记录条数就是左表的COUNT(*)数量

     

    改为WHERE之后

    SELECT e.ename, d.dname, d.deptno
    FROM t_emp e
    LEFT JOIN t_dept d ON e.deptno=d.deptno
    WHERE e.deptno=10;

    分析:左连接保留左表全部,按照部门号进行对应连接,连接完再进行筛选员工部门号位10的记录,不满足的就过滤。一步步的执行过程如下图

     

    8.5 自然连接

    自然连接: natural join, 自然连接, 就是自动匹配连接条件: 系统以字段名字作为匹配模式(同名字段就作为条件, 多个同名字段都作为条件).

     

    自然连接: 可以分为自然内连接和自然外连接.

     

    自然内连接: 左表 natural join 右表;

     

    自然外连接: 左表 natural left/right join 右表;

     

    其实, 内连接和外连接都可以模拟自然连接: 使用同名字段,合并字段

    左表 left/right/inner join 右表 using(字段名); -- 使用同名字段作为连接条件: 自动合并条件

     

    多表连接: A表 inner join B表 on 条件 left join C表 on条件 ...

    执行顺序: A表内连接B表,得到一个二维表, 左连接C表形成二维表..

     

    8.6 子查询

    子查询: sub query, 查询是在某个查询结果之上进行的.(一条select查询的sql语句内部包含了另外一条select查询的sql语句).

     

    8.6.1 子查询分类

    Where子查询: 子查询出现where条件中,where语句里不推荐使用子查询,每执行一次where条件筛选,就会进行一次子查询,效率低下。像这种反复子查询就属于相关子查询,where语句的子查询都属于相关子查询,我们要避免相关子查询的存在。

    比如查询底薪超过公司平均底薪的员工信息

    From子查询: 子查询跟在from之后,通常这种子查询的结果集作为一个临时表,from子查询只会执行一次,不是相关子查询,所以查询效率高。

    SELECT子查询,子查询跟在SELECT之后,SELECT子查询也是相关子查询,不推荐

     

     

     

     

     

    8.6.2 单行子查询和多行子查询

    单行子查询的结果集只有一条记录,多行子查询结果集有多行记录

    多行子查询只能出现在WHERE子句和FROM子句中

    eg:如何用子查询查找FORD和MARTIN两个人的同事?

    分析:同一个部门的都算作同事,而且题目限定了用子查询来做,所以不用表连接做。

    SELECT ename FROM t_emp
    WHERE deptno IN
    (SELECT deptno FROM t_emp WHERE ename IN("FORD","MARTIN"))
    AND ename NOT IN("FORD","MARTIN");

    当然这个题目用表连接做时最好的,效率比WHERE里面子查询高的多,只不过这里题目要求用子查询,这里我们还是给出表连接的sql语句供大家参考

    SELECT ename
    FROM t_emp e
    JOIN
    (SELECT deptno FROM t_emp WHERE ename IN("FORD","MARTIN")) d
    ON e.deptno=d.deptno
    AND ename NOT IN("FORD","MARTIN");/*不需要用e.ename因为只有e表有ename*/

     

    8.6.3 WHERE子句中的多行子查询

    WHERE子句中,可以用IN、ALL、ANY、EXISTS关键字来处理多行表达式结果集的条件判断。

    eg:查询比FORD和MARTIN底薪都高的员工信息?

    SELECT ename FROM t_emp
    WHERE sal > ALL
    (SELECT sal FROM t_emp
    WHERE ename IN("FORD","MARTIN"));
    

    这里是ALL,表示比FORD和MARTIN底薪都高,如果换成ANY,则表示比两者任意一个高就满足条件

     

    8.6.4 子查询的EXISTS关键字

    EXISTS关键字是把原来在子查询之外的条件判断,写到了子查询的里面。

    SELECT ... FROM 表名 WHERE [NOT] EXISTS (子查询)

    eg:查询工资等级是3级或者4级的员工信息

    SELECT empno, ename, sal
    FROM t_emp
    WHERE EXISTS(
    SELECT *           /*这里选择其他字段也可以,比如grade*/
    FROM t_salgrade
    WHERE sal BETWEEN losal AND hisal
    AND grade IN(3,4)
    )

    只要子查询结果为不为空,那么EXISTS这个条件就是满足的,这条记录就满足条件不会被过滤。

    这里只是演示WHERE多行子查询的EXISTS关键字,解决这个问题用表连接其实好的多。如下:

    SELECT empno, ename, sal
    FROM t_emp
    JOIN t_salgrade
    ON sal BETWEEN losal AND hisal AND grade IN(3,4)

     

     

    第9章 视图

    视图: view, 是一种有结构(有行有列)但是没结果(结构中不真实存放数据)的虚拟表, 虚拟表的结构来源不是自己定义, 而是从对应的基表中产生(视图的数据来源).

     

    9.1 创建视图

    基本语法

    Create view 视图名字 as select语句; -- select语句可以是普通查询;可以是连接查询; 可以是联合查询; 可以是子查询.

     

    创建单表视图: 基表只有一个

    创建多表视图: 基表来源至少两个

     

    9.2 查看视图

    查看视图: 查看视图的结构

     

    视图是一张虚拟表: 表, 表的所有查看方式都适用于视图: show tables [like]/desc 视图名字/show create table 视图名;

     

    视图比表还是有一个关键字的区别: view. 查看”表(视图)”的创建语句的时候可以使用view关键字

     

    视图一旦创建: 系统会在视图对应的数据库文件夹下创建一个对应的结构文件: frm文件

     

    9.3 使用视图

    使用视图主要是为了查询: 将视图当做表一样查询即可.

     

    视图的执行: 其实本质就是执行封装的select语句.

     

    9.4 修改视图

    视图本身不可修改, 但是视图的来源是可以修改的.

     

    修改视图: 修改视图本身的来源语句(select语句)

    Alter view 视图名字 as 新的select语句;

     

    9.5 删除视图

    Drop view 视图名字;

     

    9.6 视图意义

    1. 视图可以节省SQL语句: 将一条复杂的查询语句使用视图进行保存: 以后可以直接对视图进行操作
    2. 数据安全: 视图操作是主要针对查询的, 如果对视图结构进行处理(删除), 不会影响基表数据(相对安全).
    3. 视图往往是在大项目中使用, 而且是多系统使用: 可以对外提供有用的数据, 但是隐藏关键(无用)的数据: 数据安全
    4. 视图可以对外提供友好型: 不同的视图提供不同的数据, 对外好像专门设计
    5. 视图可以更好(容易)的进行权限控制
     

    9.7 视图数据操作

    视图是的确可以进行数据写操作的: 但是有很多限制

    将数据直接在视图上进行操作.

     

    9.7.1 新增数据

    数据新增就是直接对视图进行数据新增.

     

    1.多表视图不能新增数据

     

    2.可以向单表视图插入数据: 但是视图中包含的字段必须有基表中所有不能为空(或者没有默认值)字段

     

    3.视图是可以向基表插入数据的.

     

    9.7.2 删除数据

    多表视图不能删除数据

     

    单表视图可以删除数据

     

    9.7.3 更新数据

    理论上不能单表视图还是多表示视图都可以更新数据.

     

    更新限制: with check option, 如果对视图在新增的时候,限定了某个字段有限制: 那么在对视图进行数据更新操作时,系统会进行验证: 要保证更新之后,数据依然可以被实体查询出来,否则不让更新.

     

    9.8 视图算法

    视图算法: 系统对视图以及外部查询视图的Select语句的一种解析方式.

     

    视图算法分为三种

    Undefined: 未定义(默认的), 这不是一种实际使用算法, 是一种推卸责任的算法: 告诉系统,视图没有定义算法, 系统自己看着办

    Temptable: 临时表算法: 系统应该先执行视图的select语句,后执行外部查询语句

    Merge: 合并算法: 系统应该先将视图对应的select语句与外部查询视图的select语句进行合并,然后执行(效率高: 常态)

     

    算法指定: 在创建视图的时候

    Create algorithm = 指定算法 view 视图名字 as select语句;

     

    视图算法选择: 如果视图的select语句中会包含一个查询子句(五子句), 而且很有可能顺序比外部的查询语句要靠后, 一定要使用算法temptable,其他情况可以不用指定(默认即可).

    第10章 数据备份与还原

    备份: 将当前已有的数据或者记录保留

    还原: 将已经保留的数据恢复到对应的表中

     

    为什么要做备份还原?

    防止数据丢失: 被盗, 误操作
    保护数据记录
     

    数据备份还原的方式有很多种: 数据表备份, 单表数据备份, SQL备份, 增量备份.

     

    10.1 数据表备份

    不需要通过SQL来备份: 直接进入到数据库文件夹复制对应的表结构以及数据文件, 以后还原的时候,直接将备份的内容放进去即可.

     

    数据表备份有前提条件: 根据不同的存储引擎有不同的区别.

     

    存储引擎: mysql进行数据存储的方式: 主要是两种: innodb和myisam(免费)

     

    对比myisam和innodb: 数据存储方式

    Innodb: 只有表结构,数据全部存储到ibdata1文件中

    Myisam: 表,数据和索引全部单独分开存储

     

    这种文件备份通常适用于myisam存储引擎: 直接复制三个文件即可, 然后直接放到对应的数据库下即可以使用.

     

    10.2 单表数据备份

    每次只能备份一张表; 只能备份数据(表结构不能备份)

    如果业务数据非常多,建议只导出表结构,然后用SELECT INTO OUTFILE把数据导出成文本文档,具体操作可以看10.5节图形化操作。

     

    备份: 从表中选出一部分数据保存到外部的文件中(outfile)

    Select */字段列表 into outfile 文件所在路径 from 数据源; -- 前提: 外部文件不存在

     

    高级备份: 自己制定字段和行的处理方式

    Select */字段列表 into outfile 文件所在路径 fields 字段处理 lines 行处理 from 数据源;

    Fields: 字段处理

    Enclosed by: 字段使用什么内容包裹, 默认是’’,空字符串

    Terminated by: 字段以什么结束, 默认是”\t”, tab键

    Escaped by: 特殊符号用什么方式处理,默认是’\\’, 使用反斜杠转义

    Lines: 行处理

    Starting by: 每行以什么开始, 默认是’’,空字符串

    Terminated by: 每行以什么结束,默认是”\r\n”,换行符

     

    数据还原: 将一个在外部保存的数据重新恢复到表中(如果表结构不存在,那么sorry)

    Load data infile 文件所在路径 into table 表名[(字段列表)] fields字段处理 lines 行处理; -- 怎么备份的怎么还原

     

    10.3 SQL备份与还原

    备份的是SQL语句: 系统会对表结构以及数据进行处理,变成对应的SQL语句, 然后进行备份: 还原的时候只要执行SQL指令即可.(主要就是针对表结构)

     

    备份: mysql没有提供备份指令: 需要利用mysql提供的软件: mysqldump.exe

    Mysqldump.exe也是一种客户端,需要操作服务器: 必须连接认证

    Mysqldump/mysqldump.exe -hPup 数据库名字 [数据表名字1[ 数据表名字2...]] > 外部文件目录(建议使用.sql)

    mysqldump用来把业务数据导出成SQL文件,其中也包括了表结构

    mysqldump -uroot -p [no-data] 逻辑库 > 路径

    不写no-data表示既包含表结构,又包含数据

    单表备份

    图形化操作如下,选中数据表,点击右键

     

    整库备份

    Mysqldump/mysqldump.exe -hPup 数据库名字 > 外部文件目录

    对应图形化操作如下,选中数据库选中右键

     

    SQL还原数据: 两种方式还原

    方案1: 使用mysql.exe客户端还原

    Mysql.exe/mysql -hPup 数据库名字 < 备份文件目录

     

    方案2: 使用SQL指令还原

    1.use选择数据库; 2.Source 备份文件所在路径;

    对应图形化操作如下

     

    SQL备份优缺点

    优点: 可以备份结构
    缺点: 会浪费空间(额外的增加SQL指令)

    练习题

    答案选A,语法记忆,注意标点符号。

     

     

    10.4 增量备份

    不是针对数据或者SQL指令进行备份: 是针对mysql服务器的日志文件进行备份

    增量备份: 指定时间段开始进行备份., 备份数据不会重复, 而且所有的操作都会备份(大项目都用增量备份)

    练习题

    答案选C,C错在数据导出,导出的纯粹是业务数据。

     

    10.5 大文件备份和还原(图形化操作,推荐!)

    业务数据比较多的时候,只导出表结构到sql文件,业务数据文件导出到txt文件,这样就跳过了sql词法分析和语法优化,哪怕导入几千万条数据,也可以在1分钟内导入完毕

    1.导出表结构

    2.导出表中业务数据

     

    3.删除表,为导入做准备

     

    4.导入表结构

    5.刷新后看到表结构

    6.导入业务数据文件

     

    7.刷新表即可看到导入成功

     

    第11章 事务安全

    事务: transaction, 一系列要发生的连续的操作

    事务安全: 一种保护连续操作同时满足(实现)的一种机制

    事务安全的意义: 保证数据操作的完整性

     

    如果SQL语句直接操作文件是很危险的,比如你要给员工涨工资,正在update操作的时候,系统断电了,你就不知道谁已经涨了谁还没涨。

    我们应该利用日志来间接写入。

    MySQL总共5种日志,其中只有redo日志和undo日志与事务有关

     

    日志就相当于数据文件的一个副本,SQL语句操作什么样的记录,MySQL就会把这些记录拷贝到undo日志,然后增删改查的操作就会记录到redo日志,最后把redo日志和数据库文件进行同步就行了。即使同步过程中断电了,有了redo日志的存在,重启MySQL数据库之后继续同步数据,同步成功后我们修改的数据就真正同步到数据库里面了,有事务的数据库抵抗风险的能力变强了。

    RDBMS=SQL语句+事务(ACID)

    事务是一个或者多个SQL语句组成的整体,要么全部执行成功,要么全部失败。

     

    11.1 事务操作

    事务操作分为两种: 自动事务(默认的), 手动事务

    默认情况下,MySQL执行每条SQL语句都会自动开启和提交事务。为了让多条SQL语句纳入到一个事物之下,可以手动管理事务。

    START TRANSACTION;

    SQL语句

    [COMMIT | ROLLBACK];

    START TRANSACTION;
    
    DELETE FROM t_emp;
    DELETE FROM t_dept;
    SELECT * FROM t_emp;
    SELECT * FROM t_dept;

    开启事务: 告诉系统以下所有的操作(写)不要直接写入到数据表, 先存放到redo日志。

    删除员工表t_emp和部门表t_dept之后,SQL语句查询两表的的数据均为空

    但是去看数据表的数据却仍然存在,这是为什么呢?

    因为你开启了事务,你现在的操作还在redo日志里面,并没有同步到数据库文件里面,你只有COMMIT之后才会同步

    继续执行

    COMMIT;

    去数据表查看,2张数据表都被清空了。

    当然你也可以直接回滚,执行ROLLBACK;

    ROLLBACK;

    这样你的redo日志被清空,下次操作的时候重新往redo日志里面进行操作,就不会受到上一次操作的影响。

     

    11.2 自动事务处理

    在mysql中: 默认的都是自动事务处理, 用户操作完会立即同步到数据表中.

    自动事务: 系统通过autocommit变量控制

    Show variables like ‘autocommit’;

     

    关闭自动提交: set autocommit = off/0;

     

    再次直接写操作

     

    自动关闭之后,需要手动来选择处理: commit提交, rollback回滚

     

    注意: 通常都会使用自动事务

     

    11.3 事务原理

    事务操作原理: 事务开启之后, 所有的操作都会临时保存到事务日志, 事务日志只有在得到commit命令才会同步到数据表,其他任何情况都会清空(rollback, 断电, 断开连接)

     

    11.4 回滚点

    回滚点: 在某个成功的操作完成之后, 后续的操作有可能成功有可能失败, 但是不管成功还是失败,前面操作都已经成功: 可以在当前成功的位置, 设置一个点: 可以供后续失败操作返回到该位置, 而不是返回所有操作, 这个点称之为回滚点.

     

    设置回滚点语法: savepoint 回滚点名字;

     

    回到回滚点语法: rollback to 回滚点名字;

     

    11.5 事务ACID属性

    A: Atomic原子性,一个事物中的所有操作要么全部完成,要么全部失败。事物执行后,不允许停留在中间某个状态。

    C: Consistency一致性,不管在任何给定的时间,并发事务有多少,事务必须保证运行结果的一致性。事务可以并发执行,但是最终MySQL却串行执行。

    怎么保证一致性?

    阻止事务之间相互读取临时数据

    I: Isolation隔离性,每个事务只能看到事务内的相关数据,别的事务的临时数据在当前事务是看不到的。隔离性要求事务不受其他并发事务的影响,在给定时间内,该事务是数据库运行的唯一事务。

     

    如果事务没有隔离性,按照不受控制的顺序并发读取和修改数据,想像一下会出现哪些问题?

    一、脏读:一个事务读取了第二个事物未提交的数据,当第二个事务回滚了数据之后,第一个事务就读取到了无效的数据。

    如下图,事务1查询course_id=59的平均分score为9.2,而事务2此时将其平均分修改为9.6,当事务1再次读取的时候,平均分就变成了9.6,此时事务2回滚,事务1就是读取的无效数据,简称脏读。

    二、不可重复读:一个事物前后两次读取的同一数据不一致。

    如下图,事务1查询course_id=59的平均分score为9.6,而事务2此时将其平均分修改为9.7,并将修改提交,当事务1再次读取的时候,平均分就变成了9.7,事务1就是读取的错误数据,注意,不可重复读和脏读的区别就是,脏读的数据会回滚,不可重复读会把数据提交,脏读的数据是无效的,而不可重复读因为事务2的提交,数据是有效的。

    三、幻读:指一个事务两次查询的结果集记录数不一致

    如下图,事务1查询到平均分在9.5到9.8之间的记录数是2条,经过事务2对course_id=43的平均分修改,导致事务1第二次查询的记录数为3条,这种情况就叫幻读,幻读的数据最终也是有效的数据。

    innodb的事务隔离性保证了我们事务操作的安全,才让我们实际操作中并没有出现这么多问题。

    怎么保证隔离性?

    undo和redo日志中的数据都会被标记属于哪个事务的,所以事务执行过程中就只能读到自己的临时数据了。

    D: Durability持久性,事务一旦提交,结果便是永久性的。即便发生宕机,仍然可依靠事务日志完成数据持久化。

     

    锁机制: innodb默认是行锁, 但是如果在事务操作的过程中, 没有使用到索引,那么系统会自动全表检索数据, 自动升级为表锁

    行锁: 只有当前行被锁住, 别的用户不能操作

    表锁: 整张表被锁住, 别的用户都不能操作

     

    11.6 事务的隔离级别

    在某些特定场合,我们又想让事务之间读取到一些临时数据,这就需要修改事务的隔离级别

    设置事务隔离级别的语法如下:

    SET [PERSIST|GLOBAL|SESSION]
        TRANSACTION ISOLATION LEVEL
        {
            READ UNCOMMITTED | READ COMMITTED
            | REPEATABLE READ
            | SERIALIZABLE
        }
    
    -- PERSIST:所有连接到mysql服务的新的连接都有效,并且mysql服务器重启后也不会丢失修改
    -- GLOCAL: 所有连接到mysql服务的新的连接都有效,但是mysql服务器重启后会丢失这个修改
    -- SESSION:开发最常用,只会影响到当前连接,当前连接断开,这个隔离级别的修改就会丢失
    
    -- 开发中也可以用show variables like '%iso%'查看当前session的隔离级别
    -- 因为有一个变量参数名为transaction_isolation

     

    11.6.1 read uncommitted

    场景一:比如买票的场景,逢年过节都需要买票回家,假如A和B都在买同一辆车的车票,此时还剩最后一张票,A点击购买,但是还没付款提交,因为查看不到事务之间的临时数据,所以B查看时,也还剩一张票,于是B点击购买,立即付款提交,结果A就会购买失败。所以理想的情况应该是,当A点击购买去付款时,B应该看得到这个临时数据,显示没有票才对。这种场景会出现脏读、幻读、不可重复读情况,隔离性最低,并发性最高。

    eg1:查看事务之间能否读取未提交的数据

    START TRANSACTION;
    UPDATE t_emp SET sal=1;

    此时开启事务1并进行更新操作,但是没有commit

    再开启一个事务2

    START TRANSACTION;
    SELECT empno, ename, sal FROM t_emp;
    COMMIT;

    注意:这里没有修改数据,仅仅只是select查询数据,redo日志没有改变,所以不会做同步到文件的操作,commit之后会清空对应的undo日志数据。

    结果显示如下,前者在事务1中修改sal为1,事务2中却看不到。

    如果修改事务2隔离级别,如下

    SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; /*代表可以读取其他事务未提交的数据*/
    START TRANSACTION;
    SELECT empno, ename, sal FROM t_emp;
    COMMIT;

    结果立马就变了,事务2能够读取事务1未提交的数据,但是要注意,因为前者并未commit,所以数据库表文件的数据还没有修改

     

    11.6.2 read committed

    场景二:银行转账的场景,A事务执行往Scott账户转账1000的操作,B事务执行扣除Scott账户100块的操作,如果A能读取到B事务未提交的数据,那么转账后就会修改为5900,而此时因为各种原因需要回滚支出100元的这个操作,此时账户就只有5900块了,凭空消失100块,所以只有A事务读取到B事务提交后的数据才能保证转账的正确性。这种场景就和买票的场景完全不同。这种场景是会出现幻读和不可重复读的。

    还是eg1的例子,此时修改隔离级别的SQL语句即可

    SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;/*只能读取其他事务提交的数据*/
    START TRANSACTION;
    SELECT empno, ename, sal FROM t_emp;
    COMMIT;

    其他事物提交的数据都会同步到数据库表文件中,所以这里就是从数据库表文件中读取的数据。

     

    11.6.3 repeatable read

    场景三:你在淘宝或者京东等电商,点击购买,选好收货地址之类的之后,点击提交订单,就会让你输入支付密码支付,此时显示的价格是undo日志的价格,如果此时卖家涨价,你购买的还是涨价之前的价格,这种场景就是可重复读。可重复读不会出现脏读、不可重复读的情况,因为事务1读取不到事务2对数据的修改。对于幻读,这里只有靠临键锁才能保证不出现幻读的问题。

    新建一个查询,开启事务1

    SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;/*事务在执行中反复读取数据,得到的结果是一致的*/
    START TRANSACTION;
    SELECT empno, ename, sal FROM t_emp;

    这里一定要先执行一次select语句,保证undo日志拷贝过一次数据

    再新建一个查询,开启事务2

    START TRANSACTION;
    UPDATE t_emp SET sal=1;

    此时数据库表文件的数据如下

     

    此时在事务1执行SELECT empno, ename, sal FROM t_emp;

    虽然数据库表文件的数据已经修改了,但是事务1处的事务隔离级别是可以反复读,每次都从undo日志里面读取,所以这里还是修改前的价格,直到提交commit,commit之后清空对应的undo日志记录,下次会重新从数据库文件里面拷贝数据,那个时候才是sal=1的数据。

    注意:MySQL默认事务隔离级别就是REPEATABLE READ

     

    11.6.4 serializable

    由于事务并发执行所带来的各种问题,前三种隔离级别只适用于在某种业务场景中,凡事序列化的隔离性,让事务逐一执行,就不会产生上述问题了。但是序列化的隔离级别使用的特别少,它让事务的并发性大大降低。可重复读不会出现幻读、脏读、不可重复读的情况,因为事务1读取不到事务2对数据的修改。隔离性最高,并发性最低,其实就是没有并发,所有事务按照顺序执行。

    开始事务1,sql语句如下

    START TRANSACTION;
    UPDATE t_emp SET sal=2;

    开始事务2,sql语句如下

    SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;/*事务序列化*/
    START TRANSACTION;
    SELECT empno, ename, sal FROM t_emp;

    但是这行sql之后并没有出结果

    直到你的事务1执行commit之后,事务2就会立即执行查询结果。

    针对这4种隔离级别与脏读幻读不可重复读的关系如下:

     

    第12章 触发器

    触发器: trigger, 事先为某张表绑定好一段代码 ,当表中的某些内容发生改变的时候(增删改)系统会自动触发代码,执行.

    触发器: 事件类型, 触发时间, 触发对象

    事件类型: 增删改, 三种类型insert,delete和update

    触发时间: 前后: before和after

    触发对象: 表中的每一条记录(行)

     

    一张表中只能拥有一种触发时间的一种类型的触发器: 最多一张表能有6个触发器

     

    12.1 创建触发器

    在mysql高级结构中: 没有大括号,  都是用对应的字符符号代替

     

    触发器基本语法

    -- 临时修改语句结束符

    Delimiter 自定义符号: 后续代码中只有碰到自定义符号才算结束

     

    Create trigger 触发器名字 触发时间 事件类型 on 表名 for each row

    Begin -- 代表左大括号: 开始

    -- 里面就是触发器的内容: 每行内容都必须使用语句结束符: 分号

    End -- 代表右带括号: 结束

    -- 语句结束符

    自定义符号

    -- 将临时修改修正过来

    Delimiter  ;

     

    12.2 查看触发器

    查看所有触发器或者模糊匹配

    Show triggers [like ‘pattern’];

    \g 的作用是分号和在sql语句中写’;’是等效的

    \G 的作用是将查到的结构旋转90度变成纵向

     

    可以查看触发器创建语句

    Show create trigger 触发器名字;

     

    所有的触发器都会保存一张表中: Information_schema.triggers

     

    12.3 使用触发器

    触发器: 不需要手动调用, 而是当某种情况发生时会自动触发.(订单里面插入记录之后)

     

    12.4 修改触发器&删除触发器

    触发器不能修改,只能先删除,后新增.

    Drop trigger 触发器名字;

     

    12.5 触发器记录

    触发器记录: 不管触发器是否触发了,只要当某种操作准备执行, 系统就会将当前要操作的记录的当前状态和即将执行之后新的状态给分别保留下来, 供触发器使用: 其中, 要操作的当前状态保存到old中, 操作之后的可能形态保存给new.

    Old代表的是旧记录,new代表的是新记录

    删除的时候是没有new的; 插入的时候是没有old

     

    Old和new都是代表记录本身: 任何一条记录除了有数据, 还有字段名字.

    使用方式: old.字段名 / new.字段名(new代表的是假设发生之后的结果)

     

    查看触发器的效果

     

    如果触发器内部只有一条要执行的SQL指令, 可以省略大括号(begin和end)

    Create trigger 触发器名字 触发时间 事件类型 on 表名 for each row

    一条SQL指令;

    触发器: 可以很好的协调表内部的数据处理顺序和关系. 但是从JAVA角度出发, 触发器会增加数据库维护的难度, 所以较少使用触发器.

    第13章 函数

     

    13.1 数字函数

    eg:求四舍五入

    select round(4.6288*100)/100;

    13.2 日期函数

    13.2.1 获取系统时间函数

    NOW()函数能获得系统日期和时间,格式yyyy-MM-dd hh:mm:ss,数据库的最小时间单位是秒s,而不是毫秒ms
    CURDATE()函数能获得当前系统日期,格式yyyy-MM-dd
    CURTIME()函数能获得当前系统时间,格式hh:mm:ss

    SELECT NOW(), CURDATE(), CURTIME();

     

    13.2.2 日期格式化函数

    DATE_FORMAT(日期,  表达式)

    该函数用于格式化日期,返回用户想要的日期格式

    eg:比如查看员工入职的年份

    SELECT ename, DATE_FORMAT(hiredate,"%Y") AS result FROM t_emp;

    占位符说明

    eg:查询某个日期是星期几

    SELECT DATE_FORMAT("2021-1-1","%w");

    结果是星期5,如果是大写%W,那么就输出英文Friday

    eg:利用日期函数,查询1981年上半年入职的员工有多少个

    SELECT COUNT(*) FROM t_emp
    WHERE DATE_FORMAT(hiredate,"%Y")=1981
    AND DATE_FORMAT(hiredate,"%m")<=6;

    练习题

     

    答案选A,语法基础。

     

    13.2.3 日期偏移计算

    注意:MySQL数据库里面,两个日期不能直接加减,日期也不能与数字加减
    比如 select hiredate+1 from t_emp;
    其实hiredate是"1980-12-18"变成了19801218,然后+1,结果是19801219

    DATE_ADD(日期, INTERVAL 偏移量  偏移的时间单位)

    该函数可以实现日期的偏移计算,而且时间单位很灵活

    举几个例子

    /*100天之后是什么时间*/
    SELECT DATE_ADD(NOW(), INTERVAL 100 DAY);
    /*300分钟之前是什么时间*/
    SELECT DATE_ADD(NOW(), INTERVAL -300 MINUTE);
    /*6个月零3天之前是什么时间*/
    SELECT DATE_ADD(DATE_ADD(NOW(),INTERVAL -6 MONTH),INTERVAL -3 DAY)

    把日期偏移函数和日期格式化函数混合用一下

    eg:6个月零3天之前是什么时间,保留年月日即可

    SELECT 
    DATE_FORMAT(DATE_ADD(DATE_ADD(NOW(),INTERVAL -6 MONTH), INTERVAL -3 DAY), "%Y/%m/%d");

     

     

    13.2.4 计算日期之间相隔的天数

    DATEDIFF(日期1, 日期2)

    该函数用来计算两个日期之间相差的天数为日期1-日期2。

    eg:比如计算现在和2019-1-1相差多少天

    SELECT DATEDIFF(NOW(),"2019-1-1");

     

    2019-1-1已经是707天之前了。

     

    13.3 字符函数

    eg:查询员工表中姓名小写、姓名大写、姓名包含的字符数、底薪末尾添加$,姓名包含有A

    SELECT
    	LOWER(ename), UPPER(ename), LENGTH(ename),
    	CONCAT(sal,"$"),INSTR(ename,"A")
    FROM t_emp;

    这里对于汉字,LOWER和UPPER函数是没有转换作用的,对于LENGTH函数,因为这里的数据库编码是UTF8字符集,所以一个汉字占3个字节,长度为6,INSTR函数会返回首次出现A的位置,从1开始,如果没有包含A,则返回0。

    INSERT例子

    /*插入"先生"并替换从1开始的3个字符*/
    SELECT INSERT("女士早上好", 1, 3, "先生");

     

    REPLACE例子

    SELECT REPLACE("女士早上好","女士","先生");

     

    SUBSTR、SUBSTRING、LPAD、TRIM例子

    SELECT SUBSTR("你好世界", 3, 4), SUBSTRING("你好世界", 3, 2),
    LPAD(SUBSTRING("13312345678", 8, 4),11,"*"),
    TRIM("                Hello World    ");

    说明:SUBSTR("你好世界", 3, 4)表示获取从1开始下标为[3,4]闭区间位置子串,SUBSTRING("13312345678", 8, 4)表示获取从下标8开始后面的4个字符,LPAD(SUBSTRING("13312345678", 8, 4),11,"*")表示子串将由"*"左填充到11个字符的长度,TRIM就是去除首尾空格。

    练习题

    答案选C,A项错在直接把最后4位也用*替代了,B错在substring下标从1开始,D错在是rpad而不是lpad。

     

     

    13.4 条件函数

    13.4.1 简单条件判断

    SQL语句可以利用条件函数来实现编程语言里的条件判断

    IFNULL(表达式, 值)

    IF(表达式, 值1, 值2)

    eg:SALES部门发放礼品A,其余部门发放礼品B,打印每名员工获得的礼品

    SELECT
    	e.empno, e.ename, d.dname,
    	IF(d.dname="SALES","礼品A","礼品B")
    FROM t_emp e JOIN t_dept d ON e.deptno=d.deptno;

    练习题

    答案选D,A错在as写成逗号,B错在函数用错,if也是3个参数,C错在入学日期和系号之间没有逗号分隔。

     

    答案选A,B错在函数错用ifnull,并且ifnull也是2个参数而不是3个,C错在if函数里面的相框参数填写反了,D错在根们没有打印相框类型。

     

    13.4.2 复杂条件判断

    复杂的条件判断可以用条件语句来实现,比IF语句功能更强大

    CASE
        WHEN 表达式 THEN 值1
        WHEN 表达式 THEN 值2
        ...
        ELSE 值N
    END

     

    eg:公司集体旅游,每个部门目的地不同,SALES部门去P1地点,ACCOUNTING部门去P2地点,RESEARCH部门去P3地点,查询每名员工的旅行地点。

    SELECT
    	e.empno, e.ename,
    	CASE
    		WHEN d.dname="SALES" THEN "p1"
    		WHEN d.dname="ACCOUNTING" THEN "p2"
    		WHEN d.dname="RESEARCH" THEN "P3"
    		END AS place
    FROM t_emp e JOIN t_dept d ON e.deptno=d.deptno;

    eg:公司调整员工基本工资,具体方案如下:

    1.SALES部门中工龄超过20年,涨幅10%
    2.SALES部门中工龄不满20年,涨幅5%
    3.ACCOUNTING部门,涨幅300
    4.RESEARCH部门里低于部门平均底薪,涨幅200
    5.没有部门的员工,涨幅100

    UPDATE t_emp e LEFT JOIN t_dept d ON e.deptno=d.deptno
    LEFT JOIN (SELECT deptno, AVG(sal) avg FROM t_emp GROUP BY deptno) t
    ON e.deptno=d.deptno
    SET sal=(
    	CASE
    		WHEN d.dname="SALES" AND DATEDIFF(NOW(),e.hiredate)/365>=20
    		THEN e.sal*1.1
    		WHEN d.dname="SALES" AND DATEDIFF(NOW(),e.hiredate)/365<20
    		THEN e.sal*1.05
    		WHEN d.dname="ACCOUNTING" THEN e.sal+300
    		WHEN d.dname="RESEARCH" AND e.sal<t.avg THEN e.sal+200
    		WHEN e.deptno IS NULL THEN e.sal+100
    		ELSE e.sal
    	END
    );
    

     

     

     

    13.5 自定义函数

    函数要素: 函数名, 参数列表(形参和实参), 返回值, 函数体(作用域)

    13.5.1 创建函数

    创建语法

    Create function  函数名([形参列表]) returns 数据类型 -- 规定要返回的数据类型

    Begin

    -- 函数体

    -- 返回值: return 类型(指定数据类型);

    End

     

    定义函数

     

    自定义函数与系统函数的调用方式是一样: select 函数名([实参列表]);

     

    13.5.2 查看函数

    查看所有函数: show function status [like ‘pattern’];

     

    查看函数的创建语句: show create function 函数名;

     

    13.5.3 修改函数&删除函数

    函数只能先删除后新增,不能修改.

    Drop function 函数名;

     

    13.5.4 函数参数

    参数分为两种: 定义时的参数叫形参, 调用时的参数叫实参(实参可以是数值也可以是变量)

    形参: 要求必须指定数据类型

    Function 函数名(形参名字 字段类型) returns 数据类型

     

    在函数内部使用@定义的变量在函数外部也可以访问

     

    13.5.5 作用域

    Mysql中的作用域与js中的作用域完全一样

    全局变量可以在任何地方使用; 局部变量只能在函数内部使用.

    全局变量: 使用set关键字定义, 使用@符号标志

    局部变量: 使用declare关键字声明, 没有@符号: 所有的局部变量的声明,必须在函数体开始之前

     

    第14章 存储过程

    存储过程简称过程,procedure, 是一种用来处理数据的方式.

    存储过程是一种没有返回值的函数.

     

    14.1 创建过程

    Create procedure 过程名字([参数列表])

    Begin

    -- 过程体

    End

     

    14.2 查看过程

    函数的查看方式完全适用于过程: 关键字换成procedure

    查看所有过程: show procedure status [like ‘pattern’];

     

    查看过程创建语句: show create procedure 过程名;

     

    14.3 调用过程

    过程没有返回值: select是不能访问的.

     

    过程有一个专门的调用关键字: call

     

    14.4 修改过程&删除过程

    过程只能先删除,后新增

    Drop procedure 过程名;

     

    14.5 过程参数

    函数的参数需要数据类型指定, 过程比函数更严格.

    过程还有自己的类型限定: 三种类型

    In: 数据只是从外部传入给内部使用(值传递): 可以是数值也可以是变量

    Out: 只允许过程内部使用(不用外部数据), 给外部使用的.(引用传递: 外部的数据会被先清空才会进入到内部): 只能是变量

    Inout: 外部可以在内部使用,内部修改也可以给外部使用: 典型的引用传递: 只能传变量

     

    基本使用

    Create procedure 过程名(in 形参名字 数据类型, out 形参名字 数据类型, inout 形参名字 数据类型)

     

    调用: out和inout类型的参数必须传入变量,而不能是数值

     

    正确调用: 传入变量

     

    存储过程对于变量的操作(返回)是滞后的: 是在存储过程调用结束的时候,才会重新将内部修改的值赋值给外部传入的全局变量.

     

    测试: 传入数据1,2,3: 说明局部变量与全局变量无关

     

    最后: 在存储过程调用结束之后, 系统会将局部变量重复返回给全局变量(out和inout)

     

    关注、留言,我们一起学习。

     

    ===============Talk is cheap, show me the code================

    展开全文
  • MySQL数据库面试题(2020最新版)

    万次阅读 多人点赞 2020-03-10 17:20:40
    数据库三大范式是什么mysql有关权限的表都有哪几个MySQL的binlog有有几种录入格式?分别有什么区别?数据类型mysql有哪些数据类型引擎MySQL存储引擎MyISAM与InnoDB区别MyISAM索引与InnoDB索引的区别?InnoDB引擎的4...

    大家好,我是CSDN的博主ThinkWon,“2020博客之星年度总评选"开始啦,希望大家帮我投票,每天都可以投多票哦,点击下方链接,然后点击"最大”,再点击"投TA一票"就可以啦!
    投票链接:https://bss.csdn.net/m/topic/blog_star2020/detail?username=thinkwon
    在技术的世界里,ThinkWon将一路与你相伴!创作出更多更高质量的文章!2020为努力奋斗的你点赞👍,️新的一年,祝各位大牛牛气冲天,牛年大吉!😊😊

    文章目录

    Java面试总结汇总,整理了包括Java基础知识,集合容器,并发编程,JVM,常用开源框架Spring,MyBatis,数据库,中间件等,包含了作为一个Java工程师在面试中需要用到或者可能用到的绝大部分知识。欢迎大家阅读,本人见识有限,写的博客难免有错误或者疏忽的地方,还望各位大佬指点,在此表示感激不尽。文章持续更新中…

    序号 内容 链接地址
    1 Java基础知识面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390612
    2 Java集合容器面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588551
    3 Java异常面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390689
    4 并发编程面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104863992
    5 JVM面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390752
    6 Spring面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397516
    7 Spring MVC面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397427
    8 Spring Boot面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397299
    9 Spring Cloud面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397367
    10 MyBatis面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/101292950
    11 Redis面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/103522351
    12 MySQL数据库面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104778621
    13 消息中间件MQ与RabbitMQ面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588612
    14 Dubbo面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390006
    15 Linux面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588679
    16 Tomcat面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397665
    17 ZooKeeper面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397719
    18 Netty面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104391081
    19 架构设计&分布式&数据结构与算法面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/105870730

    数据库基础知识

    为什么要使用数据库

    数据保存在内存

    优点: 存取速度快

    缺点: 数据不能永久保存

    数据保存在文件

    优点: 数据永久保存

    缺点:1)速度比内存操作慢,频繁的IO操作。2)查询数据不方便

    数据保存在数据库

    1)数据永久保存

    2)使用SQL语句,查询方便效率高。

    3)管理数据方便

    什么是SQL?

    结构化查询语言(Structured Query Language)简称SQL,是一种数据库查询语言。

    作用:用于存取数据、查询、更新和管理关系数据库系统。

    什么是MySQL?

    MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。在Java企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展。

    数据库三大范式是什么

    第一范式:每个列都不可以再拆分。

    第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。

    第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。

    在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由。比如性能。事实上我们经常会为了性能而妥协数据库的设计。

    mysql有关权限的表都有哪几个

    MySQL服务器通过权限表来控制用户对数据库的访问,权限表存放在mysql数据库里,由mysql_install_db脚本初始化。这些权限表分别user,db,table_priv,columns_priv和host。下面分别介绍一下这些表的结构和内容:

    • user权限表:记录允许连接到服务器的用户帐号信息,里面的权限是全局级的。
    • db权限表:记录各个帐号在各个数据库上的操作权限。
    • table_priv权限表:记录数据表级的操作权限。
    • columns_priv权限表:记录数据列级的操作权限。
    • host权限表:配合db权限表对给定主机上数据库级操作权限作更细致的控制。这个权限表不受GRANT和REVOKE语句的影响。

    MySQL的binlog有有几种录入格式?分别有什么区别?

    有三种格式,statement,row和mixed。

    • statement模式下,每一条会修改数据的sql都会记录在binlog中。不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能。由于sql的执行是有上下文的,因此在保存的时候需要保存相关的信息,同时还有一些使用了函数之类的语句无法被记录复制。
    • row级别下,不记录sql语句上下文相关信息,仅保存哪条记录被修改。记录单元为每一行的改动,基本是可以全部记下来但是由于很多操作,会导致大量行的改动(比如alter table),因此这种模式的文件保存的信息太多,日志量太大。
    • mixed,一种折中的方案,普通操作使用statement记录,当无法使用statement的时候使用row。

    此外,新版的MySQL中对row级别也做了一些优化,当表结构发生变化的时候,会记录语句而不是逐行记录。

    数据类型

    mysql有哪些数据类型

    分类 类型名称 说明
    整数类型 tinyInt 很小的整数(8位二进制)
    smallint 小的整数(16位二进制)
    mediumint 中等大小的整数(24位二进制)
    int(integer) 普通大小的整数(32位二进制)
    小数类型 float 单精度浮点数
    double 双精度浮点数
    decimal(m,d) 压缩严格的定点数
    日期类型 year YYYY 1901~2155
    time HH:MM:SS -838:59:59~838:59:59
    date YYYY-MM-DD 1000-01-01~9999-12-3
    datetime YYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00~ 9999-12-31 23:59:59
    timestamp YYYY-MM-DD HH:MM:SS 19700101 00:00:01 UTC~2038-01-19 03:14:07UTC
    文本、二进制类型 CHAR(M) M为0~255之间的整数
    VARCHAR(M) M为0~65535之间的整数
    TINYBLOB 允许长度0~255字节
    BLOB 允许长度0~65535字节
    MEDIUMBLOB 允许长度0~167772150字节
    LONGBLOB 允许长度0~4294967295字节
    TINYTEXT 允许长度0~255字节
    TEXT 允许长度0~65535字节
    MEDIUMTEXT 允许长度0~167772150字节
    LONGTEXT 允许长度0~4294967295字节
    VARBINARY(M) 允许长度0~M个字节的变长字节字符串
    BINARY(M) 允许长度0~M个字节的定长字节字符串
    • 1、整数类型,包括TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT,分别表示1字节、2字节、3字节、4字节、8字节整数。任何整数类型都可以加上UNSIGNED属性,表示数据是无符号的,即非负整数。
      长度:整数类型可以被指定长度,例如:INT(11)表示长度为11的INT类型。长度在大多数场景是没有意义的,它不会限制值的合法范围,只会影响显示字符的个数,而且需要和UNSIGNED ZEROFILL属性配合使用才有意义。
      例子,假定类型设定为INT(5),属性为UNSIGNED ZEROFILL,如果用户插入的数据为12的话,那么数据库实际存储数据为00012。

    • 2、实数类型,包括FLOAT、DOUBLE、DECIMAL。
      DECIMAL可以用于存储比BIGINT还大的整型,能存储精确的小数。
      而FLOAT和DOUBLE是有取值范围的,并支持使用标准的浮点进行近似计算。
      计算时FLOAT和DOUBLE相比DECIMAL效率更高一些,DECIMAL你可以理解成是用字符串进行处理。

    • 3、字符串类型,包括VARCHAR、CHAR、TEXT、BLOB
      VARCHAR用于存储可变长字符串,它比定长类型更节省空间。
      VARCHAR使用额外1或2个字节存储字符串长度。列长度小于255字节时,使用1字节表示,否则使用2字节表示。
      VARCHAR存储的内容超出设置的长度时,内容会被截断。
      CHAR是定长的,根据定义的字符串长度分配足够的空间。
      CHAR会根据需要使用空格进行填充方便比较。
      CHAR适合存储很短的字符串,或者所有值都接近同一个长度。
      CHAR存储的内容超出设置的长度时,内容同样会被截断。

      使用策略:
      对于经常变更的数据来说,CHAR比VARCHAR更好,因为CHAR不容易产生碎片。
      对于非常短的列,CHAR比VARCHAR在存储空间上更有效率。
      使用时要注意只分配需要的空间,更长的列排序时会消耗更多内存。
      尽量避免使用TEXT/BLOB类型,查询时会使用临时表,导致严重的性能开销。

    • 4、枚举类型(ENUM),把不重复的数据存储为一个预定义的集合。
      有时可以使用ENUM代替常用的字符串类型。
      ENUM存储非常紧凑,会把列表值压缩到一个或两个字节。
      ENUM在内部存储时,其实存的是整数。
      尽量避免使用数字作为ENUM枚举的常量,因为容易混乱。
      排序是按照内部存储的整数

    • 5、日期和时间类型,尽量使用timestamp,空间效率高于datetime,
      用整数保存时间戳通常不方便处理。
      如果需要存储微妙,可以使用bigint存储。
      看到这里,这道真题是不是就比较容易回答了。

    引擎

    MySQL存储引擎MyISAM与InnoDB区别

    存储引擎Storage engine:MySQL中的数据、索引以及其他对象是如何存储的,是一套文件系统的实现。

    常用的存储引擎有以下:

    • Innodb引擎:Innodb引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。
    • MyIASM引擎(原本Mysql的默认引擎):不提供事务的支持,也不支持行级锁和外键。
    • MEMORY引擎:所有的数据都在内存中,数据的处理速度快,但是安全性不高。

    MyISAM与InnoDB区别

    MyISAM Innodb
    存储结构 每张表被存放在三个文件:frm-表格定义、MYD(MYData)-数据文件、MYI(MYIndex)-索引文件 所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB
    存储空间 MyISAM可被压缩,存储空间较小 InnoDB的表需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引
    可移植性、备份及恢复 由于MyISAM的数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作 免费的方案可以是拷贝数据文件、备份 binlog,或者用 mysqldump,在数据量达到几十G的时候就相对痛苦了
    文件格式 数据和索引是分别存储的,数据.MYD,索引.MYI 数据和索引是集中存储的,.ibd
    记录存储顺序 按记录插入顺序保存 按主键大小有序插入
    外键 不支持 支持
    事务 不支持 支持
    锁支持(锁是避免资源争用的一个机制,MySQL锁对用户几乎是透明的) 表级锁定 行级锁定、表级锁定,锁定力度小并发能力高
    SELECT MyISAM更优
    INSERT、UPDATE、DELETE InnoDB更优
    select count(*) myisam更快,因为myisam内部维护了一个计数器,可以直接调取。
    索引的实现方式 B+树索引,myisam 是堆表 B+树索引,Innodb 是索引组织表
    哈希索引 不支持 支持
    全文索引 支持 不支持

    MyISAM索引与InnoDB索引的区别?

    • InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。
    • InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效。
    • MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。
    • InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效。

    InnoDB引擎的4大特性

    • 插入缓冲(insert buffer)

    • 二次写(double write)

    • 自适应哈希索引(ahi)

    • 预读(read ahead)

    存储引擎选择

    如果没有特别的需求,使用默认的Innodb即可。

    MyISAM:以读写插入为主的应用程序,比如博客系统、新闻门户网站。

    Innodb:更新(删除)操作频率也高,或者要保证数据的完整性;并发量高,支持事务和外键。比如OA自动化办公系统。

    索引

    什么是索引?

    索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。

    索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。

    更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。

    索引有哪些优缺点?

    索引的优点

    • 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
    • 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

    索引的缺点

    • 时间方面:创建索引和维护索引要耗费时间,具体地,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,会降低增/改/删的执行效率;
    • 空间方面:索引需要占物理空间。

    索引使用场景(重点)

    where

    img

    上图中,根据id查询记录,因为id字段仅建立了主键索引,因此此SQL执行可选的索引只有主键索引,如果有多个,最终会选一个较优的作为检索的依据。

    -- 增加一个没有建立索引的字段
    alter table innodb1 add sex char(1);
    -- 按sex检索时可选的索引为null
    EXPLAIN SELECT * from innodb1 where sex='男';
    

    img

    可以尝试在一个字段未建立索引时,根据该字段查询的效率,然后对该字段建立索引(alter table 表名 add index(字段名)),同样的SQL执行的效率,你会发现查询效率会有明显的提升(数据量越大越明显)。

    order by

    当我们使用order by将查询结果按照某个字段排序时,如果该字段没有建立索引,那么执行计划会将查询出的所有数据使用外部排序(将数据从硬盘分批读取到内存使用内部排序,最后合并排序结果),这个操作是很影响性能的,因为需要将查询涉及到的所有数据从磁盘中读到内存(如果单条数据过大或者数据量过多都会降低效率),更无论读到内存之后的排序了。

    但是如果我们对该字段建立索引alter table 表名 add index(字段名),那么由于索引本身是有序的,因此直接按照索引的顺序和映射关系逐条取出数据即可。而且如果分页的,那么只用取出索引表某个范围内的索引对应的数据,而不用像上述那取出所有数据进行排序再返回某个范围内的数据。(从磁盘取数据是最影响性能的)

    join

    join语句匹配关系(on)涉及的字段建立索引能够提高效率

    索引覆盖

    如果要查询的字段都建立过索引,那么引擎会直接在索引表中查询而不会访问原始数据(否则只要有一个字段没有建立索引就会做全表扫描),这叫索引覆盖。因此我们需要尽可能的在select只写必要的查询字段,以增加索引覆盖的几率。

    这里值得注意的是不要想着为每个字段建立索引,因为优先使用索引的优势就在于其体积小。

    索引有哪几种类型?

    主键索引: 数据列不允许重复,不允许为NULL,一个表只能有一个主键。

    唯一索引: 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。

    • 可以通过 ALTER TABLE table_name ADD UNIQUE (column); 创建唯一索引

    • 可以通过 ALTER TABLE table_name ADD UNIQUE (column1,column2); 创建唯一组合索引

    普通索引: 基本的索引类型,没有唯一性的限制,允许为NULL值。

    • 可以通过ALTER TABLE table_name ADD INDEX index_name (column);创建普通索引

    • 可以通过ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);创建组合索引

    全文索引: 是目前搜索引擎使用的一种关键技术。

    • 可以通过ALTER TABLE table_name ADD FULLTEXT (column);创建全文索引

    索引的数据结构(b树,hash)

    索引的数据结构和具体存储引擎的实现有关,在MySQL中使用较多的索引有Hash索引B+树索引等,而我们经常使用的InnoDB存储引擎的默认索引实现为:B+树索引。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。

    1)B树索引

    mysql通过存储引擎取数据,基本上90%的人用的就是InnoDB了,按照实现方式分,InnoDB的索引类型目前只有两种:BTREE(B树)索引和HASH索引。B树索引是Mysql数据库中使用最频繁的索引类型,基本所有存储引擎都支持BTree索引。通常我们说的索引不出意外指的就是(B树)索引(实际是用B+树实现的,因为在查看表索引时,mysql一律打印BTREE,所以简称为B树索引)

    img

    查询方式:

    主键索引区:PI(关联保存的时数据的地址)按主键查询,

    普通索引区:si(关联的id的地址,然后再到达上面的地址)。所以按主键查询,速度最快

    B+tree性质:

    1.)n棵子tree的节点包含n个关键字,不用来保存数据而是保存数据的索引。

    2.)所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。

    3.)所有的非终端结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)关键字。

    4.)B+ 树中,数据对象的插入和删除仅在叶节点上进行。

    5.)B+树有2个头指针,一个是树的根节点,一个是最小关键码的叶节点。

    2)哈希索引

    简要说下,类似于数据结构中简单实现的HASH表(散列表)一样,当我们在mysql中用哈希索引时,主要就是通过Hash算法(常见的Hash算法有直接定址法、平方取中法、折叠法、除数取余法、随机数法),将数据库字段数据转换成定长的Hash值,与这条数据的行指针一并存入Hash表的对应位置;如果发生Hash碰撞(两个不同关键字的Hash值相同),则在对应Hash键下以链表形式存储。当然这只是简略模拟图。

    img

    索引的基本原理

    索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。

    索引的原理很简单,就是把无序的数据变成有序的查询

    1. 把创建了索引的列的内容进行排序

    2. 对排序结果生成倒排表

    3. 在倒排表内容上拼上数据地址链

    4. 在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据

    索引算法有哪些?

    索引算法有 BTree算法和Hash算法

    BTree算法

    BTree是最常用的mysql数据库索引算法,也是mysql默认的算法。因为它不仅可以被用在=,>,>=,<,<=和between这些比较操作符上,而且还可以用于like操作符,只要它的查询条件是一个不以通配符开头的常量, 例如:

    -- 只要它的查询条件是一个不以通配符开头的常量
    select * from user where name like 'jack%'; 
    -- 如果一通配符开头,或者没有使用常量,则不会使用索引,例如: 
    select * from user where name like '%jack'; 
    

    Hash算法

    Hash Hash索引只能用于对等比较,例如=,<=>(相当于=)操作符。由于是一次定位数据,不像BTree索引需要从根节点到枝节点,最后才能访问到页节点这样多次IO访问,所以检索效率远高于BTree索引。

    索引设计的原则?

    1. 适合索引的列是出现在where子句中的列,或者连接子句中指定的列
    2. 基数较小的类,索引效果较差,没有必要在此列建立索引
    3. 使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间
    4. 不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。

    创建索引的原则(重中之重)

    索引虽好,但也不是无限制的使用,最好符合一下几个原则

    1) 最左前缀匹配原则,组合索引非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

    2)较频繁作为查询条件的字段才去创建索引

    3)更新频繁字段不适合创建索引

    4)若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)

    5)尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。

    6)定义有外键的数据列一定要建立索引。

    7)对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。

    8)对于定义为text、image和bit的数据类型的列不要建立索引。

    创建索引的三种方式,删除索引

    第一种方式:在执行CREATE TABLE时创建索引

    CREATE TABLE user_index2 (
    	id INT auto_increment PRIMARY KEY,
    	first_name VARCHAR (16),
    	last_name VARCHAR (16),
    	id_card VARCHAR (18),
    	information text,
    	KEY name (first_name, last_name),
    	FULLTEXT KEY (information),
    	UNIQUE KEY (id_card)
    );
    

    第二种方式:使用ALTER TABLE命令去增加索引

    ALTER TABLE table_name ADD INDEX index_name (column_list);
    

    ALTER TABLE用来创建普通索引、UNIQUE索引或PRIMARY KEY索引。

    其中table_name是要增加索引的表名,column_list指出对哪些列进行索引,多列时各列之间用逗号分隔。

    索引名index_name可自己命名,缺省时,MySQL将根据第一个索引列赋一个名称。另外,ALTER TABLE允许在单个语句中更改多个表,因此可以在同时创建多个索引。

    第三种方式:使用CREATE INDEX命令创建

    CREATE INDEX index_name ON table_name (column_list);
    

    CREATE INDEX可对表增加普通索引或UNIQUE索引。(但是,不能创建PRIMARY KEY索引)

    删除索引

    根据索引名删除普通索引、唯一索引、全文索引:alter table 表名 drop KEY 索引名

    alter table user_index drop KEY name;
    alter table user_index drop KEY id_card;
    alter table user_index drop KEY information;
    

    删除主键索引:alter table 表名 drop primary key(因为主键只有一个)。这里值得注意的是,如果主键自增长,那么不能直接执行此操作(自增长依赖于主键索引):

    img

    需要取消自增长再行删除:

    alter table user_index
    -- 重新定义字段
    MODIFY id int,
    drop PRIMARY KEY
    

    但通常不会删除主键,因为设计主键一定与业务逻辑无关。

    创建索引时需要注意什么?

    • 非空字段:应该指定列为NOT NULL,除非你想存储NULL。在mysql中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值;
    • 取值离散大的字段:(变量各个取值之间的差异程度)的列放到联合索引的前面,可以通过count()函数查看字段的差异值,返回值越大说明字段的唯一值越多字段的离散程度高;
    • 索引字段越小越好:数据库的数据存储以页为单位一页存储的数据越多一次IO操作获取的数据越大效率越高。

    使用索引查询一定能提高查询的性能吗?为什么

    通常,通过索引查询数据比全表扫描要快。但是我们也必须注意到它的代价。

    • 索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时,索引本身也会被修改。 这意味着每条记录的INSERT,DELETE,UPDATE将为此多付出4,5 次的磁盘I/O。 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。使用索引查询不一定能提高查询性能,索引范围查询(INDEX RANGE SCAN)适用于两种情况:
    • 基于一个范围的检索,一般查询返回结果集小于表中记录数的30%
    • 基于非唯一性索引的检索

    百万级别或以上的数据如何删除

    关于索引:由于索引需要额外的维护成本,因为索引文件是单独存在的文件,所以当我们对数据的增加,修改,删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的IO,会降低增/改/删的执行效率。所以,在我们删除数据库百万级别数据的时候,查询MySQL官方手册得知删除数据的速度和创建的索引数量是成正比的。

    1. 所以我们想要删除百万数据的时候可以先删除索引(此时大概耗时三分多钟)
    2. 然后删除其中无用数据(此过程需要不到两分钟)
    3. 删除完成后重新创建索引(此时数据较少了)创建索引也非常快,约十分钟左右。
    4. 与之前的直接删除绝对是要快速很多,更别说万一删除中断,一切删除会回滚。那更是坑了。

    前缀索引

    语法:index(field(10)),使用字段值的前10个字符建立索引,默认是使用字段的全部内容建立索引。

    前提:前缀的标识度高。比如密码就适合建立前缀索引,因为密码几乎各不相同。

    实操的难度:在于前缀截取的长度。

    我们可以利用select count(*)/count(distinct left(password,prefixLen));,通过从调整prefixLen的值(从1自增)查看不同前缀长度的一个平均匹配度,接近1时就可以了(表示一个密码的前prefixLen个字符几乎能确定唯一一条记录)

    什么是最左前缀原则?什么是最左匹配原则

    • 顾名思义,就是最左优先,在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。
    • 最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
    • =和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式

    B树和B+树的区别

    • 在B树中,你可以将键和值存放在内部节点和叶子节点;但在B+树中,内部节点都是键,没有值,叶子节点同时存放键和值。

    • B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。

      img

    使用B树的好处

    B树可以在内部节点同时存储键和值,因此,把频繁访问的数据放在靠近根节点的地方将会大大提高热点数据的查询效率。这种特性使得B树在特定数据重复多次查询的场景中更加高效。

    使用B+树的好处

    由于B+树的内部节点只存放键,不存放值,因此,一次读取,可以在内存页中获取更多的键,有利于更快地缩小查找范围。 B+树的叶节点由一条链相连,因此,当需要进行一次全数据遍历的时候,B+树只需要使用O(logN)时间找到最小的一个节点,然后通过链进行O(N)的顺序遍历即可。而B树则需要对树的每一层进行遍历,这会需要更多的内存置换次数,因此也就需要花费更多的时间

    Hash索引和B+树所有有什么区别或者说优劣呢?

    首先要知道Hash索引和B+树索引的底层实现原理:

    hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据。B+树底层实现是多路平衡查找树。对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据。

    那么可以看出他们有以下的不同:

    • hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询。

    因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询。而B+树的的所有节点皆遵循(左节点小于父节点,右节点大于父节点,多叉树也类似),天然支持范围。

    • hash索引不支持使用索引进行排序,原理同上。
    • hash索引不支持模糊查询以及多列索引的最左前缀匹配。原理也是因为hash函数的不可预测。AAAA和AAAAB的索引没有相关性。
    • hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询。
    • hash索引虽然在等值查询上较快,但是不稳定。性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能极差。而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低。

    因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度。而不需要使用hash索引。

    数据库为什么使用B+树而不是B树

    • B树只适合随机检索,而B+树同时支持随机检索和顺序检索;
    • B+树空间利用率更高,可减少I/O次数,磁盘读写代价更低。一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗。B+树的内部结点并没有指向关键字具体信息的指针,只是作为索引使用,其内部结点比B树小,盘块能容纳的结点中关键字数量更多,一次性读入内存中可以查找的关键字也就越多,相对的,IO读写次数也就降低了。而IO读写次数是影响索引检索效率的最大因素;
    • B+树的查询效率更加稳定。B树搜索有可能会在非叶子结点结束,越靠近根节点的记录查找时间越短,只要找到关键字即可确定记录的存在,其性能等价于在关键字全集内做一次二分查找。而在B+树中,顺序检索比较明显,随机检索时,任何关键字的查找都必须走一条从根节点到叶节点的路,所有关键字的查找路径长度相同,导致每一个关键字的查询效率相当。
    • B-树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。B+树的叶子节点使用指针顺序连接在一起,只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作。
    • 增删文件(节点)时,效率更高。因为B+树的叶子节点包含所有关键字,并以有序的链表结构存储,这样可很好提高增删效率。

    B+树在满足聚簇索引和覆盖索引的时候不需要回表查询数据,

    在B+树的索引中,叶子节点可能存储了当前的key值,也可能存储了当前的key值以及整行的数据,这就是聚簇索引和非聚簇索引。 在InnoDB中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引。如果没有唯一键,则隐式的生成一个键来建立聚簇索引。

    当查询使用聚簇索引时,在对应的叶子节点,可以获取到整行数据,因此不用再次进行回表查询。

    什么是聚簇索引?何时使用聚簇索引与非聚簇索引

    • 聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据
    • 非聚簇索引:将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行,myisam通过key_buffer把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据,这也就是为什么索引不在key buffer命中时,速度慢的原因

    澄清一个概念:innodb中,在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值

    何时使用聚簇索引与非聚簇索引

    img

    非聚簇索引一定会回表查询吗?

    不一定,这涉及到查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必再进行回表查询。

    举个简单的例子,假设我们在员工表的年龄上建立了索引,那么当进行select age from employee where age < 20的查询时,在索引的叶子节点上,已经包含了age信息,不会再次进行回表查询。

    联合索引是什么?为什么需要注意联合索引中的顺序?

    MySQL可以使用多个字段同时建立一个索引,叫做联合索引。在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。

    具体原因为:

    MySQL使用索引时需要索引有序,假设现在建立了"name,age,school"的联合索引,那么索引的排序为: 先按照name排序,如果name相同,则按照age排序,如果age的值也相等,则按照school进行排序。

    当进行查询时,此时索引仅仅按照name严格有序,因此必须首先使用name字段进行等值查询,之后对于匹配到的列而言,其按照age字段严格有序,此时可以使用age字段用做索引查找,以此类推。因此在建立联合索引的时候应该注意索引列的顺序,一般情况下,将查询需求频繁或者字段选择性高的列放在前面。此外可以根据特例的查询或者表结构进行单独的调整。

    事务

    什么是数据库事务?

    事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。

    事务最经典也经常被拿出来说例子就是转账了。

    假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。

    事物的四大特性(ACID)介绍一下?

    关系性数据库需要遵循ACID规则,具体内容如下:

    事务的特性

    1. 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
    2. 一致性: 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
    3. 隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
    4. 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。

    什么是脏读?幻读?不可重复读?

    • 脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
    • 不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
    • 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

    什么是事务的隔离级别?MySQL的默认隔离级别是什么?

    为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。

    隔离级别 脏读 不可重复读 幻影读
    READ-UNCOMMITTED
    READ-COMMITTED ×
    REPEATABLE-READ × ×
    SERIALIZABLE × × ×

    SQL 标准定义了四个隔离级别:

    • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
    • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
    • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
    • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读

    这里需要注意的是:Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别

    事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。

    因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)**并不会有任何性能损失。

    InnoDB 存储引擎在 分布式事务 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。

    对MySQL的锁了解吗

    当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。

    就像酒店的房间,如果大家随意进出,就会出现多人抢夺同一个房间的情况,而在房间上装上锁,申请到钥匙的人才可以入住并且将房间锁起来,其他人只有等他使用完毕才可以再次使用。

    隔离级别与锁的关系

    在Read Uncommitted级别下,读取数据不需要加共享锁,这样就不会跟被修改的数据上的排他锁冲突

    在Read Committed级别下,读操作需要加共享锁,但是在语句执行完以后释放共享锁;

    在Repeatable Read级别下,读操作需要加共享锁,但是在事务提交之前并不释放共享锁,也就是必须等待事务执行完毕以后才释放共享锁。

    SERIALIZABLE 是限制性最强的隔离级别,因为该级别锁定整个范围的键,并一直持有锁,直到事务完成。

    按照锁的粒度分数据库锁有哪些?锁机制与InnoDB锁算法

    在关系型数据库中,可以按照锁的粒度把数据库锁分为行级锁(INNODB引擎)、表级锁(MYISAM引擎)和页级锁(BDB引擎 )。

    MyISAM和InnoDB存储引擎使用的锁:

    • MyISAM采用表级锁(table-level locking)。
    • InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁

    行级锁,表级锁和页级锁对比

    行级锁 行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁 和 排他锁。

    特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

    表级锁 表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。

    特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。

    页级锁 页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。

    特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

    从锁的类别上分MySQL都有哪些锁呢?像上面那样子进行锁定岂不是有点阻碍并发效率了

    从锁的类别上来讲,有共享锁和排他锁。

    共享锁: 又叫做读锁。 当用户要进行数据的读取时,对数据加上共享锁。共享锁可以同时加上多个。

    排他锁: 又叫做写锁。 当用户要进行数据的写入时,对数据加上排他锁。排他锁只可以加一个,他和其他的排他锁,共享锁都相斥。

    用上面的例子来说就是用户的行为有两种,一种是来看房,多个用户一起看房是可以接受的。 一种是真正的入住一晚,在这期间,无论是想入住的还是想看房的都不可以。

    锁的粒度取决于具体的存储引擎,InnoDB实现了行级锁,页级锁,表级锁。

    他们的加锁开销从大到小,并发能力也是从大到小。

    MySQL中InnoDB引擎的行锁是怎么实现的?

    答:InnoDB是基于索引来完成行锁

    例: select * from tab_with_index where id = 1 for update;

    for update 可以根据条件来完成行锁锁定,并且 id 是有索引键的列,如果 id 不是索引键那么InnoDB将完成表锁,并发将无从谈起

    InnoDB存储引擎的锁的算法有三种

    • Record lock:单个行记录上的锁
    • Gap lock:间隙锁,锁定一个范围,不包括记录本身
    • Next-key lock:record+gap 锁定一个范围,包含记录本身

    相关知识点:

    1. innodb对于行的查询使用next-key lock
    2. Next-locking keying为了解决Phantom Problem幻读问题
    3. 当查询的索引含有唯一属性时,将next-key lock降级为record key
    4. Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生
    5. 有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1

    什么是死锁?怎么解决?

    死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。

    常见的解决死锁的方法

    1、如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。

    2、在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;

    3、对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;

    如果业务处理不好可以用分布式事务锁或者使用乐观锁

    数据库的乐观锁和悲观锁是什么?怎么实现的?

    数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。

    悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。在查询完数据的时候就把事务锁起来,直到提交事务。实现方式:使用数据库中的锁机制

    乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。在修改数据的时候把事务锁起来,通过version的方式来进行锁定。实现方式:乐一般会使用版本号机制或CAS算法实现。

    两种锁的使用场景

    从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。

    但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

    视图

    为什么要使用视图?什么是视图?

    为了提高复杂SQL语句的复用性和表操作的安全性,MySQL数据库管理系统提供了视图特性。所谓视图,本质上是一种虚拟表,在物理上是不存在的,其内容与真实的表相似,包含一系列带有名称的列和行数据。但是,视图并不在数据库中以储存的数据值形式存在。行和列数据来自定义视图的查询所引用基本表,并且在具体引用视图时动态生成。

    视图使开发者只关心感兴趣的某些特定数据和所负责的特定任务,只能看到视图中所定义的数据,而不是视图所引用表中的数据,从而提高了数据库中数据的安全性。

    视图有哪些特点?

    视图的特点如下:

    • 视图的列可以来自不同的表,是表的抽象和在逻辑意义上建立的新关系。

    • 视图是由基本表(实表)产生的表(虚表)。

    • 视图的建立和删除不影响基本表。

    • 对视图内容的更新(添加,删除和修改)直接影响基本表。

    • 当视图来自多个基本表时,不允许添加和删除数据。

    视图的操作包括创建视图,查看视图,删除视图和修改视图。

    视图的使用场景有哪些?

    视图根本用途:简化sql查询,提高开发效率。如果说还有另外一个用途那就是兼容老的表结构。

    下面是视图的常见使用场景:

    • 重用SQL语句;

    • 简化复杂的SQL操作。在编写查询后,可以方便的重用它而不必知道它的基本查询细节;

    • 使用表的组成部分而不是整个表;

    • 保护数据。可以给用户授予表的特定部分的访问权限而不是整个表的访问权限;

    • 更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据。

    视图的优点

    1. 查询简单化。视图能简化用户的操作
    2. 数据安全性。视图使用户能以多种角度看待同一数据,能够对机密数据提供安全保护
    3. 逻辑数据独立性。视图对重构数据库提供了一定程度的逻辑独立性

    视图的缺点

    1. 性能。数据库必须把视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,数据库也把它变成一个复杂的结合体,需要花费一定的时间。

    2. 修改限制。当用户试图修改视图的某些行时,数据库必须把它转化为对基本表的某些行的修改。事实上,当从视图中插入或者删除时,情况也是这样。对于简单视图来说,这是很方便的,但是,对于比较复杂的视图,可能是不可修改的

      这些视图有如下特征:1.有UNIQUE等集合操作符的视图。2.有GROUP BY子句的视图。3.有诸如AVG\SUM\MAX等聚合函数的视图。 4.使用DISTINCT关键字的视图。5.连接表的视图(其中有些例外)

    什么是游标?

    游标是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果,每个游标区都有一个名字。用户可以通过游标逐一获取记录并赋给主变量,交由主语言进一步处理。

    存储过程与函数

    什么是存储过程?有哪些优缺点?

    存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需要创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。

    优点

    1)存储过程是预编译过的,执行效率高。

    2)存储过程的代码直接存放于数据库中,通过存储过程名直接调用,减少网络通讯。

    3)安全性高,执行存储过程需要有一定权限的用户。

    4)存储过程可以重复使用,减少数据库开发人员的工作量。

    缺点

    1)调试麻烦,但是用 PL/SQL Developer 调试很方便!弥补这个缺点。

    2)移植问题,数据库端代码当然是与数据库相关的。但是如果是做工程型项目,基本不存在移植问题。

    3)重新编译问题,因为后端代码是运行前编译的,如果带有引用关系的对象发生改变时,受影响的存储过程、包将需要重新编译(不过也可以设置成运行时刻自动编译)。

    4)如果在一个程序系统中大量的使用存储过程,到程序交付使用的时候随着用户需求的增加会导致数据结构的变化,接着就是系统的相关问题了,最后如果用户想维护该系统可以说是很难很难、而且代价是空前的,维护起来更麻烦。

    触发器

    什么是触发器?触发器的使用场景有哪些?

    触发器是用户定义在关系表上的一类由事件驱动的特殊的存储过程。触发器是指一段代码,当触发某个事件时,自动执行这些代码。

    使用场景

    • 可以通过数据库中的相关表实现级联更改。
    • 实时监控某张表中的某个字段的更改而需要做出相应的处理。
    • 例如可以生成某些业务的编号。
    • 注意不要滥用,否则会造成数据库及应用程序的维护困难。
    • 大家需要牢记以上基础知识点,重点是理解数据类型CHAR和VARCHAR的差异,表存储引擎InnoDB和MyISAM的区别。

    MySQL中都有哪些触发器?

    在MySQL数据库中有如下六种触发器:

    • Before Insert
    • After Insert
    • Before Update
    • After Update
    • Before Delete
    • After Delete

    常用SQL语句

    SQL语句主要分为哪几类

    数据定义语言DDL(Data Ddefinition Language)CREATE,DROP,ALTER

    主要为以上操作 即对逻辑结构等有操作的,其中包括表结构,视图和索引。

    数据查询语言DQL(Data Query Language)SELECT

    这个较为好理解 即查询操作,以select关键字。各种简单查询,连接查询等 都属于DQL。

    数据操纵语言DML(Data Manipulation Language)INSERT,UPDATE,DELETE

    主要为以上操作 即对数据进行操作的,对应上面所说的查询操作 DQL与DML共同构建了多数初级程序员常用的增删改查操作。而查询是较为特殊的一种 被划分到DQL中。

    数据控制功能DCL(Data Control Language)GRANT,REVOKE,COMMIT,ROLLBACK

    主要为以上操作 即对数据库安全性完整性等有操作的,可以简单的理解为权限控制等。

    超键、候选键、主键、外键分别是什么?

    • 超键:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以为作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键。
    • 候选键:是最小超键,即没有冗余元素的超键。
    • 主键:数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null)。
    • 外键:在一个表中存在的另一个表的主键称此表的外键。

    SQL 约束有哪几种?

    SQL 约束有哪几种?

    • NOT NULL: 用于控制字段的内容一定不能为空(NULL)。
    • UNIQUE: 控件字段内容不能重复,一个表允许有多个 Unique 约束。
    • PRIMARY KEY: 也是用于控件字段内容不能重复,但它在一个表只允许出现一个。
    • FOREIGN KEY: 用于预防破坏表之间连接的动作,也能防止非法数据插入外键列,因为它必须是它指向的那个表中的值之一。
    • CHECK: 用于控制字段的值范围。

    六种关联查询

    • 交叉连接(CROSS JOIN)
    • 内连接(INNER JOIN)
    • 外连接(LEFT JOIN/RIGHT JOIN)
    • 联合查询(UNION与UNION ALL)
    • 全连接(FULL JOIN)
    • 交叉连接(CROSS JOIN)
    SELECT * FROM A,B(,C)或者SELECT * FROM A CROSS JOIN B (CROSS JOIN C)#没有任何关联条件,结果是笛卡尔积,结果集会很大,没有意义,很少使用内连接(INNER JOIN)SELECT * FROM A,B WHERE A.id=B.id或者SELECT * FROM A INNER JOIN B ON A.id=B.id多表中同时符合某种条件的数据记录的集合,INNER JOIN可以缩写为JOIN
    

    内连接分为三类

    • 等值连接:ON A.id=B.id
    • 不等值连接:ON A.id > B.id
    • 自连接:SELECT * FROM A T1 INNER JOIN A T2 ON T1.id=T2.pid

    外连接(LEFT JOIN/RIGHT JOIN)

    • 左外连接:LEFT OUTER JOIN, 以左表为主,先查询出左表,按照ON后的关联条件匹配右表,没有匹配到的用NULL填充,可以简写成LEFT JOIN
    • 右外连接:RIGHT OUTER JOIN, 以右表为主,先查询出右表,按照ON后的关联条件匹配左表,没有匹配到的用NULL填充,可以简写成RIGHT JOIN

    联合查询(UNION与UNION ALL)

    SELECT * FROM A UNION SELECT * FROM B UNION ...
    
    • 就是把多个结果集集中在一起,UNION前的结果为基准,需要注意的是联合查询的列数要相等,相同的记录行会合并
    • 如果使用UNION ALL,不会合并重复的记录行
    • 效率 UNION 高于 UNION ALL

    全连接(FULL JOIN)

    • MySQL不支持全连接
    • 可以使用LEFT JOIN 和UNION和RIGHT JOIN联合使用
    SELECT * FROM A LEFT JOIN B ON A.id=B.id UNIONSELECT * FROM A RIGHT JOIN B ON A.id=B.id
    

    表连接面试题

    有2张表,1张R、1张S,R表有ABC三列,S表有CD两列,表中各有三条记录。

    R表

    A B C
    a1 b1 c1
    a2 b2 c2
    a3 b3 c3

    S表

    C D
    c1 d1
    c2 d2
    c4 d3
    1. 交叉连接(笛卡尔积):

    select r.*,s.* from r,s

    A B C C D
    a1 b1 c1 c1 d1
    a2 b2 c2 c1 d1
    a3 b3 c3 c1 d1
    a1 b1 c1 c2 d2
    a2 b2 c2 c2 d2
    a3 b3 c3 c2 d2
    a1 b1 c1 c4 d3
    a2 b2 c2 c4 d3
    a3 b3 c3 c4 d3
    1. 内连接结果:

      select r.*,s.* from r inner join s on r.c=s.c

    A B C C D
    a1 b1 c1 c1 d1
    a2 b2 c2 c2 d2
    1. 左连接结果:

      select r.*,s.* from r left join s on r.c=s.c

    A B C C D
    a1 b1 c1 c1 d1
    a2 b2 c2 c2 d2
    a3 b3 c3
    1. 右连接结果:

      select r.*,s.* from r right join s on r.c=s.c

    A B C C D
    a1 b1 c1 c1 d1
    a2 b2 c2 c2 d2
    c4 d3
    1. 全表连接的结果(MySql不支持,Oracle支持):

      select r.*,s.* from r full join s on r.c=s.c

    A B C C D
    a1 b1 c1 c1 d1
    a2 b2 c2 c2 d2
    a3 b3 c3
    c4 d3

    什么是子查询

    1. 条件:一条SQL语句的查询结果做为另一条查询语句的条件或查询结果

    2. 嵌套:多条SQL语句嵌套使用,内部的SQL查询语句称为子查询。

    子查询的三种情况

    1. 子查询是单行单列的情况:结果集是一个值,父查询使用:=、 <、 > 等运算符
    -- 查询工资最高的员工是谁? 
    select  * from employee where salary=(select max(salary) from employee);   
    
    1. 子查询是多行单列的情况:结果集类似于一个数组,父查询使用:in 运算符
    -- 查询工资最高的员工是谁? 
    select  * from employee where salary=(select max(salary) from employee);    
    
    1. 子查询是多行多列的情况:结果集类似于一张虚拟表,不能用于where条件,用于select子句中做为子表
    -- 1) 查询出2011年以后入职的员工信息
    -- 2) 查询所有的部门信息,与上面的虚拟表中的信息比对,找出所有部门ID相等的员工。
    select * from dept d,  (select * from employee where join_date > '2011-1-1') e where e.dept_id =  d.id;    
    
    -- 使用表连接:
    select d.*, e.* from  dept d inner join employee e on d.id = e.dept_id where e.join_date >  '2011-1-1'  
    

    mysql中 in 和 exists 区别

    mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的。这个是要区分环境的。

    1. 如果查询的两个表大小相当,那么用in和exists差别不大。
    2. 如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in。
    3. not in 和not exists:如果查询语句使用了not in,那么内外表都进行全表扫描,没有用到索引;而not extsts的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。

    varchar与char的区别

    char的特点

    • char表示定长字符串,长度是固定的;

    • 如果插入数据的长度小于char的固定长度时,则用空格填充;

    • 因为长度固定,所以存取速度要比varchar快很多,甚至能快50%,但正因为其长度固定,所以会占据多余的空间,是空间换时间的做法;

    • 对于char来说,最多能存放的字符个数为255,和编码无关

    varchar的特点

    • varchar表示可变长字符串,长度是可变的;

    • 插入的数据是多长,就按照多长来存储;

    • varchar在存取方面与char相反,它存取慢,因为长度不固定,但正因如此,不占据多余的空间,是时间换空间的做法;

    • 对于varchar来说,最多能存放的字符个数为65532

    总之,结合性能角度(char更快)和节省磁盘空间角度(varchar更小),具体情况还需具体来设计数据库才是妥当的做法。

    varchar(50)中50的涵义

    最多存放50个字符,varchar(50)和(200)存储hello所占空间一样,但后者在排序时会消耗更多内存,因为order by col采用fixed_length计算col长度(memory引擎也一样)。在早期 MySQL 版本中, 50 代表字节数,现在代表字符数。

    int(20)中20的涵义

    是指显示字符的长度。20表示最大显示宽度为20,但仍占4字节存储,存储范围不变;

    不影响内部存储,只是影响带 zerofill 定义的 int 时,前面补多少个 0,易于报表展示

    mysql为什么这么设计

    对大多数应用没有意义,只是规定一些工具用来显示字符的个数;int(1)和int(20)存储和计算均一样;

    mysql中int(10)和char(10)以及varchar(10)的区别

    • int(10)的10表示显示的数据的长度,不是存储数据的大小;chart(10)和varchar(10)的10表示存储数据的大小,即表示存储多少个字符。

      int(10) 10位的数据长度 9999999999,占32个字节,int型4位
      char(10) 10位固定字符串,不足补空格 最多10个字符
      varchar(10) 10位可变字符串,不足补空格 最多10个字符

    • char(10)表示存储定长的10个字符,不足10个就用空格补齐,占用更多的存储空间

    • varchar(10)表示存储10个变长的字符,存储多少个就是多少个,空格也按一个字符存储,这一点是和char(10)的空格不同的,char(10)的空格表示占位不算一个字符

    FLOAT和DOUBLE的区别是什么?

    • FLOAT类型数据可以存储至多8位十进制数,并在内存中占4字节。
    • DOUBLE类型数据可以存储至多18位十进制数,并在内存中占8字节。

    drop、delete与truncate的区别

    三者都表示删除,但是三者有一些差别:

    Delete Truncate Drop
    类型 属于DML 属于DDL 属于DDL
    回滚 可回滚 不可回滚 不可回滚
    删除内容 表结构还在,删除表的全部或者一部分数据行 表结构还在,删除表中的所有数据 从数据库中删除表,所有的数据行,索引和权限也会被删除
    删除速度 删除速度慢,需要逐行删除 删除速度快 删除速度最快

    因此,在不再需要一张表的时候,用drop;在想删除部分数据行时候,用delete;在保留表而删除所有数据的时候用truncate。

    UNION与UNION ALL的区别?

    • 如果使用UNION ALL,不会合并重复的记录行
    • 效率 UNION 高于 UNION ALL

    SQL优化

    如何定位及优化SQL语句的性能问题?创建的索引有没有被使用到?或者说怎么才可以知道这条语句运行很慢的原因?

    对于低性能的SQL语句的定位,最重要也是最有效的方法就是使用执行计划,MySQL提供了explain命令来查看语句的执行计划。 我们知道,不管是哪种数据库,或者是哪种数据库引擎,在对一条SQL语句进行执行的过程中都会做很多相关的优化,对于查询语句,最重要的优化方式就是使用索引。 而执行计划,就是显示数据库引擎对于SQL语句的执行的详细情况,其中包含了是否使用索引,使用什么索引,使用的索引的相关信息等

    在这里插入图片描述

    执行计划包含的信息 id 有一组数字组成。表示一个查询中各个子查询的执行顺序;

    • id相同执行顺序由上至下。
    • id不同,id值越大优先级越高,越先被执行。
    • id为null时表示一个结果集,不需要使用它查询,常出现在包含union等查询语句中。

    select_type 每个子查询的查询类型,一些常见的查询类型。

    id select_type description
    1 SIMPLE 不包含任何子查询或union等查询
    2 PRIMARY 包含子查询最外层查询就显示为 PRIMARY
    3 SUBQUERY 在select或 where字句中包含的查询
    4 DERIVED from字句中包含的查询
    5 UNION 出现在union后的查询语句中
    6 UNION RESULT 从UNION中获取结果集,例如上文的第三个例子

    table 查询的数据表,当从衍生表中查数据时会显示 x 表示对应的执行计划id partitions 表分区、表创建的时候可以指定通过那个列进行表分区。 举个例子:

    create table tmp (
        id int unsigned not null AUTO_INCREMENT,
        name varchar(255),
        PRIMARY KEY (id)
    ) engine = innodb
    partition by key (id) partitions 5;
    

    type(非常重要,可以看到有没有走索引) 访问类型

    • ALL 扫描全表数据
    • index 遍历索引
    • range 索引范围查找
    • index_subquery 在子查询中使用 ref
    • unique_subquery 在子查询中使用 eq_ref
    • ref_or_null 对Null进行索引的优化的 ref
    • fulltext 使用全文索引
    • ref 使用非唯一索引查找数据
    • eq_ref 在join查询中使用PRIMARY KEYorUNIQUE NOT NULL索引关联。

    possible_keys 可能使用的索引,注意不一定会使用。查询涉及到的字段上若存在索引,则该索引将被列出来。当该列为 NULL时就要考虑当前的SQL是否需要优化了。

    key 显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。

    TIPS:查询中若使用了覆盖索引(覆盖索引:索引的数据覆盖了需要查询的所有数据),则该索引仅出现在key列表中

    key_length 索引长度

    ref 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

    rows 返回估算的结果集数目,并不是一个准确的值。

    extra 的信息非常丰富,常见的有:

    1. Using index 使用覆盖索引
    2. Using where 使用了用where子句来过滤结果集
    3. Using filesort 使用文件排序,使用非索引列进行排序时出现,非常消耗性能,尽量优化。
    4. Using temporary 使用了临时表 sql优化的目标可以参考阿里开发手册
    【推荐】SQL性能优化的目标:至少要达到 range 级别,要求是ref级别,如果可以是consts最好。 
    说明: 
    1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。 
    2) ref 指的是使用普通的索引(normal index)。 
    3) range 对索引进行范围检索。 
    反例:explain表的结果,type=index,索引物理文件全扫描,速度非常慢,这个index级别比较range还低,与全表扫描是小巫见大巫。
    

    SQL的生命周期?

    1. 应用服务器与数据库服务器建立一个连接

    2. 数据库进程拿到请求sql

    3. 解析并生成执行计划,执行

    4. 读取数据到内存并进行逻辑处理

    5. 通过步骤一的连接,发送结果到客户端

    6. 关掉连接,释放资源

      在这里插入图片描述

    大表数据查询,怎么优化

    1. 优化shema、sql语句+索引;
    2. 第二加缓存,memcached, redis;
    3. 主从复制,读写分离;
    4. 垂直拆分,根据你模块的耦合度,将一个大的系统分为多个小的系统,也就是分布式系统;
    5. 水平切分,针对数据量大的表,这一步最麻烦,最能考验技术水平,要选择一个合理的sharding key, 为了有好的查询效率,表结构也要改动,做一定的冗余,应用也要改,sql中尽量带sharding key,将数据定位到限定的表上去查,而不是扫描全部的表;

    超大分页怎么处理?

    超大的分页一般从两个方向上来解决.

    • 数据库层面,这也是我们主要集中关注的(虽然收效没那么大),类似于select * from table where age > 20 limit 1000000,10这种查询其实也是有可以优化的余地的. 这条语句需要load1000000数据然后基本上全部丢弃,只取10条当然比较慢. 当时我们可以修改为select * from table where id in (select id from table where age > 20 limit 1000000,10).这样虽然也load了一百万的数据,但是由于索引覆盖,要查询的所有字段都在索引中,所以速度会很快. 同时如果ID连续的好,我们还可以select * from table where id > 1000000 limit 10,效率也是不错的,优化的可能性有许多种,但是核心思想都一样,就是减少load的数据.
    • 从需求的角度减少这种请求…主要是不做类似的需求(直接跳转到几百万页之后的具体某一页.只允许逐页查看或者按照给定的路线走,这样可预测,可缓存)以及防止ID泄漏且连续被人恶意攻击.

    解决超大分页,其实主要是靠缓存,可预测性的提前查到内容,缓存至redis等k-V数据库中,直接返回即可.

    在阿里巴巴《Java开发手册》中,对超大分页的解决办法是类似于上面提到的第一种.

    【推荐】利用延迟关联或者子查询优化超多分页场景。 
    
    说明:MySQL并不是跳过offset行,而是取offset+N行,然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。 
    
    正例:先快速定位需要获取的id段,然后再关联: 
    
    SELECT a.* FROM1 a, (select id from1 where 条件 LIMIT 100000,20 ) b where a.id=b.id
    

    mysql 分页

    LIMIT 子句可以被用于强制 SELECT 语句返回指定的记录数。LIMIT 接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的偏移量是 0(而不是 1)

    mysql> SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15 
    

    为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:

    mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last. 
    

    如果只给定一个参数,它表示返回最大的记录行数目:

    mysql> SELECT * FROM table LIMIT 5; //检索前 5 个记录行 
    

    换句话说,LIMIT n 等价于 LIMIT 0,n。

    慢查询日志

    用于记录执行时间超过某个临界值的SQL日志,用于快速定位慢查询,为我们的优化做参考。

    开启慢查询日志

    配置项:slow_query_log

    可以使用show variables like ‘slov_query_log’查看是否开启,如果状态值为OFF,可以使用set GLOBAL slow_query_log = on来开启,它会在datadir下产生一个xxx-slow.log的文件。

    设置临界时间

    配置项:long_query_time

    查看:show VARIABLES like 'long_query_time',单位秒

    设置:set long_query_time=0.5

    实操时应该从长时间设置到短的时间,即将最慢的SQL优化掉

    查看日志,一旦SQL超过了我们设置的临界时间就会被记录到xxx-slow.log

    关心过业务系统里面的sql耗时吗?统计过慢查询吗?对慢查询都怎么优化过?

    在业务系统中,除了使用主键进行的查询,其他的我都会在测试库上测试其耗时,慢查询的统计主要由运维在做,会定期将业务中的慢查询反馈给我们。

    慢查询的优化首先要搞明白慢的原因是什么? 是查询条件没有命中索引?是load了不需要的数据列?还是数据量太大?

    所以优化也是针对这三个方向来的,

    • 首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写。
    • 分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引。
    • 如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。

    为什么要尽量设定一个主键?

    主键是数据库确保数据行在整张表唯一性的保障,即使业务上本张表没有主键,也建议添加一个自增长的ID列作为主键。设定了主键之后,在后续的删改查的时候可能更加快速以及确保操作数据范围安全。

    主键使用自增ID还是UUID?

    推荐使用自增ID,不要使用UUID。

    因为在InnoDB存储引擎中,主键索引是作为聚簇索引存在的,也就是说,主键索引的B+树叶子节点上存储了主键索引以及全部的数据(按照顺序),如果主键索引是自增ID,那么只需要不断向后排列即可,如果是UUID,由于到来的ID与原来的大小不确定,会造成非常多的数据插入,数据移动,然后导致产生很多的内存碎片,进而造成插入性能的下降。

    总之,在数据量大一些的情况下,用自增主键性能会好一些。

    关于主键是聚簇索引,如果没有主键,InnoDB会选择一个唯一键来作为聚簇索引,如果没有唯一键,会生成一个隐式的主键。

    字段为什么要求定义为not null?

    null值会占用更多的字节,且会在程序中造成很多与预期不符的情况。

    如果要存储用户的密码散列,应该使用什么字段进行存储?

    密码散列,盐,用户身份证号等固定长度的字符串应该使用char而不是varchar来存储,这样可以节省空间且提高检索效率。

    优化查询过程中的数据访问

    • 访问数据太多导致查询性能下降
    • 确定应用程序是否在检索大量超过需要的数据,可能是太多行或列
    • 确认MySQL服务器是否在分析大量不必要的数据行
    • 避免犯如下SQL语句错误
    • 查询不需要的数据。解决办法:使用limit解决
    • 多表关联返回全部列。解决办法:指定列名
    • 总是返回全部列。解决办法:避免使用SELECT *
    • 重复查询相同的数据。解决办法:可以缓存数据,下次直接读取缓存
    • 是否在扫描额外的记录。解决办法:
    • 使用explain进行分析,如果发现查询需要扫描大量的数据,但只返回少数的行,可以通过如下技巧去优化:
    • 使用索引覆盖扫描,把所有的列都放到索引中,这样存储引擎不需要回表获取对应行就可以返回结果。
    • 改变数据库和表的结构,修改数据表范式
    • 重写SQL语句,让优化器可以以更优的方式执行查询。

    优化长难的查询语句

    • 一个复杂查询还是多个简单查询
    • MySQL内部每秒能扫描内存中上百万行数据,相比之下,响应数据给客户端就要慢得多
    • 使用尽可能小的查询是好的,但是有时将一个大的查询分解为多个小的查询是很有必要的。
    • 切分查询
    • 将一个大的查询分为多个小的相同的查询
    • 一次性删除1000万的数据要比一次删除1万,暂停一会的方案更加损耗服务器开销。
    • 分解关联查询,让缓存的效率更高。
    • 执行单个查询可以减少锁的竞争。
    • 在应用层做关联更容易对数据库进行拆分。
    • 查询效率会有大幅提升。
    • 较少冗余记录的查询。

    优化特定类型的查询语句

    • count(*)会忽略所有的列,直接统计所有列数,不要使用count(列名)
    • MyISAM中,没有任何where条件的count(*)非常快。
    • 当有where条件时,MyISAM的count统计不一定比其它引擎快。
    • 可以使用explain查询近似值,用近似值替代count(*)
    • 增加汇总表
    • 使用缓存

    优化关联查询

    • 确定ON或者USING子句中是否有索引。
    • 确保GROUP BY和ORDER BY只有一个表中的列,这样MySQL才有可能使用索引。

    优化子查询

    • 用关联查询替代
    • 优化GROUP BY和DISTINCT
    • 这两种查询据可以使用索引来优化,是最有效的优化方法
    • 关联查询中,使用标识列分组的效率更高
    • 如果不需要ORDER BY,进行GROUP BY时加ORDER BY NULL,MySQL不会再进行文件排序。
    • WITH ROLLUP超级聚合,可以挪到应用程序处理

    优化LIMIT分页

    • LIMIT偏移量大的时候,查询效率较低
    • 可以记录上次查询的最大ID,下次查询时直接根据该ID来查询

    优化UNION查询

    • UNION ALL的效率高于UNION

    优化WHERE子句

    解题方法

    对于此类考题,先说明如何定位低效SQL语句,然后根据SQL语句可能低效的原因做排查,先从索引着手,如果索引没有问题,考虑以上几个方面,数据访问的问题,长难查询句的问题还是一些特定类型优化的问题,逐一回答。

    SQL语句优化的一些方法?

    • 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
    • 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
    select id from t where num is null
    -- 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
    select id from t where num=
    
    • 3.应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。
    • 4.应尽量避免在 where 子句中使用or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
    select id from t where num=10 or num=20
    -- 可以这样查询:
    select id from t where num=10 union all select id from t where num=20
    
    • 5.in 和 not in 也要慎用,否则会导致全表扫描,如:
    select id from t where num in(1,2,3) 
    -- 对于连续的数值,能用 between 就不要用 in 了:
    select id from t where num between 1 and 3
    
    • 6.下面的查询也将导致全表扫描:select id from t where name like ‘%李%’若要提高效率,可以考虑全文检索。
    • 7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
    select id from t where num=@num
    -- 可以改为强制查询使用索引:
    select id from t with(index(索引名)) where num=@num
    
    • 8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
    select id from t where num/2=100
    -- 应改为:
    select id from t where num=100*2
    
    • 9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
    select id from t where substring(name,1,3)=’abc’
    -- name以abc开头的id应改为:
    select id from t where name like ‘abc%
    • 10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

    数据库优化

    为什么要优化

    • 系统的吞吐量瓶颈往往出现在数据库的访问速度上
    • 随着应用程序的运行,数据库的中的数据会越来越多,处理时间会相应变慢
    • 数据是存放在磁盘上的,读写速度无法和内存相比

    优化原则:减少系统瓶颈,减少资源占用,增加系统的反应速度。

    数据库结构优化

    一个好的数据库设计方案对于数据库的性能往往会起到事半功倍的效果。

    需要考虑数据冗余、查询和更新的速度、字段的数据类型是否合理等多方面的内容。

    将字段很多的表分解成多个表

    对于字段较多的表,如果有些字段的使用频率很低,可以将这些字段分离出来形成新表。

    因为当一个表的数据量很大时,会由于使用频率低的字段的存在而变慢。

    增加中间表

    对于需要经常联合查询的表,可以建立中间表以提高查询效率。

    通过建立中间表,将需要通过联合查询的数据插入到中间表中,然后将原来的联合查询改为对中间表的查询。

    增加冗余字段

    设计数据表时应尽量遵循范式理论的规约,尽可能的减少冗余字段,让数据库设计看起来精致、优雅。但是,合理的加入冗余字段可以提高查询速度。

    表的规范化程度越高,表和表之间的关系越多,需要连接查询的情况也就越多,性能也就越差。

    注意:

    冗余字段的值在一个表中修改了,就要想办法在其他表中更新,否则就会导致数据不一致的问题。

    MySQL数据库cpu飙升到500%的话他怎么处理?

    当 cpu 飙升到 500%时,先用操作系统命令 top 命令观察是不是 mysqld 占用导致的,如果不是,找出占用高的进程,并进行相关处理。

    如果是 mysqld 造成的, show processlist,看看里面跑的 session 情况,是不是有消耗资源的 sql 在运行。找出消耗高的 sql,看看执行计划是否准确, index 是否缺失,或者实在是数据量太大造成。

    一般来说,肯定要 kill 掉这些线程(同时观察 cpu 使用率是否下降),等进行相应的调整(比如说加索引、改 sql、改内存参数)之后,再重新跑这些 SQL。

    也有可能是每个 sql 消耗资源并不多,但是突然之间,有大量的 session 连进来导致 cpu 飙升,这种情况就需要跟应用一起来分析为何连接数会激增,再做出相应的调整,比如说限制连接数等

    大表怎么优化?某个表有近千万数据,CRUD比较慢,如何优化?分库分表了是怎么做的?分表分库了有什么问题?有用到中间件么?他们的原理知道么?

    当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:

    1. 限定数据的范围: 务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内。;
    2. 读/写分离: 经典的数据库拆分方案,主库负责写,从库负责读;
    3. 缓存: 使用MySQL的缓存,另外对重量级、更新少的数据可以考虑使用应用级别的缓存;

    还有就是通过分库分表的方式进行优化,主要有垂直分表和水平分表

    1. 垂直分区:

      根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。

      简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。 如下图所示,这样来说大家应该就更容易理解了。

      img

      垂直拆分的优点: 可以使得行数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。

      垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂;

      垂直分表

      把主键和一些列放在一个表,然后把主键和另外的列放在另一个表中

      img

      适用场景
      • 1、如果一个表中某些列常用,另外一些列不常用
      • 2、可以使数据行变小,一个数据页能存储更多数据,查询时减少I/O次数
      缺点
      • 有些分表的策略基于应用层的逻辑算法,一旦逻辑算法改变,整个分表逻辑都会改变,扩展性较差
      • 对于应用层来说,逻辑算法增加开发成本
      • 管理冗余列,查询所有数据需要join操作
    2. 水平分区:

      保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。

      水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。

      数据库水平拆分

      水品拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以 水平拆分最好分库

      水平拆分能够 支持非常大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨界点Join性能较差,逻辑复杂。

      《Java工程师修炼之道》的作者推荐 尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度 ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。

      水平分表:

      表很大,分割后可以降低在查询时需要读的数据和索引的页数,同时也降低了索引的层数,提高查询次数

      img

      适用场景
      • 1、表中的数据本身就有独立性,例如表中分表记录各个地区的数据或者不同时期的数据,特别是有些数据常用,有些不常用。
      • 2、需要把数据存放在多个介质上。
      水平切分的缺点
      • 1、给应用增加复杂度,通常查询时需要多个表名,查询所有数据都需UNION操作
      • 2、在许多数据库应用中,这种复杂度会超过它带来的优点,查询时会增加读一个索引层的磁盘次数

      下面补充一下数据库分片的两种常见方案:

      • 客户端代理: 分片逻辑在应用端,封装在jar包中,通过修改或者封装JDBC层来实现。 当当网的 Sharding-JDBC 、阿里的TDDL是两种比较常用的实现。
      • 中间件代理: 在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。 我们现在谈的 Mycat 、360的Atlas、网易的DDB等等都是这种架构的实现。

    分库分表后面临的问题

    • 事务支持 分库分表后,就成了分布式事务了。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价; 如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。

    • 跨库join

      只要是进行切分,跨节点Join的问题是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据。 分库分表方案产品

    • 跨节点的count,order by,group by以及聚合函数问题 这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。解决方案:与解决跨节点join问题的类似,分别在各个节点上得到结果后在应用程序端进行合并。和join不同的是每个结点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。

    • 数据迁移,容量规划,扩容等问题 来自淘宝综合业务平台团队,它利用对2的倍数取余具有向前兼容的特性(如对4取余得1的数对2取余也是1)来分配数据,避免了行级别的数据迁移,但是依然需要进行表级别的迁移,同时对扩容规模和分表数量都有限制。总得来说,这些方案都不是十分的理想,多多少少都存在一些缺点,这也从一个侧面反映出了Sharding扩容的难度。

    • ID问题

    • 一旦数据库被切分到多个物理结点上,我们将不能再依赖数据库自身的主键生成机制。一方面,某个分区数据库自生成的ID无法保证在全局上是唯一的;另一方面,应用程序在插入数据之前需要先获得ID,以便进行SQL路由. 一些常见的主键生成策略

    UUID 使用UUID作主键是最简单的方案,但是缺点也是非常明显的。由于UUID非常的长,除占用大量存储空间外,最主要的问题是在索引上,在建立索引和基于索引进行查询时都存在性能问题。 Twitter的分布式自增ID算法Snowflake 在分布式系统中,需要生成全局UID的场合还是比较多的,twitter的snowflake解决了这种需求,实现也还是很简单的,除去配置信息,核心代码就是毫秒级时间41位 机器ID 10位 毫秒内序列12位。

    • 跨分片的排序分页

      般来讲,分页时需要按照指定字段进行排序。当排序字段就是分片字段的时候,我们通过分片规则可以比较容易定位到指定的分片,而当排序字段非分片字段的时候,情况就会变得比较复杂了。为了最终结果的准确性,我们需要在不同的分片节点中将数据进行排序并返回,并将不同分片返回的结果集进行汇总和再次排序,最后再返回给用户。如下图所示:

      在这里插入图片描述

    MySQL的复制原理以及流程

    主从复制:将主数据库中的DDL和DML操作通过二进制日志(BINLOG)传输到从数据库上,然后将这些日志重新执行(重做);从而使得从数据库的数据与主数据库保持一致。

    主从复制的作用

    1. 主数据库出现问题,可以切换到从数据库。
    2. 可以进行数据库层面的读写分离。
    3. 可以在从数据库上进行日常备份。

    MySQL主从复制解决的问题

    • 数据分布:随意开始或停止复制,并在不同地理位置分布数据备份
    • 负载均衡:降低单个服务器的压力
    • 高可用和故障切换:帮助应用程序避免单点失败
    • 升级测试:可以用更高版本的MySQL作为从库

    MySQL主从复制工作原理

    • 在主库上把数据更高记录到二进制日志
    • 从库将主库的日志复制到自己的中继日志
    • 从库读取中继日志的事件,将其重放到从库数据中

    基本原理流程,3个线程以及之间的关联

    :binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中;

    :io线程——在使用start slave 之后,负责从master上拉取 binlog 内容,放进自己的relay log中;

    :sql执行线程——执行relay log中的语句;

    复制过程

    img

    Binary log:主数据库的二进制日志

    Relay log:从服务器的中继日志

    第一步:master在每个事务更新数据完成之前,将该操作记录串行地写入到binlog文件中。

    第二步:salve开启一个I/O Thread,该线程在master打开一个普通连接,主要工作是binlog dump process。如果读取的进度已经跟上了master,就进入睡眠状态并等待master产生新的事件。I/O线程最终的目的是将这些事件写入到中继日志中。

    第三步:SQL Thread会读取中继日志,并顺序执行该日志中的SQL事件,从而与主数据库中的数据保持一致。

    读写分离有哪些解决方案?

    读写分离是依赖于主从复制,而主从复制又是为读写分离服务的。因为主从复制要求slave不能写只能读(如果对slave执行写操作,那么show slave status将会呈现Slave_SQL_Running=NO,此时你需要按照前面提到的手动同步一下slave)。

    方案一

    使用mysql-proxy代理

    优点:直接实现读写分离和负载均衡,不用修改代码,master和slave用一样的帐号,mysql官方不建议实际生产中使用

    缺点:降低性能, 不支持事务

    方案二

    使用AbstractRoutingDataSource+aop+annotation在dao层决定数据源。
    如果采用了mybatis, 可以将读写分离放在ORM层,比如mybatis可以通过mybatis plugin拦截sql语句,所有的insert/update/delete都访问master库,所有的select 都访问salve库,这样对于dao层都是透明。 plugin实现时可以通过注解或者分析语句是读写方法来选定主从库。不过这样依然有一个问题, 也就是不支持事务, 所以我们还需要重写一下DataSourceTransactionManager, 将read-only的事务扔进读库, 其余的有读有写的扔进写库。

    方案三

    使用AbstractRoutingDataSource+aop+annotation在service层决定数据源,可以支持事务.

    缺点:类内部方法通过this.xx()方式相互调用时,aop不会进行拦截,需进行特殊处理。

    备份计划,mysqldump以及xtranbackup的实现原理

    (1)备份计划

    视库的大小来定,一般来说 100G 内的库,可以考虑使用 mysqldump 来做,因为 mysqldump更加轻巧灵活,备份时间选在业务低峰期,可以每天进行都进行全量备份(mysqldump 备份出来的文件比较小,压缩之后更小)。

    100G 以上的库,可以考虑用 xtranbackup 来做,备份速度明显要比 mysqldump 要快。一般是选择一周一个全备,其余每天进行增量备份,备份时间为业务低峰期。

    (2)备份恢复时间

    物理备份恢复快,逻辑备份恢复慢

    这里跟机器,尤其是硬盘的速率有关系,以下列举几个仅供参考

    20G的2分钟(mysqldump)

    80G的30分钟(mysqldump)

    111G的30分钟(mysqldump)

    288G的3小时(xtra)

    3T的4小时(xtra)

    逻辑导入时间一般是备份时间的5倍以上

    (3)备份恢复失败如何处理

    首先在恢复之前就应该做足准备工作,避免恢复的时候出错。比如说备份之后的有效性检查、权限检查、空间检查等。如果万一报错,再根据报错的提示来进行相应的调整。

    (4)mysqldump和xtrabackup实现原理

    mysqldump

    mysqldump 属于逻辑备份。加入–single-transaction 选项可以进行一致性备份。后台进程会先设置 session 的事务隔离级别为 RR(SET SESSION TRANSACTION ISOLATION LEVELREPEATABLE READ),之后显式开启一个事务(START TRANSACTION /*!40100 WITH CONSISTENTSNAPSHOT */),这样就保证了该事务里读到的数据都是事务事务时候的快照。之后再把表的数据读取出来。如果加上–master-data=1 的话,在刚开始的时候还会加一个数据库的读锁(FLUSH TABLES WITH READ LOCK),等开启事务后,再记录下数据库此时 binlog 的位置(showmaster status),马上解锁,再读取表的数据。等所有的数据都已经导完,就可以结束事务

    Xtrabackup:

    xtrabackup 属于物理备份,直接拷贝表空间文件,同时不断扫描产生的 redo 日志并保存下来。最后完成 innodb 的备份后,会做一个 flush engine logs 的操作(老版本在有 bug,在5.6 上不做此操作会丢数据),确保所有的 redo log 都已经落盘(涉及到事务的两阶段提交

    概念,因为 xtrabackup 并不拷贝 binlog,所以必须保证所有的 redo log 都落盘,否则可能会丢最后一组提交事务的数据)。这个时间点就是 innodb 完成备份的时间点,数据文件虽然不是一致性的,但是有这段时间的 redo 就可以让数据文件达到一致性(恢复的时候做的事

    情)。然后还需要 flush tables with read lock,把 myisam 等其他引擎的表给备份出来,备份完后解锁。这样就做到了完美的热备。

    数据表损坏的修复方式有哪些?

    使用 myisamchk 来修复,具体步骤:

    • 1)修复前将mysql服务停止。
    • 2)打开命令行方式,然后进入到mysql的/bin目录。
    • 3)执行myisamchk –recover 数据库所在路径/*.MYI

    使用repair table 或者 OPTIMIZE table命令来修复,REPAIR TABLE table_name 修复表 OPTIMIZE TABLE table_name 优化表 REPAIR TABLE 用于修复被破坏的表。 OPTIMIZE TABLE 用于回收闲置的数据库空间,当表上的数据行被删除时,所占据的磁盘空间并没有立即被回收,使用了OPTIMIZE TABLE命令后这些空间将被回收,并且对磁盘上的数据行进行重排(注意:是磁盘上,而非数据库)

    展开全文
  • Mysql 技术内幕(一)

    2016-06-11 11:36:08
    MySQL技术内幕系列博客,是本人在阅读《MySQL技术内幕 InnoDB》存储引擎所做的整理总结。本节内容来源于书中前两章。MySQL 体系结构MySQL结构模块图Innodb 存储引擎Innodb 体系结构图Innodb 内存池checkPointInnodb ...
  • 网站 ...> CiCi岛 下载 电子版仅供预览及学习交流使用,下载后请24小时内删除,支持正版,喜欢的请购买正版书籍 ...购买正版 ...从公共可用性的意义上讲,...MySQL开发团队的前成/员Sasha Pachev通过本书给出了MySQL ...
  • 知识图谱关键技术与应用案例

    万次阅读 多人点赞 2018-11-06 11:50:18
    在知识图谱关键技术概念与工具的实践应用中,本课程也会讲解知识图谱的构建经验;以及达观在各行业领域系统中的产品开发和系统应用。 报名地址: https://edu.csdn.net/huiyiCourse/detail/844 作者简介:桂洪冠,...
  • MySQL事务日志

    万次阅读 2021-03-15 17:51:49
    事务是MySQL区别于NoSQL的重要特征,是保证关系型数据库数据一致性的关键技术。事务可看作是对数据库操作的基本执行单元,可能包含一个或者多个SQL语句。这些语句在执行时,要么都执行,要么都不执行。 事务的执行...
  • MySQL引擎

    千次阅读 2011-07-08 13:53:45
    一般来说,MySQL有以下几种引擎:ISAM、MyISAM、HEAP、InnoDB和Berkley(BDB)。...当然啦,如果你感觉自己的确技术高超,你还能够使用MySQL++来创建自己的数据库引擎,这个已经out of my knowledge
  • 第一章 MySQL架构与历史 MySQL的架构 从上图可以看出,MySQL数据库区别于其他数据库的最重要的一个特点就是其插件式的表存储引擎。需要注意的是,存储引擎是基于表的,而不是数据库的(即同一个数据库中的不同表...
  • 01Mysql 基础 02SQL语言 03JDBC 04DBUtils 1.Mysql的安装和使用 1.SQL语句的三种类型 1.JDBC概述 1.使用QueryRunner 2.图解Mysql程序结构 2.DML、DDL、DCL 2.获取数据库连接 2.可插拔式结果集处理 3.Mysql...
  • MySQL 面试题

    万次阅读 多人点赞 2019-09-02 16:03:33
    MySQL 面试题 MySQL 涉及的内容非常非常非常多,所以面试题也容易写的杂乱。当年,我们记着几个一定要掌握的重心: 重点的题目添加了【重点】前缀。 索引。 锁。 事务和隔离级别。 因为 MySQL 还会有部分内容和...
  • MySQL技术内幕:InnoDB存储引擎

    千次阅读 2010-11-23 22:47:00
    在这个过程中积累了一些经验,正是这些不断累积的经验赋予了我灵感,于是有了《MySQL技术内幕:InnoDB存储引擎》。这本书实际上反映了这些年来我做了哪些事情,汇集了很多同行每天可能都会遇到的一些问题,并给出了...
  • 网易云 MySQL实例迁移的技术实现

    千次阅读 2018-11-27 11:31:07
    欢迎访问网易云社区,了解更多网易技术产品运营经验。 我们把数据库里部分或全部 Schema和数据迁移到另一个实例的行为称为实例迁移,将导出数据的实例称为源实例,导入数据的实例称为目标实例。 根据迁移数据库...
  • InnoDB 存储引擎最早由 Innobase Oy 公司旰发,被包括在 MySQL 数据库所有的二进制发行版本中,从 MySQL 5.5 版本开始是默认的表存储引擎(之前的版本 InnoDB 存储引擎仅在 Windows 下为默认的存储引擎)。...
  • 分布式关键技术之全栈监控

    千次阅读 2018-09-12 20:49:11
    最近又看了一遍左耳朵耗子对分布式系统的阐述,结合最近做的项目,感觉理解又深入了一层,特总结了接下来几篇关于分布式关键技术的介绍。 2. 何谓全栈监控 在分布式系统下,系统一般分为多层,服务各种关联,需要...
  • MySQL详解(九):MySQL Cluster

    万次阅读 2018-08-05 15:47:12
    MySQL Cluster技术在分布式系统中为MySQL提供了冗余特性,增强了安全性,可以大大的提高系统的可靠性和数据的有效性。MySQL集群需要一组计算机,每台计算机可以理解为一个节点,这些节点的功能各不相同。MySQL ...
  • MySQL概述

    千次阅读 2013-10-20 21:49:49
    MySQL概述 简介 MySQL是一个开放源码的小型关联式数据库管理系统,最初的开发者为瑞典MySQL AB公司。在2008年1月16号该公司被Sun公司收购,而2009年,SUN又被Oracal收购,因此现在的MySQL由Oracle公司管理和维护,但...
  • Mysql面试题

    千次阅读 多人点赞 2020-09-16 20:56:53
    文章目录 数据库基础知识为什么要使用数据库什么是SQL?...数据库三大范式是什么mysql有关权限的表都有哪几个MySQL的binlog有有几种录入格式?分别有什么区别? 数据类型mysql有哪些数据类型 引擎MySQL存储引擎My...
  • 第9章 MySQL高级应用技术来源:http://book.csdn.net/bookfiles/526/目录9.1 PHP的mysqli扩展 9.2 MySQL视图的应用 9.3 MySQL存储过程 9.4 MySQL触发器 9.5 MySQL事务 9.1 PHP的mysqli扩展在PHP5.0以后的版本...
  • MySQL技术内幕-InnoDB存储引擎-读书笔记(一) 作为php开发,使用mysql总是少不了的 第一章 MySQL体系结构和存储引擎 MySQL被设计为一个单进程多线程架构的数据库 ./mysql --help | grep my.cnf 可以查看mysql...
  • mysql面试题解答

    万次阅读 2020-09-17 15:06:32
    一条MySQL更新语句的执行过程是什么样的? 1.连接验证及解析 客户端与MySQL Server建立连接,发送语句给MySQL Server,接收到后如果是查询语句会先去查询缓存中看,有的话就直接返回了,(新版本的MySQL已经废除...
  • MySql全文索引

    千次阅读 2017-09-30 09:21:57
    全文索引(也称全文检索)是目前搜索引擎使用的一种关键技术。它能够利用「分词技术「等多种算法智能分析出文本文字中关键字词的频率及重要性,然后按照一定的算法规则智能地筛选出我们想要的搜索结果。在这
  • 转:MySQL技术内幕:InnoDB存储引擎

    千次阅读 2011-04-12 11:58:00
    内容简介: 本书是国内目前唯一的一本关于InnoDB的著作,由资深MySQL专家亲自执笔,中外数据库专家联袂推荐,权威性毋庸置疑。 内容深入,从源代码的角度深度解析了InnoDB的体系结构、实现原理、工作机制...
  • 关注微信公众号(文强的技术小屋),学习更多技术知识,一起遨游知识海洋~ 快速导航: MySQL原理与实践(一):一条select语句引出Server层和存储引擎层 MySQL原理与实践(二):一条update语句引...
  • 一:Mysql原理与慢查询 MySQL凭借着出色的性能、低廉的成本、丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库。虽然性能出色,但所谓“好马配好鞍”,如何能够更好的使用它,已经成为开发工程师的必修...
  • MySQL JDBC StreamResult通信原理浅析

    千次阅读 多人点赞 2018-10-17 17:02:56
    好几年没写技术博客了,今天写一个小的技术点给大家分享,关于MySQL JDBC StreamResult的原理分享,难度不大,就当程序员的闲聊。 如果使用MySQL JDBC读取过比较大的数据(例如超过1GB),应该清楚在读取的时候,很...
  • MySQL索引优化

    千次阅读 2021-02-19 09:42:44
    一、MySQL索引基础 本文主要讨论MySQL索引的部分知识。将会从MySQL索引基础、索引优化实战和数据库索引背后的数据结构三部分相关内容。 一、MySQL索引基础 首先,我们将从索引基础开始介绍一下什么是索引,分析...
  • 程序员的 MySQL 面试金典

    千次阅读 2019-09-25 23:30:02
    基于这个原则,我和汽车之家的资深技术专家老张,也是一位有着 10 年工作经验的技术老兵,前前后后大概花了两个多月的时间,改了 4、5 个版本,最终总结了 200 道左右的 MySQL 面试题,来帮助准备面试的朋友们。...
  • mysql性能

    千次阅读 2016-09-05 18:07:11
    监控MySQL的性能指标系列 / 数据库 / 度量 / mysql的 / 性能 这个职位是一个3部分组成的系列关于MySQL的第1部分监控。 第二 ​​部分是关于从MySQL收集度量和第3部分解释了如何使用MySQL的Datadog进行...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 90,467
精华内容 36,186
关键字:

关键技术mysql

mysql 订阅