首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
142 阅读
2
php接口优化 使用curl_multi_init批量请求
132 阅读
3
2024年备考系统架构设计师
102 阅读
4
《从菜鸟到大师之路 ElasticSearch 篇》
102 阅读
5
PHP 文件I/O
89 阅读
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
登录
Search
标签搜索
php函数
php语法
性能优化
安全
错误和异常处理
问题
vue
Composer
Session
缓存
框架
Swoole
api
并发
异步
正则表达式
php-fpm
mysql 索引
开发规范
协程
dafenqi
累计撰写
785
篇文章
累计收到
8
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
785
篇与
的结果
2023-12-05
编程中常用的字符编码知识点
编程中常用的字符编码知识点字符集和字符编码字符集就是字符的集合,如常见的 ASCII字符集,GB2312字符集,Unicode字符集等。这些不同字符集之间最大的区别是所包含的字符数量的不同。字符编码则代表字符集的实际编码规则,是用于计算机解析字符的,如 GB2312,GBK,UTF-8 等。字符编码的本质就是如何使用二进制字节来表示字符的问题。字符集和编码是一对多的关系,同一字符集可能有多种字符编码,如Unicode字符集就有 UTF-8,UTF-16 等。在前端开发中,Javascript程序是使用Unicode字符集,Javascript源码文本通常是基于UTF-8编码。 但js代码中的字符串类型是UTF-16编码的,这也是为什么会碰到api接口返回字符串在前端出现乱码,因为多数服务都使用utf-8编码,前后编码方式不一致。说起字符集的发展历程,可以总结为一句话:几乎都是对ASCII字符集的扩展。ASCII我们知道,计算机是使用二进制来处理信息的。 其中,每一个二进制位(bit)有 0和1 两种状态。一个字节(byte)则有8个二进制位,可以有256种状态。而ASCII就是基于拉丁字母、主要用于显示英文的一种单字节字符集,它的编码和字符是一一对应的,因为它就是使用一个字节8个二进制位来表示,不会超过256个字符。标准的ASCII字符总计有128个字符(2^7),其中前面32个控制字符,后面96个是可打印字符,包括常用的大小写字母数字标点符号等。因为只占用了一个字节的后7位,那字节的最高位一般设置为0。'a'.charCodeAt() // 97 'A'.charCodeAt() // 65 '9'.charCodeAt() // 57 '.'.charCodeAt() // 46 如上,每个字符会对应一个编码(使用数字标识),总共会从0-128。完整的ASCII码表,网上很容易找到。通过ASCII码表,我们发现,小写字母并没有和大写字母挨着排序?这是为了方便大小写之间的转换, A 排在 65(64 + 1) 位,而 a 排在 97(64 + 32 + 1) 位。65 ^ 32 = 97 // A ^ 32 = a 字符集的发展历史ASCII是几乎所有字符集的基础。标准的ASCII码最多只能标识128个字符,欧美国家可以很好的使用,但其他国家的字符变多,自然就不够用了。这个时候,最高位就开始被惦记上,通过扩展ASCII码的最高位,又能满足用于特殊符号的一些国家的需求,这种就是扩展ASCII码。但是亚非拉更多非拉丁语系的国家,字符成千上万,只能使用新的方式。如中文,就又进行了扩展,小于127的字符的意义与标准ASCII码相同,当需要标识汉字时,使用2个字节,每个字节都大于127。这种多字节字符集即GB2312,后续因为不断的扩展,如繁体字和各种符号,甚至少数民族的语言符号等等,又使用了包括GBK等不同字符集。因此,很多国家都制定了自己的编码字符集,基本都是在ASCII的基础上进行的。各字符集虽然都能够兼容标准ASCII码,但在使用交流上的不便是显而易见的,乱码也是随处可见。为了解决这种各自为战的问题,Unicode字符集就诞生了。UnicodeUnicode是国际组织制定的,用于收纳世界上所有文字和符号的字符集方案。前128个字符同ASCII一样,进行扩充后,使用数字0-0x10FFFF来映射这些字符,最多可以有1114112个字符。目前仍然只使用了其中的一小部分。Unicode一般使用两个字节来表示一个字符。码点 Unicode 规定了每个字符的数字编号,这个编号被称为 码点(code point)。码点以 U+hex 的形式表示,U+是代表Unicode的前缀,而 hex 是一个16进制数。取值范围是从 U+0000 到 U+10FFFF。每个码点对应一个字符,绝大部分的常见字符在最前面的 65536 个字符,范围是 U+0000到U+FFFF。一般汉字的码点区间为 U+2E80 - U+9FFF。字符平面 目前的Unicode分成了17个编组,也称平面,每个平面有65536个码点。第一个平面是基本多语言平面,范围:U+0000 - U+FFFF,多数常见字符都在该区间。其他平面则为辅助平面,范围:U+10000 到 U+10FFFF,如我们在网上常见 Emoji 表情。码元 码元(Code Unit)可以理解为对码点进行编码时的最小基本单元,码元是一个整体。而字符编码的作用就是将Unicode码点转换成码元序列。Unicode常用的编码方式有 UTF-8 、UTF-16 和 UTF-32,UTF是Unicode TransferFormat的缩写。UTF-8是8位的单字节码元,UTF-16是16位的双字节码元,UTF-32是32位的四字节码元。另外,为什么总看到使用十六进制数据来表示如码点等各种数据呢?因为,两位的十六进制正好等于一个字节8位,0xff = 0b11111111。UTF-8UTF-8是一种可变长度的字符编码方式。目前是使用 1 到 4 个字节来编码字符;是互联网时代应用最广的一种编码方式,前端接触的相对最多。需要注意的是:汉字一般占3个字节,表情符号一般占4个字节。UTF-8的编码规则:1个字节的字符,第一位为0,后7位为码点,与ASCII相同。n个字节的字符,第一个字节前面 n 位都是1,n+1位是0,可据此判断有几个字节。后面的几个字节都是 10 为开头2位。 这里规定的都是前缀,对于字符的码点,需要进行截取后依次放入除前缀外的其他位,所以UTF-8又被称为前缀码。格式如表:通过上表的编码规则,我们就可以进行各种转换了。下面,我们以一个中文字符的编码转换为例,如汉字 '好':'好'的Unicode码点:'好'.codePointAt() \\ 22909,结果是22909; 22909在UTF-8的3字节数的编码区间 U+0800 (2048) ~ U+FFFF (65535); 22909的二进制值:101100101111101,有15位; 而3字节数的编码需要16位,前面补0,根据表中规则分成3组:0101 100101 111101; 依次填入对应的前缀:11100101 10100101 10111101,得到3个字节; 将得到的三个字节转成十六进制数据:E5 A5 BD,所以汉字 '好' 的UTF-8就是:E5 A5 BD。我们使用 encodeURI 进行验证——encodeURI函数支持将中文进行 UTF-8 编码:encodeURI('好') // '%E5%A5%BD' 去除百分号,结果正好一致。UTF-16UTF-16的编码方式:基本平面的字符占用 2 个字节(U+0000到U+FFFF),辅助平面的字符占用 4 个字节(U+010000到U+10FFFF)。也就是说,UTF-16的编码长度要么是2个字节要么是4个字节。当为2字节时,则实际上与Unicode相同。并且还有个原则,在Unicode基本多语言平面内,从U+D800到U+DFFF之间的码点区间是不对应字符的。而UTF-16需要利用这块码位来对辅助平面的字符进行编码。它的具体规则:码点小于U+FFFF,基本字符,不需处理,直接使用,占两个字节。否则,拆分成两个码元,四个字节,cp表示码点:低位——((cp - 65536) / 1024) + 0xD800,值范围是 0xD800~0xDBFF;高位——((cp - 65536) % 1024) + 0xDC00,值范围是 0xDC00~0xDFFF。看下面的示例:汉字 '好','好'.codePointAt() // 22909,码点小于U+FFFF,直接进行十六进制转换:579D。表情符号 '',''.codePointAt() // 128516,码点需要拆分:低位:Math.floor(((128516 - 65536) / 1024)) + 0xD800 // 55357, 得到 D83D高位:((128516 - 65536) % 1024) + 0xDC00 // 56836,得到 DE04使用 String.fromCharCode 方法进行验证:String.fromCharCode(0xD83D, 0xDE04) // '' 需要明确的一点,Javascript中的字符串是基于UTF-16编码的,大端序字节。UTF-32是定长的编码,每个码位使用四个字节进行编码。优点是和unicode一一对应,缺点是太浪费空间。比较下面将选取字母、汉字、表情字符,进行编码对比查看:// UTF-8 'a': 97 - 0x61 '好': 22909 - (0xE5 0xA5 0xBD) '': 128516 - (0xF0 0x9F 0x98 0x84) // UTF-16 'a': 97 - 0x0061 '好': 22909 - 0x597d '': 128516 - (0xD83D, 0xDE04) 可以看到,UTF-8是变长1-4个字节,码元为8位;UTF-16是2或4字节,码元是16位。这里记住UTF-16的码元,对于我们理解下面的问题,比较有帮助。前端开发中的编码前面已提到过,javascript中的字符串是基于UTF-16编码的,所以在计算字符串长度时,我们需要先理解UTF-16编码。下面,我们看一下处理字符串时可能会遇到的问题。字符串长度计算字符串的length属性,实际上是使用UTF-16的码元个数来进行计算的:ASCII码和大部分中文,都是一个码元而表情字符和其他特殊字符都是两个码元所以,当某个字符中存在2个码元时,就算显示的是一个字符,length却等于2。'a'.length // 1 '好'.length // 1,多数汉字都是基本字符平面,只有一个码元,长度就为1。 ''.length // 2 组合字符的长度还有一种特殊的,组合字符,一般指一些带标点符号的字符:é。'é'.length // 2 'e\u0301'.length // 2 // 获取码点时,忽略了标点符号,显示的是字母的码点 'é'.codePointAt() // 101 'e'.codePointAt() // 101 如要正常操作组合字符,使用normalize()。'é'.normalize().length = 1。 多码元字符操作对于多码元字符使用下标取值时,得到的将是它的码元:''[0] // '\uD83D' ''[1] // '\uDE04' '123'[0] // '1' 循环时,使用 for 会乱码,而 for-of 则正常:let smile = '' for(let i = 0; i < smile.length; i++) { console.log(smile[i]) } // � // � for (let tt of smile) { console.log(tt) } // 但是,可以使用转换成扩展数组的方式访问:[...''][0] // '' Array.from('') // [''] 还可以使用码点的方式:String.fromCodePoint(''.codePointAt()) // '' 对于这种特殊字符,使用下面的字符串方法都会分割码元:split(),slice(),charAt(),charCodeAt(),substr(),substring()。''.slice(0, 2) // '' ''.slice(0, 1) // '\uD83D' ''.slice(1, 2) // '\uDE04' ''.substr(0,1) // '\uD83D' ''.substr(0,2) // '' ''.split('') // ['\uD83D', '\uDE04'] 正则中的 u 修饰符ES6在正则中添加了u修饰符,用来正确处理大于\uFFFF的 Unicode 字符,也就是能够正确处理四个字节的 UTF-16 编码。/^\S$/.test('') // false /^\S$/u.test('') // true 但对组合字符,u修饰符不起作用:/^\S$/u.test('é') // false /^\S$/u.test('e\u0301') // false 转义字符我们还需要注意的,是转义字符的计算,结果会以实际字符为准:'\x3f'.length // 1 '?'.length // 1 读取操作时,也能正常处理:'\x3f'[0] // '?' '\x3f'.split('') // ['?'] 常用API前端在对Unicode编码处理时,提供了一些可以使用的API,在实际工作中,会方便我们处理这方面的问题。处理码点和字符charAt(index) 从一个字符串中返回指定的字符,对于多码元字符,仍会返回码元字符:'a'.charAt() // 'a' ''.charAt() // '\uD83D' ''.charAt(1) // '\uDE04' charCodeAt(index) 返回0到65535之间的整数码点值。对于多码元如果字符的码点大于U+FFFF,则返回第一个码元值,还可以加索引参数取后面码元的值。codePointAt(pos) 返回Unicode码点,多码元也能返回完整的码点值。codePointAt可以传入索引参数,对多码元字符取第二个码元值。// 小于 U+FFFF '好'.codePointAt() // 22909 '好'.charCodeAt() // 22909 // 大于 U+FFFF ''.charCodeAt() // 55357 ''.charCodeAt(1) // 56836 ''.codePointAt() // 128516 ''.codePointAt(1) // 56836 String.fromCharCode(num1[, ...[, numN]]) 返回由指定的UTF-16码点序列创建的字符串。参数范围0到65535,大于65535的数据将被截断,结果不准确。对于多码元字符,则会将两个码元组合得到该字符。String.fromCodePoint(num1[, ...[, numN]]) 返回使用指定的代码点序列创建的字符串。可以处理多码元字符的完整码点值。String.fromCharCode(55357, 56836, 123) // '{' String.fromCodePoint(128516, 123, 8776) // '{≈' TextEncoderTextEncoder,使用 UTF-8 编码将代码点流转换成字节流。TextDecoder:解码。默认编码方式就是UTF-8,可以解决字符转UTF-8编码的问题。const txtEn = new TextEncoder() const enVal = txtEn.encode('好') // Uint8Array(3) [229, 165, 189] const txtDe = new TextDecoder() txtDe.decode(enVal) // '好' IE不支持。String.prototype.normalize()对于语调符号和重音符号,Unicode提供了两种方法,一种是直接提供带符号的字符,如 é (码点233);另一种是组合字符,如上文提到的 é (码点101)。针对这种码点不同,但实质一样的字符,Javascript识别不了:'é' === 'é' // false 而 normalize() 方法的引入,正是为了解决这一问题,它会按照一定的方式将字符的不同表示方法统一为标准形式:'é' === 'é'.normalize() // true URL的UTF8编解码另外,在前端常接触的网页中,URL链接编码也是非常常见的。诸如:'http%3A%2F%2Fbaidu.com%2F%E4%B8%AD%E5%9B%BD'。这里面涉及到的就是关于UTF-8的编码。而JavaScript提供了四个URL的编码/解码方法,可以用于将非ASCII码的字符,如中文字符、特殊字符、表情字符等,进行UTF-8的编解码操作:encodeURI() 和 encodeURIComponent()decodeURI() 和 decodeURIComponent()他们的短处也很明显,对ASCII字符如英文数字等字符无法处理。这里的转换方式:先转为UTF-8的字节码,然后前面加个 % 进行拼接得到编码结果。encodeURI('好') // '%E5%A5%BD' decodeURI('%E5%A5%BD') // '好' encodeURIComponent('好') // '%E5%A5%BD' decodeURIComponent('%E5%A5%BD') // '好' encodeURI('hello') // 'hello' encodeURIComponent('hello') // 'hello' encodeURIComponent('') // '%F0%9F%98%84' encodeURI和encodeURIComponent的区别这两者的不同之处,在于对部分URL元字符符号的处理上。URL元字符:分号(;),逗号(’,’),斜杠(/),问号(?),冒号(:),at(@),&,等号(=),加号(+),美元符号($),井号(#)。encodeURIComponent会对这些URL元字符进行编码,但是encodeURI则不会:encodeURIComponent(';,/@&=') // '%3B%2C%2F%40%26%3D' encodeURI(';,/@&=') // ';,/@&='END作者:土豆居士来源:一口Linux版权归原作者所有,如有侵权,请联系删除。
2023年12月05日
4 阅读
0 评论
0 点赞
2023-12-05
MySQL varchar(n)能存储几个汉字
MySQL varchar(n)能存储几个汉字 1 前言我们本节看一个小问题,就是我们平时用的varchar(n)能存储几个汉字。2 一个中文汉字占多少字节与编码有关在mysql中,一个中文汉字所占的字节数与编码格式有关:如果是GBK编码,则一个中文汉字占2个字节;如果是UTF8编码,则一个中文汉字占3个字节,而英文字母占1字节。UTF8:一个中文汉字=3个字节GBK:一个中文汉字=2个字节utf-8, 英文字母1字节3 varchar(n)能存储几个汉字MySQL中varchar(n)表示n个字符,无论汉字和英文,Mysql都能存入n个字符,仅是实际字节长度有所区别。对于utf8字符集:length返回字节数,char_length返回字符数。对于gbk字符集而言:说明:UTF-8:Unicode Transformation Format-8bit,允许含BOM,但通常不含BOM。是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24为(三个字节)来编码。UTF-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。UTF-8编码的文字可以在各国支持UTF8字符集的浏览器上显示。如,如果是UTF8编码,则在外国人的英文IE上也能显示中文,他们无需下载IE的中文语言支持包。GBK 是国家标准GB2312基础上扩容后兼容GB2312的标准。GBK的文字编码是用双字节来表示的,即不论中、英文字符均使用双字节来表示,为了区分中文,将其最高位都设定成1。GBK包含全部中文字符,是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBD大。GBK、GB2312等与UTF8之间都必须通过Unicode编码才能相互转换:GBK、GB2312 --> Unicode --> UTF8 UTF8 --> Unicode -> GBK、GB2312GB2312是GBK的子集,GBK是GB18030的子集GBK是包括中日韩字符的大字符集合为了避免所有乱码问题,应该采用UTF-8,将来要支持国际化也非常方便 UTF8可以看作是大字符集,它包含了大部分文字的编码。使用UTF8的一个好处是其他地区的用户(如香港台湾)无需安装简体中文支持就能正常观看你的文字而不会出现乱码。gb2312是简体中文的码gbk支持简体中文及繁体中文big5支持繁体中文utf8支持几乎所有字符4 小结在字符集为UTF-8的情况下:若 MySQL version < 4.1:VARCHAR以字节为单位存储,假设全部为常用汉字,则VARCHAR(255)共可存放约85个汉字。若 MySQL version >= 4.1:VARCHAR以字符为单位存储,假设全部为常用汉字,则VARCHAR(255)可以存放255个汉字。当然,现在MySQL基本都大于4.1版本的,所以说MySQL中varchar(n)表示n个字符,无论汉字和英文,Mysql都能存入n个字符,仅是实际字节长度有所区别。实践以上是转载,以前遇到过这个问题,类似一个公告数据表,公告内容字段类型为varchar(256),还想按照字节编码utf-8能存85个中文字符,往该表字段值塞进去超过三百字中文试了下,最后得到该字段值显示为:253个中文+2个数字+1个中文感叹号。mysql版5.7,石锤了。拓展:💻1、char的长度是不可变的,而varchar的长度是可变的例如值:abc类型char(10),存储值为:abc_______(abc+7个空格)类型varchar(10), 存储值为:abc (自动缩短为3个字母的长度,节省存储空间)🌈💻2、因为char类型长度固定,因此char的存取效率比varchar要快得多,方便程序的存储与查找。但是char也为此付出的是空间的代价,因为其长度固定,所以会占据多余的空间,可谓是以空间换取时间效率。(以空间换时间的做法)而varchar则刚好相反,节省空间但存取效率相对较低。(以时间换空间的做法)🌈💻3、存储的容量不同char类型最多可存放255个字符,并且和编码类型无关varchar类型复杂一些:varchar的最大长度为65535个字节,varchar可存放的字符数因编码类型不同而异(mysql的version小于4.1)👉字符类型若为gbk,每个字符最多占2个字节,最大长度不能超过32766个字符👉字符类型若为utf8,每个字符最多占3个字节,最大长度不能超过21845个字符🌈💻使用建议👉若字段长度固定,则可以使用char来保存,存取效率高,如:手机号、身份证👉若字段长度经常改变,可选用varchar来最大化利用存储空间,减少空间浪费。
2023年12月05日
33 阅读
0 评论
0 点赞
2023-12-04
php接口优化 使用curl_multi_init批量请求
需求:一个接口里请求了多个接口(方法),查询速度缓慢,每个请求2s,加起来11、12s。实现:PHP中的curl_multi一类函数可以实现同时请求多个url,而不是一个一个依次请求,这就类似一个进程实现了多个线程的功能,最终接口请求耗时优化到2s,即查询耗时最长的那次是最终的查询时长。<?php class XClass extends BaseSys { public function index() { $rs = []; $local_url = "当前请求地址"; $urlArr = []; $paramsArr = []; $params = $_POST; $curAction = [ ['name'=>'getANum','bool'=>''], ['name'=>'getCNum','bool'=>true], ['name'=>'getCNum','bool'=>false], ['name'=>'getLNum','bool'=>''], ...... ];//接口名、参数 foreach ($curAction as $key => $item) { $urlArr[] = $local_url . "XClass/".$item["name"]; $subparams = $params; $subparams["bool"] = $item["bool"]; $subparams["isMutil"] = 1; $paramsArr[] = $subparams; } $contentArr = $this->mutil_curl($urlArr, $paramsArr); foreach ($contentArr as &$item) { $rs[] = json_decode($item,true); } return $rs; } public function getCNum($isNew = false) { //TODO if (isset($_POST["isMutil"])) {//mutil_curl并发请求结果 exit($json_encode($rs, JSON_UNESCAPED_UNICODE) . ''); } return $rs; } /** * 多url同时访问curl操作封装,一般用于内部并行数据查询 * $urlArr 要访问的网址集合,get方法的时候请在此网址上直接带上参数 * $paramsArr 要post的数据集合 */ public function mutil_curl($urlArr, $paramsArr = null, $timeout = 60) { $contentArr = []; $num = 0; $len = count($urlArr)-1; foreach ($urlArr as $k => $url) { if($num == 0){ $subrulArr = []; } $subrulArr[$k] = $url; $num++; if($num == 16 || $len == $k){ $subcontentArr = $this->_do_mutil_curl($subrulArr, $paramsArr); foreach ($subcontentArr as $subkey => $subvalue) { $contentArr[$subkey] = $subvalue; } $num = 0; } } return $contentArr; } public function _do_mutil_curl($urlArr, $paramsArr = null, $timeout = 60) { set_time_limit($timeout); // curl 批处理 $mh = curl_multi_init(); // 保存单个句柄 $handles = []; // 向 curl 批处理会话 中添加单独的 curl句柄 foreach ($urlArr as $k => $url) { $ch = curl_init(); // 初始化curl并设置链接 curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); // 设置是否为post传递 curl_setopt($ch, CURLOPT_POST, !empty($paramsArr)); // 对于https 设定为不验证证书和host curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//设置是否返回信息 if (!empty($paramsArr)) { $params = $paramsArr[$k]; if ($params) { if (is_array($params)) { $params = http_build_query($params); } // POST 数据 curl_setopt($ch, CURLOPT_POSTFIELDS, $params); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); } } $handles[$k] = $ch; curl_multi_add_handle($mh, $handles[$k]); } $active = null; $contentArr = []; do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active and $mrc == CURLM_OK) { if (curl_multi_select($mh) != -1) { do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } } // 循环输出 大小以及地址 foreach ($handles as $i => $handle) { // 内容 $content = curl_multi_getcontent($handle); $contentArr[$i] = $content; curl_multi_remove_handle($mh, $handle); } curl_multi_close($mh); return $contentArr; } PS:这里为了方便显示,把mutil_curl()和_do_mutil_curl()放在同一个类中,实际上它们是放在一个工具类里,方便调用。
2023年12月04日
132 阅读
2 评论
0 点赞
2023-11-30
MySQL高级篇之View视图
MySQL高级篇之View视图一、视图1.什么是视图视图是一种虚拟表 ,本身是不具有数据 的,占用很少的内存空间,它是 SQL 中的一个重要概念.视图建立在已有表的基础上, 视图赖以建立的这些表称为基表.视图的创建和删除只影响视图本身,不影响对应的基表。但是当对视图中的数据进行增加、删除和修改操作时,数据表中的数据会相应地发生变化,反之亦然。视图,是向用户提供基表数据的另一种表现形式。通常情况下,小型项目的数据库可以不使用视图,但是在大型项目中,以及数据表比较复杂的情况下,视图的价值就凸显出来了,它可以帮助我们把经常查询的结果集放到虚拟表中,提升使用效率。理解和使用起来都非常方便。2.创建视图2.1 视图语法完整的创建视图的语法结构CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] VIEW 视图名称 [(字段列表)] AS 查询语句 [WITH [CASCADED|LOCAL] CHECK OPTION]简化版本:CREATE VIEW 视图名称 AS 查询语句2.2 创建单表视图举例:CREATE VIEW v_student AS SELECT id,NAME,sex FROM student t WHERE id = 901;查询视图:SELECT * FROM v_student;针对别名的处理,可以在创建视图的子查询中指定对应的别名CREATE VIEW v_student1 AS SELECT id stu_id,name stu_name,sex gender FROM student ;也可以在创建视图的视图名称后添加对应的别名字段CREATE VIEW v_student2(stu_id,stu_name,gender) AS SELECT id ,name ,sex FROM student ;我们在创建视图的时候还可以封装不是基表中存在的字段的情况说明:实际上就是我们在 SQL 查询语句的基础上封装了视图 VIEW,这样就会基于 SQL 语句的结果集形成一张虚拟表。在创建视图时,没有在视图名后面指定字段列表,则视图中字段列表默认和SELECT语句中的字段列表一致。如果SELECT语句中给字段取了别名,那么视图中的字段名和别名相同。2.2 创建多表视图上面是基于单表创建的视图,当然我们也可以基于多表查询的结果来封装为对应的视图。CREATE VIEW v_student_score AS SELECT t1.id,t1.name,t2.c_name,t2.grade FROM student t1 left join score t2 on t1.id = t2.stu_id查询视图SELECT * FROM v_student_score;当然别名的处理方式在多表中同样的适用。2.3 基于视图创建视图当我们创建好一张视图之后,还可以在它的基础上继续创建视图。CREATE VIEW v_student_score1 AS SELECT * FROM v_student_score WHERE grade > 80;查看视图select * from v_student_score1;到这其实我们能够发下,视图的创建还是非常灵活的。3.查看视图语法1:查看数据库的表对象、视图对象SHOW TABLES;语法2:查看视图的结构DESC / DESCRIBE 视图名称;语法3:查看视图的属性信息# 查看视图信息(显示数据表的存储引擎、版本、数据行数和数据大小等) SHOW TABLE STATUS LIKE '视图名称'\\G执行结果显示,注释Comment为VIEW,说明该表为视图,其他的信息为NULL,说明这是一个虚表。语法4:查看视图的详细定义信息SHOW CREATE VIEW 视图名称;4.更新视图数据4.1 一般情况MySQL支持使用INSERT、UPDATE和DELETE语句对视图中的数据进行插入、更新和删除操作。当视图中的数据发生变化时,数据表中的数据也会发生变化,反之亦然。举例:updatemysql> select * from v_student; +-----+--------+-----+ | id | NAME | sex | +-----+--------+-----+ | 901 | 张老大 | 男 | +-----+--------+-----+ 1 row in set (0.03 sec) mysql> update v_student set name='张老大11'; Query OK, 1 row affected (0.02 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from v_student; +-----+----------+-----+ | id | NAME | sex | +-----+----------+-----+ | 901 | 张老大11 | 男 | +-----+----------+-----+ 1 row in set (0.03 sec) mysql> select * from student; +-----+----------+-----+-------+------------+--------------+ | id | name | sex | birth | department | address | +-----+----------+-----+-------+------------+--------------+ | 901 | 张老大11 | 男 | 1985 | 计算机系 | 北京市海淀区 | | 902 | 张老二 | 男 | 1986 | 中文系 | 北京市昌平区 | | 903 | 张三 | 女 | 1990 | 中文系 | 湖南省永州市 | | 904 | 李四 | 男 | 1990 | 英语系 | 辽宁省阜新市 | | 905 | 王五 | 女 | 1991 | 英语系 | 福建省厦门市 | | 906 | 王六 | 男 | 1988 | 计算机系 | 湖南省衡阳市 | +-----+----------+-----+-------+------------+--------------+ 6 rows in set (0.05 sec) mysql>举例:deletemysql> delete from v_student; Query OK, 1 row affected (0.01 sec) mysql> select * from v_student; Empty set mysql> select * from student; +-----+--------+-----+-------+------------+--------------+ | id | name | sex | birth | department | address | +-----+--------+-----+-------+------------+--------------+ | 902 | 张老二 | 男 | 1986 | 中文系 | 北京市昌平区 | | 903 | 张三 | 女 | 1990 | 中文系 | 湖南省永州市 | | 904 | 李四 | 男 | 1990 | 英语系 | 辽宁省阜新市 | | 905 | 王五 | 女 | 1991 | 英语系 | 福建省厦门市 | | 906 | 王六 | 男 | 1988 | 计算机系 | 湖南省衡阳市 | +-----+--------+-----+-------+------------+--------------+ 5 rows in set (0.07 sec)4.2 不可更新的视图要使视图可更新,视图中的行和底层基本表中的行之间必须存在 一对一 的关系。另外当视图定义出现如下情况时,视图不支持更新操作:在定义视图的时候指定了“ALGORITHM = TEMPTABLE”,视图将不支持INSERT和DELETE操作;视图中不包含基表中所有被定义为非空又未指定默认值的列,视图将不支持INSERT操作;在定义视图的SELECT语句中使用了 JOIN联合查询 ,视图将不支持INSERT和DELETE操作;在定义视图的SELECT语句后的字段列表中使用了 数学表达式 或 子查询 ,视图将不支持INSERT,也不支持UPDATE使用了数学表达式、子查询的字段值;在定义视图的SELECT语句后的字段列表中使用 DISTINCT 、 聚合函数 、 GROUP BY 、 HAVING 、 UNION 等,视图将不支持INSERT、UPDATE、DELETE;在定义视图的SELECT语句中包含了子查询,而子查询中引用了FROM后面的表,视图将不支持 INSERT、UPDATE、DELETE;视图定义基于一个不可更新视图 ;常量视图。举例:更新前面的统计视图mysql> select * from v_student4; +------------+-----------+ | department | stu_count | +------------+-----------+ | 中文系 | 2 | | 英语系 | 2 | | 计算机系 | 1 | +------------+-----------+ 3 rows in set (0.07 sec) mysql> update v_student4 set department='test' ; 1288 - The target table v_student4 of the UPDATE is not updatable mysql> delete from v_student4; 1288 - The target table v_student4 of the DELETE is not updatable注意:虽然可以更新视图数据,但总的来说,视图作为虚拟表 ,主要用于方便查询 ,不建议更新视图的数据。对视图数据的更改,都是通过对实际数据表里数据的操作来完成的。5.修改视图方式1:使用CREATE OR REPLACE VIEW 子句修改视图CREATE OR REPLACE VIEW v_student_score AS SELECT t1.id,t1.name,t2.c_name,t2.grade FROM student t1 left join score t2 on t1.id = t2.stu_id方式2:ALTER VIEW修改视图的语法是:ALTER VIEW 视图名称 AS查询语句6.删除视图删除视图只是删除视图的定义,并不会删除基表的数据。删除视图的语法是:DROP VIEW IF EXISTS 视图名称; DROP VIEW IF EXISTS 视图名称1,视图名称2,视图名称3,...;举例:DROP VIEW v_student;说明:基于视图a、b创建了新的视图c,如果将视图a或者视图b删除,会导致视图c的查询失败。这样的视图c需要手动删除或修改,否则影响使用。7.视图的总结7.1 视图的优点● 简单性。视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视图,从而使用户不必为以后的操作每次都指定全部的条件。 ● 安全性。通过视图用户只能查询和修改他们所能见到的数据。数据库中的其他数据则既看不见也取不到。数据库授权命令可以使每个用户对数据库的检索限制到特定的数据库对象上,但不能授权到数据库特定行和特定的列上。通过视图,用户可以被限制在数据的不同子集上。 ● 逻辑数据独立性。视图可以使应用程序和数据库表在一定程度上独立。如果没有视图,应用一定是建立在表上的。有了视图之后,程序可以建立在视图之上,从而程序与数据库表被视图分割开来。7.2 视图的不足● 性能:SQL Server必须把视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,SQL Server也把它变成一个复杂的结合体,需要花费一定的时间。 ● 修改限制:当用户试图修改视图的某些行时,SQL Server必须把它转化为对基本表的某些行的修改。对于简单视图来说,这是很方便的,但是,对于比较复杂的视图,可能是不可修改的。 所以,在定义数据库对象时,不能不加选择地来定义视图,应该权衡视图的优点和缺点,合理地定义视图。如果我们在实际数据表的基础上创建了视图,那么,如果实际数据表的结构变更了,我们就需要及时对相关的视图进行相应的维护。特别是嵌套的视图(就是在视图的基础上创建视图),维护会变得比较复杂, 可读性不好 ,容易变成系统的潜在隐患。因为创建视图的 SQL 查询可能会对字段重命名,也可能包含复杂的逻辑,这些都会增加维护的成本。实际项目中,如果视图过多,会导致数据库维护成本的问题。所以,在创建视图的时候,你要结合实际项目需求,综合考虑视图的优点和不足,这样才能正确使用视图,使系统整体达到最优。
2023年11月30日
16 阅读
0 评论
0 点赞
2023-11-30
mysql:详解sql_mode,应该选择怎样的运行模式?
mysql:详解sql_mode,应该选择怎样的运行模式?关于mysql的sql模式几个问题mysql设置set global sql_mode与set session sql_modeq区别set global sql_mode 是用来设置MySQL服务器的 全局SQL模式 ,这会影响到 所有新的数据库会话(命令行中测试过,设置全局SQL模式 不会当前会话中生效,在新会话生效) 。而 set session sql_mode 是用来设置 当前数据库会话的SQL模式,只会影响当前会话,不会影响其他会话或全局设置 。mysql 的set sql_mode设置后需不需要flush privileges更新用户权限在MySQL中,set sql_mode语句用于设置SQL模式,它不会影响用户权限。因此,不需要使用flush privileges命令来更新用户权限。flush privileges命令用于重新加载授权表,当对授权表进行更改时需要使用该命令来使更改生效,但是与sql_mode设置无关。set global sql_mode='';之后当mysql服务器重启会失效吗这将会将全局的sql_mode参数设置为空。这个设置会一直有效,直到你重新设置sql_mode参数为其他值为止。即使在MySQL服务器重启后,这个设置也会保持有效。建议安装mysql实例后将它的运行模式设置为 “TRADITIONAL”的好处,或者说设置sql模式的好处严格的数据验证:TRADITIONAL模式强制MySQL执行更严格的数据验证,这有助于避免一些常见的错误和数据不一致性。这可以提高数据的完整性和可靠性。避免隐式数据转换:TRADITIONAL模式禁止隐式数据转换,这意味着在进行比较和计算时,MySQL不会自动转换数据类型,而是会抛出错误。这有助于避免一些潜在的错误和数据不一致性。更严格的SQL语义:TRADITIONAL模式强制MySQL更严格地遵循SQL标准,这可以帮助开发人员编写更加规范和可移植的SQL语句。更好的兼容性:TRADITIONAL模式使MySQL更加符合SQL标准,这意味着数据库应用程序更容易在不同的数据库系统之间迁移和运行。SET GLOBAL sql_mode = 'TRADITIONAL'; #等同set @@global.sql_mode= 'TRADITIONAL';将MySQL实例的运行模式设置为“TRADITIONAL”可以提高数据的完整性和可靠性,减少潜在的错误,并使数据库更加符合SQL标准,从而提高数据库的质量和可移植性。注意,在进行此更改之前,请确保了解当前的sql_mode设置,并了解更改可能对现有应用程序和查询产生的影响。
2023年11月30日
23 阅读
0 评论
0 点赞
1
...
52
53
54
...
157