iView DatePicker组件数据回显日期格式错误1 问题描述表单提交后期需要修改，涉及到表单数据回显，其中用到了iView的DatePicker组件，在重新提交回显数据时，发生了错误，日期时间的格式不是预期的yyyy-MM-dd，而是标准时间格式Tue Sep 20 2022 00:00:00 GMT+0800(中国标准时间)，导致java后台程序时间解析报错，数据update失败。java.text.ParseException: Unparseable date: "Tue Sep 20 2022 00:00:00 GMT+0800(中国标准时间)"2 问题原因出现问题的前端代码<FormItem label="日期"> <DatePicker type="date" format="yyyy-MM-dd" placement="bottom-end" placeholder="选择日期" v-model="formItem.statusDate" @on-change="statusDateChange" ></DatePicker> (申报/公开/授权 日期) </FormItem>查阅iview官方文档发现，v-model绑定日期数据时，值为date类型，回显数据在双重绑定时，格式发生转换错误，在字符串和date类型之间不断变化，导致表单更新时好时坏。3 解决办法经过网上的查询，在这篇文章ivew日期选择器，双向绑定日期格式，执行回调后不改变格式发现同类型问题，将v-model改为:value，并配合@on-change即可<FormItem label="日期"> <DatePicker type="date" format="yyyy-MM-dd" placement="bottom-end" placeholder="选择日期" :value="formItem.statusDate" @on-change="statusDateChange" ></DatePicker> (申报/公开/授权 日期) </FormItem>
axios请求发送0 抽取baseURLVue项目可在项目根目录的main.js文件中抽取baseURLaxios.defaults.baseURL = 'http://localhost:8080/eduonline'1 get请求多个参数传递，一定是params表示1.1 代码示例1axios({ method: 'get', url: 'page/getPageInfo', headers: { 'content-type': 'application/json', }, params: { pos: this.position, }, }).then(res => { console.log('成功了') console.log(res) ) }, err => { console.log('错误了') console.log(err) })1.2 代码示例2//可配置参数的方式 axios.get('/user',{ params:{ ID:12345 } }).then(res => { console.log('成功了') console.log(res) ) }, err => { console.log('错误了') console.log(err) }) //或者直接在url后拼接参数 axios.get('/user?id=123') .then(res => { console.log('成功了') console.log(res) ) }, err => { console.log('错误了') console.log(err) })2 post请求post传递参数，必须用data2.1 代码示例//以json格式传入参数 axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone' }) .then(res => { console.log('成功了') console.log(res) ) }, err => { console.log('错误了') console.log(err) }) //发送post请求 axios({ method: 'post', //请求方式，默认是get请求 url:'/user/12345', //地址 data:{ //参数 firstName: 'simon', lastName: 'li' } });2.1 后端接收参数为null，可以用FormData传递项目中发现前端post请求传参，后端接收不到，均为null值，此时可以通过FormData进行传参let formData = new FormData() formData.append('position', this.position) formData.append('docServerFileName', this.docServerFileName) formData.append('socServerFileName', this.socServerFileName) formData.append('vidServerFileName', this.vidServerFileName) formData.append('posResponsibility', this.posResponsibility) axios({ method: 'post', url: 'page/submitPageInfo', headers: { 'content-type': 'application/json', }, data: formData, }).then(res => { console.log('成功了') this.$Message.success('提交成功') }, err => { console.log('错误了') console.log(err) this.$Message.error('提交失败，请联系技术人员') })2.3 上传文件需修改content-type上传文件，headers里的content-type需改为multipart/form-datalet formData = new FormData() formData.append('file', this.file) axios({ method: 'post', url: 'positionLearning/videoUpload/uploadVideo', headers: { 'content-type': 'multipart/form-data', }, data: formData, }).then(res => { console.log('成功了') console.log(res) }, err => { console.log('错误了') console.log(err) this.$Message.error('后台服务出问题，请联系技术人员') })
经验笔记 - 01_Vue+SpringBoot实现文件上传（单文件上传、单页面多处地方需要上传）
后端9月初，接到单位领导的任务，要求实现一个在线教育平台，主要展示岗位教学视频，以及相关的作业指导书，并建议要配套一个后台系统方便文件的上传和管理。作为后端开发，决定使用前后端分离，前端使用vue-admin-element，后端使用Springboot+MyBatis，数据库使用Mysql，容器使用tomcat，以及部署vue项目需要的nginx，jdk使用1.8由于项目开发只有我一个人，开发过程中遇到了不少问题，但所幸都一一解决了，趁着摸鱼的空档，决定将遇到的问题和解决方法记录下来，方便之后查阅。1. SpringBoot实现文件上传参考的是这篇掘金的文章，很详细。SpringBoot实现文件上传1.1 配置类 UploadConfig限制上传文件大小，以及总的请求文件大小。/** * @author ：RukiHuang * @description：文件上传配置类 * 配置MaxFileSize等属性 * @date ：2022/9/2 9:45 */ @Configuration public class UploadConfig { @Bean public MultipartConfigElement multipartConfigElement() { MultipartConfigFactory factory = new MultipartConfigFactory(); //单个数据大小 factory.setMaxFileSize(DataSize.ofMegabytes(200)); //总上传文件的大小 factory.setMaxRequestSize(DataSize.ofGigabytes(10)); return factory.createMultipartConfig(); } } 也可以通过在 application.properties配置文件中指定#文件上传 # 文件大小设置已在UploadConfig中配置，也可在配置文件中配置 #单个文件大小 spring.servlet.multipart.max-file-size=200MB #总文件大小（允许存储文件的文件夹大小) spring.servlet.multipart.max-request-size=10240MB1.2 配置文件 application.properties指定文件上传目标路径以及允许的文件类型#文件上传的目标路径 file.upload.path=G:\\temp\\ #文件上传允许的类型 file.upload.allowType[0]=application/pdf file.upload.allowType[1]=video/mp41.3 配置文件读取 UploadProperties/** * @author ：RukiHuang * @description：文件上传 * 上传路径 * 文件格式 * @date ：2022/9/2 10:05 */ @Component @ConfigurationProperties(prefix = "file.upload") public class UploadProperties { private String path; private List<String> allowTypeList; public String getPath() { return path; } public void setPath(String path) { this.path = path; } public List<String> getAllowType() { return allowTypeList; } public void setAllowType(List<String> allowTypeList) { this.allowTypeList = allowTypeList; } } 1.4 工具类1.4.1 唯一ID生成器 IDUtils生成唯一id/** * @author ：RukiHuang * @description：唯一ID生成器 * @date ：2022/9/2 10:07 */ public class IDUtils { public static String generateUniqueId() { return UUID.randomUUID().toString() + System.currentTimeMillis(); } }1.4.2 文件名替换工具 UploadUtils替换原始文件名，避免文件名重复/** * @author ：RukiHuang * @description：文件名替换工具 避免文件名重复 * @date ：2022/9/2 10:09 */ public class UploadUtils { public static String generateFileName(String oldName) { String suffix = oldName.substring(oldName.lastIndexOf(".")); return IDUtils.generateUniqueId() + suffix; } }1.5 web层 CoursewareController/** * @author ：RukiHuang * @description：课件的Controller * @date ：2022/9/1 13:37 */ @CrossOrigin(origins = "*",maxAge = 3600) @RequestMapping("/forum") @RestController public class CoursewareController { private static Logger logger = LoggerFactory.getLogger(DocController.class); @Autowired CoursewareService coursewareService; @Autowired UploadService uploadService; @RequestMapping("/coursewareUpload/uploadCourseware") public ResponseResult uploadVideo( @RequestParam("file") MultipartFile file ) { String filename = null; try { filename = uploadService.uploadCourseware(file); return ResponseResult.ok(filename, "课件上传成功"); } catch (IOException e) { logger.error(e.getMessage()); return ResponseResult.failed(e.getMessage(),"课件上传失败"); } } @RequestMapping(value = "/coursewareUpload/submitCoursewareInfo", method = RequestMethod.POST) public ResponseResult submitCoursewareInfo( @RequestParam(name = "serverFileName")String serverFileName) { try { coursewareService.addCoursewareInfo(serverFileName); return ResponseResult.ok("提交成功"); } catch (Exception e) { logger.error(e.getMessage()); return ResponseResult.failed(e.getMessage(), "提交失败"); } } } 1.6 service层 UploadService CoursewareServiceUploadService/** * @author ：RukiHuang * @description：上传service * @date ：2022/9/2 10:16 */ @Service public class UploadServiceImpl implements UploadService { @Autowired UploadProperties uploadProperties; @Override public String uploadCourseware(MultipartFile file) throws IOException { System.out.println(file.getContentType()); if(!uploadProperties.getAllowType().get(0).equals(file.getContentType())) { throw new IOException("课件上传类型错误"); } String fileName = UploadUtils.generateFileName(file.getOriginalFilename()); File newFile = new File(uploadProperties.getPath()+"\\courseware\\" + fileName);//当前是在windows目录，部署到linux路径要修改为/courseware file.transferTo(newFile); System.out.println(newFile.getPath()); return fileName; } }CoursewareService/** * @author ：RukiHuang * @description：课件service * @date ：2022/9/1 13:42 */ @Service public class CoursewareServiceImpl implements CoursewareService { @Autowired CoursewareDao coursewareDao; @Override public void addCoursewareInfo(String serverFileName) throws Exception { String[] nameArray = serverFileName.split(" / "); String coursewareName = nameArray[0]; String serverStorageName = nameArray[1]; String storagePath = "courseware/" + serverStorageName; CoursewareInfoDto coursewareInfoDto = new CoursewareInfoDto(coursewareName, storagePath); coursewareDao.addCoursewareInfo(coursewareInfoDto); } }1.7 PostMan测试文件上传第一步，头文件置空第一步，请求体中选择上传文件前端2. Vue实现文件上传2.1 单文件上传2.1.1 业务逻辑先对文件进行校验，判断文件大小以及类型是否符合要求。将文件上传至服务器。服务器返回新文件名和存储路径将文件名称和服务器名称提交至服务器，并将存储路径传入至数据库。组件使用的是vue-admin-template自带的iView，iView官方文档2.1.2 代码实现 <Upload :before-upload="beforeUpload" type="drag" action="http://localhost:8080/eduonline/forum/coursewareUpload/submitCoursewareInfo"> <Icon type="ios-cloud-upload" size="52" style="color: #3399ff"></Icon> 点击或拖拽文件至此 文档限制pdf格式，视频限制mp4格式(大小不超过200MB) </Upload> 上传文件: {{ file.name }} {{ loadingStatus ? '上传中' : '点击上传' }} 提交 2.2 一个页面多处地方需要文件上传2.2.1 业务逻辑iview无法实现单个页面多处地方文件上传，file会被替换使用原生的上传组件进行上传。2.2.2 代码实现 作业指导书： {{ loadingStatus1 ? '上传中' : '上传文件' }} SOC文档： {{ loadingStatus4 ? '上传中' : '上传文件' }} 教学视频： {{ loadingStatus5 ? '上传中' : '上传文件' }} 工段岗位： <OptionGroup label="1 片叶"> {{ item.label }} </OptionGroup> <OptionGroup label="2 烘丝"> {{ item.label }} </OptionGroup> <OptionGroup label="3 掺配加香"> {{ item.label }} </OptionGroup> <OptionGroup label="4 膨胀"> {{ item.label }} </OptionGroup> <OptionGroup label="5 梗丝"> {{ item.label }} </OptionGroup> <OptionGroup label="6 残烟间"> {{ item.label }} </OptionGroup> 岗位职责： <!-- <divider />--> 提交
微信小程序 - 05_网络数据请求（wx.request、get/post、跳过request合法域名校验、案例练习）
01 请求限制处于安全性的考虑，小程序官方对数据接口的请求做出了两个限制：只能请求HTTPS类型的接口必须将接口的域名添加到信任列表中02 配置request合法域名配置步骤：登陆微信小程序管理后台->开发->开发设置->服务器域名->修改request合法域名注意事项：域名只支持https协议域名不能使用ip地址或者localhost域名必须经过icp备案服务器域名一个月最多可以申请5次修改03 发起get请求调用微信小程序提供wx.request()方法，可以发起get数据请求wx.request({ url: 'https://www.escook.cn/api/get',//请求的接口地址，必须基于https协议 method: 'get', //请求的方式 data:{ //发送到服务器的数据 name: 'zs', age: 22 }, success: (res) => {//请求成功之后的回调函数 console.log(res)//查看真实数据 console.log(res.data) } })04 发起post请求wx.request({ url: 'https://www.escook.cn/api/post',//请求的接口地址，必须基于https协议 method: 'post', //请求的方式 data:{ //发送到服务器的数据 name: 'zs', age: 22 }, success: (res) => {//请求成功之后的回调函数 console.log(res)//查看真实数据 console.log(res.data) } })05 页面刚加载时就请求数据在很多情况下，我们需要在页面刚加载的时候，自动请求一些初始化数据。此时需要在页面的onLoad事件中调用获取数据的函数，示例代码如下：/** * 生命周期函数——监听页面加载 */ onLoad: function(options) { this.getInfo() this.postInfo() }06 跳过request合法域名校验开启开发环境不检验请求域名、TLS版本及HTTPS证书，跳过request合法域名的校验。注意：跳过合法域名校验，只在开发和测试阶段使用07 跨域和Ajax跨域问题只存在于基于浏览器的web开发中。由于小程序的宿主环境不是浏览器，而是微信客户端，所以小程序中不存在跨域的问题。Ajax技术的核心是依赖于浏览器中的XMLHttpRequest这个对象，由于小程序的宿主环境是微信客户端，所以小程序中不能叫做“发起Ajax请求”，而是叫做“发起网络数据请求”。08 练习app.json{ "pages":[ "pages/home/home", "pages/message/message", "pages/contact/contact", ], "window":{ "navigationBarBackgroundColor": "#025D54", "navigationBarTitleText": "在线测试", "navigationBarTextStyle":"white", "backgroundColor": "#efefef", "backgroundTextStyle":"dark", "onReachBottomDistance": 50 }, "tabBar": { "list": [ { "pagePath": "pages/home/home", "text": "home", "iconPath": "images/home.png", "selectedIconPath": "images/home-active.png" }, { "pagePath": "pages/message/message", "text": "message", "iconPath": "images/message.png", "selectedIconPath": "images/message-active.png" }, { "pagePath": "pages/contact/contact", "text": "contact", "iconPath": "images/contact.png", "selectedIconPath": "images/contact-active.png" } ] }, "style": "v2", "sitemapLocation": "sitemap.json", "lazyCodeLoading": "requiredComponents" } wxml <swiper indicator-dots="true" circular="true"> <swiper-item wx:for="{{swiperList}}" wx:key="id"> <image src="{{item.image}}"></image> </swiper-item> </swiper> <view class="grid-list"> <view class="grid-item" wx:for="{{gridList}}" wx:key="id"> <image src="{{item.icon}}"></image> <text>{{item.name}}</text> </view> </view> <view class="img-box"> <image src="/images/link-01.png" mode="widthFix"></image> <image src="/images/link-02.png" mode="widthFix"></image> </view> wxss/* 轮播图 */ swiper { height: 350rpx; } swiper image { height: 100%; width: 100%; } /* 九宫格 */ .grid-list { display: flex; flex-wrap: wrap; /* 自动换行 */ border-top: 1rpx solid #efefef ; border-left: 1rpx solid #efefef ; } .grid-item { width: 33.33%; height: 200rpx; display: flex; flex-direction: column; /* 纵向排列 */ align-items: center; /* 居中对齐 */ justify-content: center; /* 居中 */ border-right: 1rpx solid #efefef ; border-bottom: 1rpx solid #efefef ; box-sizing: border-box; } .grid-item image { width: 60rpx; height: 60rpx; } .grid-item text { font-size: 24rpx; margin: 10rpx; } .img-box { display: flex; padding: 20rpx 10rpx; justify-content: space-around; } .img-box image { width: 45%; }js// pages/home/home.js Page({ /** * 页面的初始数据 */ data: { //存放轮播图 swiperList: [], //存放九宫格类别数据 gridList: [] }, //获取轮播图地址 getSwiperList(){ wx.request({ url: 'https://www.escook.cn/slides', method: 'GET', success: (res) => { console.log(res) this.setData({ swiperList: res.data }) } }) }, //获取九宫格类别数据 getGridList() { wx.request({ url: 'https://www.escook.cn/categories', method: 'GET', success: (res) => { console.log(res) this.setData({ gridList: res.data }) } }) }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { this.getSwiperList() this.getGridList() } })
微信小程序 - 04_wxss模板样式和全局配置（模板样式_rpx_@import样式导入、全局配置_window_tabBar）
wxss模板样式01 什么是wxsswxss是一套样式语言，用于美化wxml的组件样式，类似于网页开发的css1.1wxss和css的关系wxss具有css大部分特性，同时wxss还对css进行了扩充和修改，以适应微信小程序的开发。wxss扩展有：rpx尺寸单位@import样式导入1.1.1 什么是rpxrpx是微信小程序独有的，用来解决屏幕适配的尺寸单位实现原理：rpx把所有设备的屏幕，在宽度上等分为750份（当前屏幕的总宽度为750rpx）在小设备上，1rpx代表的宽度较小在大设备上，1rpx代表的宽度较大1.2 样式导入使用wxss提供的@import语法，可以导入外联的样式表@import后跟需要导入的外联样式表的相对路径，用;表示语句结束/* common样式 */ .username { color: red; }/* 导入样式 */ @import "/common/common.wxss";02 全局样式和局部样式2.1 全局样式定义在app.wxss中的样式为全局样式，作用于每一个页面2.2 局部样式在页面的.wxss文件中定义的样式为局部样式，只作用于当前页面注意：当局部样式和全局样式冲突时，根据就近原则，局部样式会覆盖全局样式当局部样式的权重大于或等于全局样式的权重时，才会覆盖全局样式全局配置01 全局配置文件常用配置项小程序根目录下的app.json文件是小程序的全局配置文件。常用的配置项如下：pages记录当前小程序所有页面的存放路径window全局设置小程序窗口的外观tabBar设置小程序底部的tabBar效果style是否启用新版的组件样式1.1 window "window":{ "navigationBarBackgroundColor": "#025D54", "navigationBarTitleText": "在线测试", "navigationBarTextStyle":"white", "enablePullDownRefresh": true, "backgroundColor": "#efefef", "backgroundTextStyle":"dark", "onReachBottomDistance": 50 },1.1.1 组成部分1.1.2 常用配置项属性名类型默认值说明navigationBarTitleTextString字符串导航栏标题文字内容navigationBarBackgroundColorHexColor#000000导航栏背景颜色，如#000000navigationBarTextStyleStringwhite导航栏标题颜色，仅支持black / whitebackgroundColorHexColorf#fffff窗口的背景色backgroundTextStyleStringdark下拉loading的样式，仅支持dark / lightenablePullDowntRefreshBooleanfalse是否全局开启下拉刷新onReachBottomDistanceNumber50页面上拉触底事件触发时距页面底部距离，单位为px设置路径：app.json->window1.1.3 全局开启下拉刷新功能通过手指在屏幕上的下拉滑动操作，从而重新加载页面数据的行为设置路径：app.json->window->enablePullDownRefresh设置为true注意：在app.json开启下拉刷新功能后，会作用于每个小程序页面1.1.4 设置下拉刷新背景色当全局开启下拉刷新功能之后，默认的窗口背景为白色。设置路径：app.json->window->backgroundColor16进制颜色值（#efefef）1.1.5 设置下拉刷新时loading的样式当全局开启下拉刷新功能之后，默认窗口的loading样式为白色。设置路径：app.json->window->backgroundTextStyle为dark注意：backgroundTextStyle可选值只有dark和light1.1.6 设置上拉触底的距离通过手指在屏幕上的上拉滑动操作，从而加载更多数据的行为。设置路径：app.json->window->onReachBottomDistance设置新的值注意：默认距离为50px，如果没有特殊需求，建议使用默认值1.2 tabBartabBar是移动端常见的页面效果，用于实现多页面的快速切换。通常分为：底部tabBar顶部tabBar注意：tabBar中只能配置最少2个，最多5个tab页签当渲染顶部tabBar时，不显示icon，只显示文本1.2.1 tabBar的6个组成部分backgroundColor：tabBar 的背景色selectedlconPath：选中时的图片路径borderStyle：tabBar上边框的颜色iconPath：未选中时的图片路径selectedColor： tab上的文字选中时的颜色color：tab上文字的默认(未选中)颜色1.2.2 tabBar结点配置选项属性类型必填默认值描述listArray是 tab页签的列表，最少2个，最多5个positonString否bottomtabBar的位置，仅支持bottom/topborderStyleString否blacktabBar上边框的颜色，仅支持black/whitecolorHexColor否 tab上文字的默认(未选中)颜色selectedColorHexColor否 tab上的文字选中时的颜色backgroundColorHexColor否 tabBar的背景色1.2.3 每个tab项的配置选项属性类型必填描述pagePathString是页面路径，页面必须在pages中预先定义textString是tab上显示的文字iconPathString否未选中时的图标路径;当postion为top时，不显示iconselectedIconPathString否选中时的图标路径;当postion 为top时，不显示icon1.2.4 实例"tabBar": { "list": [ { "pagePath": "pages/home/home", "text": "home", "iconPath": "images/home.png", "selectedIconPath": "images/home-active.png" }, { "pagePath": "pages/message/message", "text": "message", "iconPath": "images/message.png", "selectedIconPath": "images/message-active.png" }, { "pagePath": "pages/contact/contact", "text": "contact", "iconPath": "images/contact.png", "selectedIconPath": "images/contact-active.png" } ] }1.3 页面配置小程序中，每个页面都有自己的.json配置问价你，用来对当前页面的窗口外观、页面效果等进行配置。1.3.1 页面配置和全局配置的关系小程序中，app.json中的window节点，可以全局配置小程序中每个页面的窗口表现。某些小程序页面想要拥有特殊的窗口表现，此时，“页面级别的.json配置文件”就可以实现这种需求。注意：当页面配置与全局配置冲突时，根据就近原则，最终效果以页面配置为准。1.3.2 页面配置中常用的配置项属性类型默认值描述navigationBarBackgroundColorHexColor#000000当前页面导航栏背景颜色，如#000000navigationBarTextStyleStringwhite当前页面导航栏标题颜色，仅支持black / whitenavigationBarTitleTextString 当前页面导航栏标题文字内容backgroundColorHexColor#ffffff当前页面窗口的背景色backgroundTextStyleStringdark当前页面下拉loading的样式，仅支持dark / lightenablePullDownRefreshBooleanfalse是否为当前页面开启下拉刷新的效果onReachBottomDistanceNumber50页面上拉触底事件触发时距页面底部距离，单位为pxenablePullDownRefresh：一般全局不开启，在特定有需求的页面开启
微信小程序 - 03_wxml模板语法（数据绑定_{{data}}、事件绑定_bindtap_bindimput、事件传参、条件渲染_wx:if_hidden、列表渲染_wx:for_wx:key）
wxml模板语法01 数据绑定mustache语法，双大括号绑定数据，类似于view中的插值应用场景：绑定内容绑定属性运算（三元运算、算数运算） <view>{{info}}</view> <image src="{{imgSrc}}" mode="widthFix"></image> <view>{{randomNum1 > 5 ? "数字大于5":"数字小于5"}}</view> <view>100以内的小数：{{randomNum2 * 100}}</view>data: { info: "data test", imgSrc: "https://www.baidu.com/img/pc_9c5c85e6b953f1d172e1ed6821618b91.png", randomNum1: Math.random() * 10, randomNum2: Math.random().toFixed(2) //保留2位小数 }02 事件绑定事件是渲染层到逻辑层的通讯方式。通过事件可以将用户在渲染层产生的行为，反馈到逻辑层进行业务的处理。2.1 小程序中常用的事件类型绑定方式事件描述tapbindtap或bind:tap手指触摸后马上离开，类似于html中的click事件inputbindinput或bind:input文本框的输入事件changebindchange或bind:change状态改变时触发2.2 事件对象的属性列表当事件回调触发的时候，会收到一个事件对象event，详细属性如下属性类型说明typeString时间类型timeStampInteger页面打开到触发时间所经过的毫秒数targetObject触发时间的组件的一些属性值集合currentTargetObject当前组件的一些属性值集合detailObject额外的信息touchesArray触摸事件，当前停留在屏幕中的触摸点信息的数组changedTouchesArray触摸事件，当前变化的触摸点信息的数组target和currentTarget的区别target是触发该时间的源头组件，而currentTarget是当前时间所绑定的组件。如下图，点击内部的按钮时，点击事件以冒泡的方式向外扩散，也会触发外层view的tap事件处理函数。此时，对于外层的view来说：e.target指向的是触发事件的源头组件，e.target是内部的按钮组件。e.currentTarget指向的是当前正在触发时间的那个组件，e.currentTarget是当前的view组件。2.3 bindtap语法格式在小程序中，不存在html中的onclick鼠标点击事件，而是通过tap事件来响应用户的触摸行为。通过bindtap，可以为组件绑定tap触摸事件。在页面的.js文件中定义对应的事件处理函数，事件参数通过形参event（一般简写成e）来接收 按钮 //定义按钮的事件处理函数 btnTapHandler(e) { console.log("test"); console.log(e) },2.3.1 在事件处理函数中为data中的数据赋值通过调用this.setData(dataObject)方法，给页面data中的数据重新赋值。Page({ data: { count: 0 }, //给data数据重新赋值 changeCount(e) { this.setData({ count: this.data.count + 1 }) } }2.3.2 事件传参小程序中的事件传参比较特殊，不能绑定事件的同时为事件处理函数传递参数。小程序会把bindtap的属性值，统一当做事件名来处理，相当于调用一个名称为btnHandler(123)的事件处理函数。 事件传参 小程序中的传参，可以为组件提供data-*自定义属性传参，其中*代表的是参数的名字info会被解析为参数的名字。数值2会被解析为参数的值。 事件传参 在事件处理函数中，通过event.target.dataset.参数名即可获取到具体参数的值。 事件传参 Page({ data: { count: 0 }, //事件传参 event.target.dataset.参数名，注意data-testInfo会被解析为testinfo，大写自动变小写 btnHandler(event) { console.log(event.target.dataset.testinfo); this.setData({ count: this.data.count + event.target.dataset.testinfo }) } }2.4 bindinput语法格式小程序中，通过input事件来响应文本框的输入事件通过bindinput，可以为文本框绑定输入事件在页面的.js文件中定义事件处理函数 Page({ //input inputHandler(e) { //e.detail.value是变化过后，文本框最新的值 console.log(e.detail.value) } }2.4.1 实现文本框和data之间的数据同步实现步骤定义数据渲染结构美化样式绑定input事件处理函数 Page({ data: { msg: "你好，" }, //实现文本框和data之间的数据同步 iptHandler(e) { this.setData({ msg: e.detail.value }); console.log(e.detail.value) } }03 条件渲染3.1 wx:if在小程序中，使用wx:if="{{condition}}"来判断是否需要渲染该代码块。也可以用wx:elif和wx:else来添加else判断 <view wx:if="{{type === 1}}">男</view> <view wx:elif="{{type === 2}}">女</view> <view wx:else>保密</view>3.2 结合block标签使用wx:if如果要一次性控制多个组件的展示与隐藏，可以使用一个<block></block>标签将多个组件包装起来，并在<block>标签上使用wx:if控制属性。注意：<block>并不是一个组件，它知识一个包裹性质的容器，不会在页面中做任何渲染。<!-- <block></block>标签的使用 --> <block wx:if="{{true}}"> <view>view1</view> <view>view2</view> </block>3.3 hidden在小程序中，直接使用hidden="{{condition}}"也能控制元素的显示与隐藏。 <view hidden="{{false}}"> 条件为true，隐藏；条件为false，显示</view>3.4 wx:if与hidden的对比运行方式不同wx:if以动态创建和移除元素的方式，控制元素的展示与隐藏hidden以切换样式的方式（display:none/block），控制元素的显示与隐藏使用建议频繁切换时，建议使用hidden控制条件复杂时，建议使用wx:if搭配wx:elif和wx:else进行展示与隐藏的切换04 列表渲染4.1 wx:for通过wx:for可以根据指定的数组，循环渲染重复的组件结构。当前循环项的索引用index表示。当前循环项用item表示。 <view wx:for="{{arr1}}"> 索引是{{index}}，循环项是{{item}} </view>4.1.1 手动指定索引和当前想的变量名*使用wx:for-index可以指定当前循环项的索引的变量名使用wx:for-item可以指定当前项的变量名<view wx:for="{{arr1}}" wx:for-index="idx" wx:for-item="itemName"> 索引是{{idx}}，循环项是{{itemName}} </view>4.1.2 wx:key的使用类似于Vue列表渲染中的:key，小程序在实现列表渲染时，也建议为渲染出来的列表项指定唯一的key值，从而提高渲染的效率 <view wx:for="{{userList}}" wx:key="id">{{item.name}}</view>
微信小程序 - 02_常用组件（view/scroll-view、swiper/swiper-item、text/rich-text、button、image、小程序三大类API）
组件介绍01 小程序中组件的分类视图容器基础内容表单组件导航组件媒体组件map地图组件canvas画布组件开放能力无障碍访问1.1 视图容器view普通视图区域类似于html中的div，是一个块级元素常用来实现页面的布局效果scroll-view可滚动的视图区域常用来实现滚动列表效果swiper和swiper-item轮播图容器组件和轮播图item组件1.1.1 view <view class="container1"> <view>A</view> <view>B</view> <view>C</view> </view>/* view */ /* 设置子元素样式 */ .container1 view{ width: 100px; height: 100px; text-align: center; line-height: 100px; } .container1 view:nth-child(1) { background-color: lightgreen; } .container1 view:nth-child(2) { background-color: lightyellow; } .container1 view:nth-child(3) { background-color: lightpink; } /* 设置父元素样式 */ .container1 { display: flex;/* 弹性布局*/ justify-content: space-around;/* 空间环绕*/ }1.1.2 scroll-viewwxml <scroll-view class="container2" scroll-y> <view>A</view> <view>B</view> <view>C</view> </scroll-view>wxss/* scroll-view */ /* 设置父元素样式，给scroll-view一个固定高度 */ .container2{ border: 1px solid red; width: 100px; height: 120px; } .container2 view{ width: 100px; height: 100px; text-align: center; line-height: 100px; } .container2 view:nth-child(1) { background-color: lightgreen; } .container2 view:nth-child(2) { background-color: lightyellow; } .container2 view:nth-child(3) { background-color: lightpink; }1.1.3 swiper/swiper-itemwxml <swiper class="swiper-container" indicator-dots="true"> <swiper-item> <view class="item">A</view> </swiper-item> <swiper-item> <view class="item">B</view> </swiper-item> <swiper-item> <view class="item">C</view> </swiper-item> </swiper>wxss/* swiper */ .swiper-container { height: 150px; } .item { height: 100%; text-align: center; line-height: 150px; /*行高*/ } /* 直接标签选择器进行选择 */ /* swiper-item:nth-child(1) .item{ background-color: lightgreen; } swiper-item:nth-child(2) .item{ background-color: lightskyblue; } swiper-item:nth-child(3) .item{ background-color: lightcoral; } */ /* 类选择器下 再选标签选择器 */ .swiper-container swiper-item:nth-child(1) .item{ background-color: lightgreen; } .swiper-container swiper-item:nth-child(2) .item{ background-color: lightskyblue; } .swiper-container swiper-item:nth-child(3) .item{ background-color: lightcoral; }swiper常用属性属性类型默认值说明indicator-dotsbooleanfalse是否显示面板指示点indicator-colorcolorrgba(0,0,0,.3)指示点颜色indicator-active-colorcolor#000000当前选中的指示点颜色autoplaybooleanfalse是否自动切换intervalnumber5000自动切换时间间隔circularbooleanfalse是否采用衔接滑动1.1.4 text/ rich-texttext文本组件类似于HTML中的span标签，是一个行内元素wxml <view> 不可选文字 </view> <view> <text user-select="true">选中文字</text> </view>rich-text富文本组件支持把html字符串渲染为wxml结构wxml<rich-text nodes=" 标题 "></rich-text>1.2 其他常用组件button按钮组件功能比html中的button按钮丰富通过open-type属性可以调用微信提供的各种功能（客服、转发、获取用户授权、获取用户信息等）image图片组件image组件默认宽度为300px，高度为240pxnavigator页面导航组件类似于html中的a链接1.2.1 buttonwxml <view class="button">-------通过type指定按钮类型-------</view> 默认按钮 主要按钮 警告按钮 <view class="button">-------size="mini"小尺寸按钮-------</view> 默认按钮 主要按钮 警告按钮 <view class="button">-------plain镂空按钮-------</view> 默认按钮 主要按钮 警告按钮 1.2.2 image<image src="" mode="widthFix"></image>mode属性image组件的mode属性用来指定图片的裁剪和缩放模式，常用的mode属性如下mode值说明scaleToFill(默认)缩放模式，不保持纵横比缩放图片，使图片的宽高完全拉伸至填满image元素aspectFit缩放模式，保持纵横比缩放图片，使图片的长边能完全显示出来。可以完整地将图片显示出来aspectFill缩放模式，保持纵横比缩放图片，只保证图片的短边能完全显示出来。图片通常在水平或垂直方向是完整的，另一个方向会发生截取widthFix缩放模式，宽度不变，高度自动变化，保持原图宽高比不变heightFix缩放模式，高度不限，宽度自动变化，保持原图宽高比不变02 API2.1 小程序API概述小程序中的API是由宿主环境提供的，通过这些丰富的小程序API，开发者可以方便地调用微信提供的能力，例如：获取用户信息、本地存储、支付功能。2.2 小程序的3大分类事件监听API特点：以on开头，用来监听某些时间的触发举例：wx.onWindowResize(function callback)监听窗口尺寸变化的事件同步API特点1：以Sync结尾的API都是同步API特点2：同步API的执行结果，可以通过函数返回值直接获取，如果执行出错会抛出异常举例：wx.setStorageSync('key', 'value')向本地存储中写入内容异步API特点：类似于jQuery中的$.ajax(option)函数，需要通过success、fail、complete接收调用的结果举例：wx.request()发起网络数据请求，通过success回调函数接收数据
微信小程序 - 01_小程序基础（项目结构、宿主环境）
微信小程序基础01 项目结构其中最重要的三个文件：pages app.json app.js推荐小程序的所有页面都存放在pages目录中，以单独的文件夹形式存在1.1 json配置文件的作用通过不同的json文件，可以对小程序项目进行不同级别的配置。小程序中有4中配置文件：app.jsonproject.config.jsonsitemap.json每个pages文件夹下的.json1.1.2 app.json全局配置，包括小程序的所有页面路径、窗口外观、界面表现、底部tab等1.1.3 project.config.json项目配置问价你，记录对小程序开发工具所作的个性化配置setting：保存了编译相关的配置projectname：项目名称appid：小程序的账号id1.1.4 sitemap.json小程序内搜索，类似于PC网页的SEO，该json用来配置小程序页面是否允许微信索引。1.1.5 页面的json文件对本页面的窗口外观进行配置，会覆盖app.json的window中相同的配置项。1.1.6 新增页面在app.json中的pages数组下，新增一条路径，保存文件后即可新增。1.1.7 修改项目首页只需要调整app.json ->pages数组中页面路径的前后顺序，即可修改项目的首页。小程序会把排在第一位的页面，当做项目首页进行渲染。1.2 wxml模板小程序框架下的标签语言，用来构建小程序页面的结构，类似于HTML。1.2.1 wxml与html的区别标签名称不同html：div, span, img, awxml: view, text, image, navigator属性节点不同超链接<navigator url="/pages/home/home"></navigator>提供了类似于vue中的模板语法数据绑定列表渲染条件渲染1.3 wxss样式微信小程序的一套样式语言，类似网页开发中的css1.3.1 wxss和css的区别新增了rpx尺寸单位css中需要手动进行像素单位的换算，如remwxss在底层支持新的尺寸单位rpx,在不同大小的屏幕上会进行自动换算提供了全局的样式和局部样式项目根目录下的app.wxss会作用于所有小程序页面局部页面的.wxss仅对当前页面生效wxss仅支持部分的css选择器.class #idelement并集选择器、后代选择器::after ::before等伪类选择器1.4 js逻辑交互通过js文件来处理用户的操作，比如响应用户的点击、获取用户的id等1.4.1 小程序中js文件app.js：整个小程序的入口文件，通过调用App()函数来启动整个小程序。页面的.js:页面的入口文件，通过调用Page()函数来创建并运行页面。普通的.js文件：普通的功能模块文件，用来封装公共的函数或属性供页面使用。02 宿主环境宿主环境是指程序运行所必须的依赖环境。手机微信是小程序的宿主环境。2.1 小程序宿主环境包含的内容2.1.1 通信模型wxml和wxss是渲染层js是逻辑层2.1.2 运行机制小程序启动的过程：把小程序的代码包下载到本地解析app.json全局配置文件执行app.js小程序入口文件，调用App()创建小程序实例渲染小程序首页小程序启动完成页面渲染的过程加载解析页面的json配置文件加载页面的wxml和wxss执行页面的js文件，调用Page()创建页面实例页面渲染完成
牛客数据库SQL实战 51-60（substr切割字符串、group_concat组拼接、limit_offset分页、exists条件成立判断、case分支、表的复用）
51* 获取Employees中的first_name51.1 题目描述获取Employees中的first_name，查询按照first_name最后两个字母，按照升序进行排列CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` char(1) NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`));输出格式：first_nameChirstianTzvetanBezalelDuangkaewGeorgiKyoichiAnnekeSumantMaryPartoSaniya51.2 题解 substrselect first_name from employees order by substr(first_name, length(first_name)-1,length(first_name))本题考查 substr(X,Y,Z) 或substr(X,Y)函数的使用。其中X是要截取的字符串。Y是字符串的起始位置==（注意第一个字符的位置为1，而不为0）==，取值范围是±(1~length(X))，当Y等于length(X)时，则截取最后一个字符；当Y等于负整数-n时，则从倒数第n个字符处截取。Z是要截取字符串的长度，取值范围是正整数，若Z省略，则从Y处一直截取到字符串末尾；若Z大于剩下的字符串长度，也是截取到字符串末尾为止。52 按照dept_no进行汇总52.1 题目描述按照dept_no进行汇总，属于同一个部门的emp_no按照逗号进行连接，结果给出dept_no以及连接出的结果employeesCREATE TABLE `dept_emp` ( `emp_no` int(11) NOT NULL, `dept_no` char(4) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`dept_no`));输出格式:dept_noemployeesd00110001,10002d00210006d00310005d00410003,10004d00510007,10008,10010d00610009,1001052.2 题解 group_concat(字段名,分隔符)select dept_no ,group_concat(emp_no, ",") from dept_emp group by dept_no本题要用到SQLite的聚合函数group_concat(X,Y)，其中X是要连接的字段，Y是连接时用的符号，可省略，默认为逗号。此函数==必须与 GROUP BY 配合使用==。此题以 dept_no 作为分组，将每个分组中不同的emp_no用逗号连接起来（即可省略Y）。53 查找排除当前最大、最小salary之后的员工的平均工资avg_salary53.1 题目描述查找排除当前最大、最小salary之后的员工的平均工资avg_salary。CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL, `salary` int(11) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`from_date`));输出格式:avg_salary69462.555555555653.2 题解正常逻辑，OJ过不了select avg(salary) as avg_salary from salaries where salary not in( -- 1 找到最高工资和最小工资的salary (select max(salary) from salaries where to_date = '9999-01-01') -- OJ中需要把时间条件删除，才能过 ,(select min(salary) from salaries where to_date = '9999-01-01') -- OJ中需要把时间条件删除，才能过 ) and to_date = '9999-01-01' OJ通过题解select avg(salary) as avg_salary from salaries where salary not in( -- 1 找到最高工资和最小工资的salary (select max(salary) from salaries ) ,(select min(salary) from salaries ) ) and to_date = '9999-01-01'54 分页查询employees表，每5行一页，返回第2页的数据54.1 题目描述分页查询employees表，每5行一页，返回第2页的数据CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` char(1) NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`));54.2 题解 limit offset方法一：利用 LIMIT 和 OFFSET 关键字。LIMIT 后的数字代表返回几条记录，OFFSET 后的数字代表从第几条记录开始返回==（第一条记录序号为0）==，也可理解为跳过多少条记录后开始返回。select * from employees limit 5 -- 返回5条数据 offset 5 -- 从第6条数据开始返回只利用 LIMIT 关键字。注意：在 LIMIT X,Y 中，Y代表返回几条记录，X代表从第几条记录开始返回（第一条记录序号为0），切勿记反。select * from employees limit 5,5 -- 从第6条记录开始，返回4条数据（第1条数据是第0条数据）55 获取所有员工的emp_no55.1 题目描述获取所有员工的emp_no、部门编号dept_no以及对应的bonus类型btype和received ，没有分配具体的员工不显示CREATE TABLE `dept_emp` ( `emp_no` int(11) NOT NULL, `dept_no` char(4) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`dept_no`)); CREATE TABLE `dept_manager` ( `dept_no` char(4) NOT NULL, `emp_no` int(11) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`dept_no`)); CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` char(1) NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`)); CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL, `salary` int(11) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`from_date`)); create table emp_bonus( emp_no int not null, received datetime not null, btype smallint not null);输出格式:e.emp_nodept_nobtypereceived10001d00112010-01-0110002d00122010-10-0110003d00432011-12-0310004d00412010-01-0110005d003 10006d002 10007d005 10008d005 10009d006 10010d005 10010d006 55.2 题解select d.emp_no ,d.dept_no ,e.btype ,e.recevied -- 这个字段是题目打错的，我日，建议击毙 from dept_emp d left join emp_bonus e on d.emp_no = e.emp_no正常题解：本题严谨的思路为，先将 employees与dept_emp 用 INNER JOIN 连接，挑选出分配了部门的员工，再用 LEFT JOIN 连接 emp_bonus（在前面的题中可看到此表），分配了奖金的员工显示奖金类型和授予时间，没分配奖金的员工则不显示。SELECT em.emp_no , de.dept_no , eb.btype , eb.recevied FROM employees AS em INNER JOIN dept_emp AS de ON em.emp_no = de.emp_no LEFT JOIN emp_bonus AS eb ON de.emp_no = eb.emp_no56* 使用含有关键字exists查找未分配具体部门的员工的所有信息。56.1 题目描述使用含有关键字exists查找未分配具体部门的员工的所有信息。CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` char(1) NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`)); CREATE TABLE `dept_emp` ( `emp_no` int(11) NOT NULL, `dept_no` char(4) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`dept_no`));输出格式:emp_nobirth_datefirst_namelast_namegenderhire_date100111953-11-07MarySluisF1990-01-2256.2 题解 exists不使用existsselect * from employees where emp_no not in ( select emp_no from dept_emp d )使用existsselect * from employees where not exists ( select emp_no from dept_emp where emp_no = employees.emp_no -- 外面的表一定要和存在条件的子表差生关联 )SELECT * FROM employees WHERE emp_no NOT exists ( SELECT emp_no FROM dept_emp) -- 过不了，在 employees 中没有一条记录能使 (SELECT emp_no FROM dept_emp) 不成立。(SELECT emp_no FROM dept_emp) 没有跟 emplotees 产生联系，因为无论选中 employees 中的哪条记录，(SELECT emp_no FROM dept_emp) 都成立57 获取employees中的行数据，且这些行也存在于emp_v中57.1 题目描述存在如下的视图：create view emp_v as select * from employees where emp_no >10005; CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` char(1) NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`));获取employees中的行数据，且这些行也存在于emp_v中。注意不能使用intersect关键字。输出格式:emp_nobirth_datefirst_namelast_namegenderhire_date100061953-04-20AnnekePreusigF1989-06-02100071957-05-23TzvetanZielinskiF1989-02-10100081958-02-19SaniyaKalloufiM1994-09-15100091952-04-19SumantPeacF1985-02-18100101963-06-01DuangkaewPiveteauF1989-08-24100111953-11-07MarySluisF1990-01-2257.2 题解-- 这题真的智障 select * from emp_v58 获取有奖金的员工相关信息。58.1 题目描述获取有奖金的员工相关信息。CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` char(1) NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`)); CREATE TABLE `dept_emp` ( `emp_no` int(11) NOT NULL, `dept_no` char(4) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`dept_no`)); create table emp_bonus( emp_no int not null, recevied datetime not null, btype smallint not null); CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL, `salary` int(11) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`from_date`));给出emp_no、first_name、last_name、奖金类型btype、对应的当前薪水情况salary以及奖金金额bonus。 bonus类型btype为1其奖金为薪水salary的10%，btype为2其奖金为薪水的20%，其他类型均为薪水的30%。 当前薪水表示to_date='9999-01-01'输出格式:emp_nofirst_namelast_namebtypesalarybonus10001GeorgiFacello1889588895.810002BezalelSimmel27252714505.410003PartoBamford34331112993.310004ChirstianKoblick1740577405.758.2 题解 case..when..then..else..endselect em.emp_no ,em.first_name ,em.last_name ,eb.btype ,s.salary ,(case eb.btype when 1 then 0.1 * s.salary when 2 then 0.2 * s.salary else 0.3 * s.salary end ) as bonus from employees em left join emp_bonus eb on em.emp_no = eb.emp_no left join salaries s on eb.emp_no = s.emp_no where s.to_date = '9999-01-01'59* 统计salary的累计和running_total59.1 题目描述按照salary的累计和running_total，其中running_total为前两个员工的salary累计和，其他以此类推。 具体结果如下Demo展示。。CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL, `salary` int(11) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`from_date`));输出格式:emp_nosalaryrunning_total10001889588895810002725271614851000343311204796100047405727885310005946923735451000643311416856100078807050492610009954096003351001094409694744100112582872057259.2 题解本题的思路为复用 salaries 表进行子查询，最后以 s1.emp_no 排序输出求和结果。 1、输出的第三个字段，是由一个 SELECT 子查询构成。将子查询内复用的 salaries 表记为 s2，主查询的 salaries 表记为 s1，当主查询的 s1.emp_no 确定时，对子查询中不大于 s1.emp_no 的 s2.emp_no 所对应的薪水求和 2、注意是对员工当前的薪水求和，所以在主查询和子查询内都要加限定条件 to_date = '9999-01-01'select s2.emp_no ,s2.salary ,( select sum(salary) from salaries s1 where s1.emp_no <= s2.emp_no -- 复用两张表 and s1.to_date = '9999-01-01' ) as running_total from salaries s2 where s2.to_date = '9999-01-01'60* 对于employees表中，给出奇数行的first_name60.1 题目描述对于employees表中，给出奇数行的first_nameCREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` char(1) NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`));输出格式:first_nameGeorgiChirstianAnnekeTzvetanSaniyaMary60.2 题解SELECT E1.first_name FROM employees E1 WHERE ( SELECT COUNT(*) -- 序号 复用2张表，先对first_name进行排序，count(*)就是序号 FROM employees E2 WHERE E1.first_name >= E2.first_name) % 2 = 1;
牛客数据库SQL实战 41-50（数据替换replace(字段,oldval,newval)、添加主键、获取两表交集intersect、获取字符串长度length(str)）
41 删除emp_no重复的记录，只保留最小的id对应的记录41.1 题目描述删除emp_no重复的记录，只保留最小的id对应的记录。CREATE TABLE IF NOT EXISTS titles_test ( id int(11) not null primary key, emp_no int(11) NOT NULL, title varchar(50) NOT NULL, from_date date NOT NULL, to_date date DEFAULT NULL); insert into titles_test values ('1', '10001', 'Senior Engineer', '1986-06-26', '9999-01-01'), ('2', '10002', 'Staff', '1996-08-03', '9999-01-01'), ('3', '10003', 'Senior Engineer', '1995-12-03', '9999-01-01'), ('4', '10004', 'Senior Engineer', '1995-12-03', '9999-01-01'), ('5', '10001', 'Senior Engineer', '1986-06-26', '9999-01-01'), ('6', '10002', 'Staff', '1996-08-03', '9999-01-01'), ('7', '10003', 'Senior Engineer', '1995-12-03', '9999-01-01');41.2 题解-- 2 删除不在最小id里的数据 delete from titles_test where id not in ( -- 1 先对emp_no进行分组，找到每组的最小id select min(id) id from titles_test group by emp_no)先用 GROUP BY 和 MIN() 选出每个 emp_no 分组中最小的 id，然后用 DELETE FROM ... WHERE ... NOT IN ... 语句删除 “非每个分组最小id对应的所有记录”42 将所有to_date为9999-01-01的全部更新为NULL42.1 题目描述将所有to_date为9999-01-01的全部更新为NULL,且 from_date更新为2001-01-01。CREATE TABLE IF NOT EXISTS titles_test ( id int(11) not null primary key, emp_no int(11) NOT NULL, title varchar(50) NOT NULL, from_date date NOT NULL, to_date date DEFAULT NULL); insert into titles_test values ('1', '10001', 'Senior Engineer', '1986-06-26', '9999-01-01'), ('2', '10002', 'Staff', '1996-08-03', '9999-01-01'), ('3', '10003', 'Senior Engineer', '1995-12-03', '9999-01-01'), ('4', '10004', 'Senior Engineer', '1995-12-03', '9999-01-01'), ('5', '10001', 'Senior Engineer', '1986-06-26', '9999-01-01'), ('6', '10002', 'Staff', '1996-08-03', '9999-01-01'), ('7', '10003', 'Senior Engineer', '1995-12-03', '9999-01-01');42.2 题解update titles_test set to_date = null ,from_date = '2001-01-01' where to_date = '9999-01-01'43* 将id=5以及emp_no=10001的行数据替换成id=5以及emp_no=1000543.1 题目描述将id=5以及emp_no=10001的行数据替换成id=5以及emp_no=10005,其他数据保持不变，使用replace实现。CREATE TABLE IF NOT EXISTS titles_test ( id int(11) not null primary key, emp_no int(11) NOT NULL, title varchar(50) NOT NULL, from_date date NOT NULL, to_date date DEFAULT NULL); insert into titles_test values ('1', '10001', 'Senior Engineer', '1986-06-26', '9999-01-01'), ('2', '10002', 'Staff', '1996-08-03', '9999-01-01'), ('3', '10003', 'Senior Engineer', '1995-12-03', '9999-01-01'), ('4', '10004', 'Senior Engineer', '1995-12-03', '9999-01-01'), ('5', '10001', 'Senior Engineer', '1986-06-26', '9999-01-01'), ('6', '10002', 'Staff', '1996-08-03', '9999-01-01'), ('7', '10003', 'Senior Engineer', '1995-12-03', '9999-01-01');43.2 题解 replace43.2.1 replace intoREPLACE INTO titles_test VALUES (5, 10005, 'Senior Engineer', '1986-06-26', '9999-01-01')全字段更新替换。由于 REPLACE 的新记录中 id=5，与表中的主键 id=5 冲突，故会替换掉表中 id=5 的记录，否则会插入一条新记录（例如新插入的记录 id = 10）。==并且要将所有字段的值写出，否则将置为空==。43.2.2 replace(字段,oldval,newval)update titles_test set emp_no = replace(emp_no, 10001, 10005) where id = 544 将titles_test表名修改为titles_201744.1 题目描述将titles_test表名修改为titles_2017。CREATE TABLE IF NOT EXISTS titles_test ( id int(11) not null primary key, emp_no int(11) NOT NULL, title varchar(50) NOT NULL, from_date date NOT NULL, to_date date DEFAULT NULL); insert into titles_test values ('1', '10001', 'Senior Engineer', '1986-06-26', '9999-01-01'), ('2', '10002', 'Staff', '1996-08-03', '9999-01-01'), ('3', '10003', 'Senior Engineer', '1995-12-03', '9999-01-01'), ('4', '10004', 'Senior Engineer', '1995-12-03', '9999-01-01'), ('5', '10001', 'Senior Engineer', '1986-06-26', '9999-01-01'), ('6', '10002', 'Staff', '1996-08-03', '9999-01-01'), ('7', '10003', 'Senior Engineer', '1995-12-03', '9999-01-01');44.2 题解 rename toalter table titles_test rename to titles_2017mysql只需要rename就行oracle rename user to user_HDU;45* 在audit表上创建外键约束，其emp_no对应employees_test表的主键id45.1 题目描述在audit表上创建外键约束，其emp_no对应employees_test表的主键id。CREATE TABLE employees_test( ID INT PRIMARY KEY NOT NULL, NAME TEXT NOT NULL, AGE INT NOT NULL, ADDRESS CHAR(50), SALARY REAL ); CREATE TABLE audit( EMP_no INT NOT NULL, create_date datetime NOT NULL );45.2 题解（通过题解有问题）DROP TABLE audit; CREATE TABLE audit( EMP_no INT NOT NULL, create_date datetime NOT NULL, FOREIGN KEY(EMP_no) REFERENCES employees_test(ID));正常题解 mysqlalter table audit add foreign key EMP_no references employees_test(id)46 如何获取emp_v和employees有相同的数据no46.1 题目描述存在如下的视图：create view emp_v as select * from employees where emp_no >10005;如何获取emp_v和employees有相同的数据？输出格式:CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` char(1) NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`));输出格式:emp_nobirth_datefirst_namelast_namegenderhire_date100061953-04-20AnnekePreusigF1989-06-02100071957-05-23TzvetanZielinskiF1989-02-10100081958-02-19SaniyaKalloufiM1994-09-15100091952-04-19SumantPeacF1985-02-18100101963-06-01DuangkaewPiveteauF1989-08-24100111953-11-07MarySluisF1990-01-2246.2 题解 intersect求交集select v.emp_no ,v.birth_date ,v.first_name ,v.last_name ,v.gender ,v.hire_date from emp_v v ,employees e where v.emp_no = e.emp_no视图就是从原表中拿出来的数据，直接拿出视图数据也行的，没必要关联视图和原表select * from emp_v intersect select * from employees 可以使用intersect求视图和原表的交集47 将所有获取奖金的员工当前的薪水增加10%47.1 题目描述将所有获取奖金的员工当前的薪水增加10%。create table emp_bonus( emp_no int not null, recevied datetime not null, btype smallint not null); CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL, `salary` int(11) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`from_date`));47.2 题解update salaries set salary = salary * 1.1 where emp_no in ( select emp_no from emp_bonus)48 针对库中的所有表生成select count()对应的SQL语句48.1 题目描述针对库中的所有表生成select count(*)对应的SQL语句CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` char(1) NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`)); create table emp_bonus( emp_no int not null, recevied datetime not null, btype smallint not null); CREATE TABLE `dept_emp` ( `emp_no` int(11) NOT NULL, `dept_no` char(4) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`dept_no`)); CREATE TABLE `dept_manager` ( `dept_no` char(4) NOT NULL, `emp_no` int(11) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`dept_no`)); CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL, `salary` int(11) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`from_date`));输出格式cntsselect count(*) from employees;select count(*) from departments;select count(*) from dept_emp;select count(*) from dept_manager;select count(*) from salaries;select count(*) from titles;select count(*) from emp_bonus;48.2 题解 sqlite_masterselect 'select count(*) from ' || name || ';' as cnts from sqlite_master where type = 'table'本题主要有以下两个关键点：在 SQLite 系统表 sqlite_master 中可以获得所有表的索引，其中字段 name 是所有表的名字，而且对于自己创建的表而言，字段 type 永远是 'table'，详情可参考： http://blog.csdn.net/xingfeng0501/article/details/7804378在 SQLite 中用 || 符号连接字符串49 将employees表中的所有员工的last_name和first_name通过(')连接起来49.1 题目描述将employees表中的所有员工的last_name和first_name通过(')连接起来。CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` char(1) NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`));输出格式:nameFacello'GeorgiSimmel'BezalelBamford'PartoKoblick'ChirstianMaliniak'KyoichiPreusig'AnnekeZielinski'TzvetanKalloufi'SaniyaPeac'SumantPiveteau'DuangkaewSluis'Mary49.2 题解49.2 .1 sqlite (||)select last_name || "'" || first_name from employees49.2.2 mysql (concat)select concat(last_name,"'",first_name) from employees50* 查找字符串'10,A,B' 中逗号','出现的次数cnt50.1 题目描述查找字符串'10,A,B' 中逗号','出现的次数cnt。50.2 题解select length('10,A,B') - length(replace('10,A,B',',','')) as cnt解题思路：①巧用length函数和replace，length函数计算字符串的长度，length("10,A,B")算出整个字符串的长度。 ②使用replace将 , 替换为空，那么整个字符串减少的长度等于 , 的长度，两者相减就是 , 出现的次数。