最近在写一些东西的时候,有一些数据是使用XML来保存的,而其中有一些数据是一段HTML文本,一开始觉得没什么问题,当把这些HTML放到XML的一个结点的时候,才发现数据已经变样了。 原来在XML中有5个预定义的实体引用: < < 小于 > > 大于 & & 和号 ' ' 省略号 " " 引号 参考:http://www.w3school.com.cn/xml/xml_cdata.asp HTML和XML差不多,是由一些预定义的标签组成的,所以包含了大量的"<"、">"符号。 所以有两个方法可以将HTML插入到XML中: 1、将HTML进行一次转换,将所有的"<"、">"等预定义符号转换成相应的实体引用。 2、将HTML包在CDATA中。 很明显,第一种方法比较烦琐,序列化数据的时候和把序列化的时候都需要进行转换。 第二种方法则很简单了。 写了一个方法:
1 2 3 4 5 6 7 |
public class XMLUtil { public static function CDATA(data:String):XML { return new XML( " <![CDATA[ " + data + " ]]\> " ); } } |
可以这样使用:
1 |
var node:XML = < node > {XMLUtil.CDATA( " 字符串 " )} </ node > ; |
参考: http://cookbooks.adobe.com/post_Create_CDATA_tags_between_XML_nodes_using_AS-6142.html 转载于:https://www.cnblogs.com/yili16438/archive/2011/04/13/2015369.html from:https://blog.csdn.net/weixin_30556161/article/details/97479499
View Details语法: select * from table_name force index (index_name) where conditions; 使用 关键字 force
1 2 3 |
#########mysql强制使用指定索引查询 SELECT * FROM `yrd_pay_flow` FORCE INDEX(`idx_trxn_date`) WHERE trxn_date > '2017-08-12 59:59:59' AND trxn_date < '2016-06-23 00:00:00'; |
from:https://www.cnblogs.com/pc-boke/articles/9916986.html
View Details缘由 我们在使用spring boot开发的服务中,一般会选择打包成单体的fatjar来发布服务,这在传统的部署方式下是非常方便的,但是当我们选择使用docker这种容器化的方式来部署应用的时候,却有一点点的不便之处,因为这个单体的jar一般都比较大,每次镜像push到仓库和从仓库拉取都需要比较长的时间。 原因是什么了? docker的一大特色就是镜像的存储是分层的,参考下面这张官图 我们在Dockerfile中的每一个指令会对应到镜像的每一层,docker在更新镜像时,只会推送变更过的层,当它计算出来这一层的摘要和之前的版本一致时,会复用上一次打包镜像时的缓存,会极大的提高打包镜像以及镜像push/pull操作的速度。 那么问题来了,当我们springboot打包出来的单体jar的时候,每次编译这个jar都会发生变化,对应的存储层也会发生变化,push和pull操作时都需要重新推送,而且这个jar一般都不小,一个典型的应用会在100M左右,对应用部署和发布的速度会有比较大的影响。 稍作思考,很容易就能发现这个肥大的jar文件里面,大部分其实都是固定不变的各种依赖库,我们真正每次编译会变化的业务代码部分其实很小很小,可能也就只有几百KB,只要能将这两部分分离,变成docker镜像中的两层,一定能极大的提升镜像发布的速度 牛刀小试 首先拿来动手尝试的是一个springboot admin的项目,项目的结构是这样的: 使用最常见的打包方式:
1 2 3 4 |
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> |
会生成一个32M的jar文件,优化之前的Dockerfile非常简单:
1 2 3 4 5 6 7 8 |
FROM openjdk:11.0.5-stretch as builder RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN echo 'Asia/Shanghai' >/etc/timezone VOLUME /tmp COPY target/*.jar ./app.jar CMD ["/bin/bash", "-c", "java -jar -server app.jar"] EXPOSE 8080 |
可以看到这种方式在构建v2版本的镜像时,会重新copy整个完整的jar 如果要拆开这个单体的jar,有两种方式,一是修改mvn打包的配置,将lib包放在独立的文件夹下,在这里我们考虑到项目众多,尽量减少修改,选择了在Docker打包镜像时,解压打包出来的jar包,将其中的内容分开来copy,修改后的Dockerfile如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#采用docker的分阶段构建方式,第一阶段负责解压jar包 FROM openjdk:11.0.5-stretch as builder RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN echo 'Asia/Shanghai' >/etc/timezone VOLUME /tmp WORKDIR /target ADD target/*.jar app.jar RUN jar xf app.jar #这里分别copy解压后的内容 FROM openjdk:11.0.5-stretch VOLUME /tmp WORKDIR /app COPY --from=builder target/BOOT-INF/lib ./lib COPY --from=builder target/org/ ./org COPY --from=builder target/META-INF/ ./META-INF COPY --from=builder target/BOOT-INF/classes ./classes CMD ["/bin/bash", "-c", "java -cp .:./classes/:./lib/* -server org.springframework.boot.loader.JarLauncher"] EXPOSE 8080 |
看看修改后的效果: 在copy lib目录时,是直接using cache的。来看看push的时候效果对比 首先是优化前的push: 可以看到在push v2的时候还是会push一个33MB的层,虽然其实我们一行代码没有修改。 然后是优化后的: 可以看到这一次仅仅只推送了一个13KB的层,推送的速度快了非常多,同理也可以想象的到,我们在拉取镜像更新版本时速度会快很多。 路遇荆棘 在针对springboot-admin这个最简单的项目的优化取得很好的效果之后,就开始准备照搬到其他的项目中,没想到同样的方式怎么折腾都无效,分离之后的lib目录依然会每次需要全量重新push。出问题的项目结构大概是这样的: 一个常见的多模块mvn项目,有common,domain,rest-client,rest-server 这4个子模块,其中rest-server会依赖common和domain这两个子模块,打包出来的jar是在rest-server这个模块中。 究竟是什么鬼了? 终得正果 苦苦思索一番之后,lib目录既然不能复用上一次的cache,那一定是因为里面的内容有变化,遂将jar包解压,进到lib目录,真凶果然在此: 项目自身的3个子模块在每次编译的时候也会做为jar包放到lib目录下,这3个jar包每次编译都会有变化,所以导致这一层的cache失效。 找到问题之后,解决的思路就很简单了,将这种jar单独copy到一个目录下即可,修改后的Dockerfile如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
FROM openjdk:11.0.5-stretch as builder RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN echo 'Asia/Shanghai' >/etc/timezone VOLUME /tmp WORKDIR /target ADD target/*.jar app.jar RUN jar xf app.jar #创建一个snapshot目录,把snapshot的jar复制过去 RUN mkdir BOOT-INF/snapshot RUN mv BOOT-INF/lib/*SNAPSHOT.jar BOOT-INF/snapshot/ FROM openjdk:11.0.5-stretch VOLUME /tmp WORKDIR /app COPY --from=builder target/BOOT-INF/lib ./lib COPY --from=builder target/org/ ./org COPY --from=builder target/META-INF/ ./META-INF #单独复制snapshot这一层 COPY --from=builder target/BOOT-INF/snapshot ./snapshot COPY --from=builder target/BOOT-INF/classes ./classes CMD ["/bin/bash", "-c", "java -cp .:./classes/:./lib/*:./snapshot/* -server org.springframework.boot.loader.JarLauncher"] EXPOSE 8089 |
这样修改之后效果就和上面单模块的项目一样了,至此,基本完成了springboot项目的docker镜像优化,在jenkins的流水线上可以将原来镜像push的时间从1分钟以上优化到10s左右 未来之路 在整个优化的过程中,发现springboot2.3 M1版本已经有针对性的优化方案,增加了LAYERED_JAR的打包格式,未来可期。 具体可参考下文: www.jdon.com/53738 注: 本文中举例的两个项目案例,可在github上找到:github.com/yishh/sprin… 作者:thor_lee 链接:https://juejin.cn/post/6844904119338008583 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
View Details遇到的问题: 公司java项目按老的方式打包出来一个fat jar,100MB, 推送到腾讯云镜像仓库很慢,8分钟。。。走的是公网,专线暂时还没配置好 以前是内网harbor,速度还不明显。 归根究底,一次推送100MB是个不合理的事情 思路: 了解spring boot打包,期望将依赖的libs 和 业务代码拆分 优化dockerfile,充分利用缓存 解决问题: 先修改spring-boot-maven-plugin,只打包业务代码。网上有些是配置exclude,我试了,恶心到了。。那么多包挨个找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!--<mainClass>cn.qg.clotho.Bootstrap</mainClass>--> <layout>ZIP</layout> <!--<executable>true</executable>--> <includes> <include> <groupId>${project.groupId}</groupId> <artifactId>${project.artifactId}</artifactId> </include> </includes> </configuration> </plugin> |
新增maven-dependency-plugin,将依赖移到libs目录下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <type>jar</type> <includeTypes>jar</includeTypes> <includeScope>runtime</includeScope> <outputDirectory>${project.build.directory}/libs</outputDirectory> </configuration> </execution> </executions> </plugin> |
优化dockerfile 参考 https://medium.com/@nieldw/caching-maven-dependencies-in-a-docker-build-dca6ca7ad612 ,牛逼
1 2 3 4 5 6 7 8 9 10 11 12 13 |
FROM mvn3.5 as builder WORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline COPY src/ /build/src/ RUN mvn package FROM jdk1.8 EXPOSE 80 CMD exec java -Dloader.path="/home/libs/" -jar /home/app.jar COPY --from=builder /build/target/*.jar /home/app.jar COPY --from=builder /build/target/libs /home/libs/ |
搞定。最终代码变化每次推送也就1MB多 启动命令 java -Dloader.path="libs/" -jar app.jar 作者:小猋_a8f1 链接:https://www.jianshu.com/p/32456eea0488 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
View Details在这容器化的世界里,我们已经很少直接通过文件发布来运行asp.net core程序了。现在大多数情况下,我们都会使用docker来运行程序。在使用docker之前,我们往往需要打包我们的应用程序。asp.net core程序的镜像打包,网上有很多教程,其中大多数是使用sdk这个镜像来直接打包。打出来的包有好几百MB,3.1 SDK打出来的包甚至超过了1GB。那么有什么办法来缩小我们打出来的镜像吗?最小能缩小到多少呢?这篇文章就来介绍下如何缩小asp.net core 打包出来镜像的大小。 新建asp.net core 程序 新建一个asp.net core应用程序,用来演示打包。首先我们演示下如果使用dotnet sdk5.0来打包 docker 镜像。 sdk:5.0
1 2 3 4 5 6 7 8 |
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build WORKDIR /app COPY /. /app RUN dotnet restore -s https://nuget.cdn.azure.cn/v3/index.json WORKDIR /app/CoreDockerImageSizeTest RUN dotnet publish -o ./out -c Release EXPOSE 5000 ENTRYPOINT ["dotnet", "out/CoreDockerImageSizeTest.dll"] |
在项目根目录下新建一个Dockerfile文件,文件内容如上。这个Dockerfile比较简单,使用dotnet sdk:5.0最为底层包来构建,这也是最傻瓜的打包方式。那么看看这个镜像打出来有多大吧。
1 |
docker build . -t coredockerimagesizetest_0.1 |
使用docker build命令进行打包。
1 2 |
REPOSITORY TAG IMAGE ID CREATED SIZE coredockerimagesizetest_0.1 latest 14aea8e0c1d5 5 seconds ago 643MB |
使用docker images命令来查看镜像列表,我们发现我们打出来的镜像居然有643MB,真的很大。如果是内网还好一点,如果在镜像存在docker hub等第三方仓库,这得下半天。显然这个镜像太大了,接下来看我们如何进行优化。 sdk:5.0-buster-slim 最新的VisualStudio内置了docker工具,可以自动为我们生成Dockerfile文件。我们来看看它生成的镜像文件有多大。 右键解决方案=>添加=>Docker支持=>Linux 。 选择完成后VS会为我们自动添加一个Dockerfile在根目录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base WORKDIR /app EXPOSE 5000 FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build WORKDIR /src COPY ["CoreDockerImageSizeTest/CoreDockerImageSizeTest.csproj", "CoreDockerImageSizeTest/"] RUN dotnet restore "CoreDockerImageSizeTest/CoreDockerImageSizeTest.csproj" COPY . . WORKDIR "/src/CoreDockerImageSizeTest" RUN dotnet build "CoreDockerImageSizeTest.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "CoreDockerImageSizeTest.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "CoreDockerImageSizeTest.dll"] |
这个自动生成的Dockerfile使用了sdk:5.0-buster-slim这个镜像进行build跟publish,使用aspnet:5.0-buster-slim这个runtime级别的镜像做为final底包。从名字来看,很明显slim代表着轻量。让我们试试这个Dockerfile打出来的包有多大。
1 2 |
REPOSITORY TAG IMAGE ID CREATED SIZE coredockerimagesizetest_0.2 latest 0a24618f6ece 11 seconds ago 210MB |
使用docker build命令进行打包。使用docker images命令查看镜像的大小,这个镜像的大小为210MB。果然比上面的镜像小了很多。那么是否还能继续缩小镜像的大小呢?继续往下看。 5.0-alpine 除了使用buster-slim镜像,我们还可以选择更加小巧的alpine镜像来打包。alpine镜像是继续alpine linux创建的镜像,所以它更加轻量级更加小巧。 关于alpine linux可以查看这篇:Alpine Linux 与 CentOS 有什么区别? 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
FROM mcr.microsoft.com/dotnet/aspnet:5.0-alpine AS base WORKDIR /app EXPOSE 5000 FROM mcr.microsoft.com/dotnet/sdk:5.0-alpine AS build WORKDIR /src COPY ["CoreDockerImageSizeTest/CoreDockerImageSizeTest.csproj", "CoreDockerImageSizeTest/"] RUN dotnet restore "CoreDockerImageSizeTest/CoreDockerImageSizeTest.csproj" COPY . . WORKDIR "/src/CoreDockerImageSizeTest" RUN dotnet build "CoreDockerImageSizeTest.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "CoreDockerImageSizeTest.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "CoreDockerImageSizeTest.dll"] |
修改Dockerfile使用aspnet:5.0-alpine及sdk:5.0-alpine来构建这个镜像。
1 2 |
REPOSITORY TAG IMAGE ID CREATED SIZE coredockerimagesizetest_0.3 latest db34d613e21a 12 seconds ago 108MB |
使用docker build命令进行打包。使用docker images命令查看镜像的大小,这个镜像的大小为108MB。现在这个镜像已经比我们第一次打包减少了500多MB了。那么还能更小吗?请往下看。 runtime-deps:5.0-alpine 最新的.net core程序支持自宿主及单文件发布。如果采用以上发布形式,那么我们可以选择使用runtime-deps:5.0-alpine做为最终底包来打包我们的镜像。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
FROM mcr.microsoft.com/dotnet/aspnet:5.0-alpine AS base WORKDIR /app EXPOSE 5000 FROM mcr.microsoft.com/dotnet/sdk:5.0-alpine AS build WORKDIR /src COPY ["CoreDockerImageSizeTest/CoreDockerImageSizeTest.csproj", "CoreDockerImageSizeTest/"] RUN dotnet restore "CoreDockerImageSizeTest/CoreDockerImageSizeTest.csproj" COPY . . WORKDIR "/src/CoreDockerImageSizeTest" RUN dotnet build "CoreDockerImageSizeTest.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "CoreDockerImageSizeTest.csproj" -c Release -o /app/publish \ --runtime alpine-x64 \ --self-contained true \ /p:PublishTrimmed=true \ /p:PublishSingleFile=true FROM mcr.microsoft.com/dotnet/runtime-deps:5.0-alpine AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["./CoreDockerImageSizeTest"] |
修改Dockerfile,使用/runtime-deps:5.0-alpine做为final镜像。
1 2 |
REPOSITORY TAG IMAGE ID CREATED SIZE coredockerimagesizetest_0.5 latest dab1289626f9 6 seconds ago 54.6MB |
使用docker build命令进行打包。使用docker images命令查看镜像的大小,这个镜像的大小为54.6MB。 总结 通过以上演示,我们的镜像大小从一开始的600多MB缩小到了54MB。一般生产我主要选择buster-slim这个镜像来打包。如果选择runtime-deps打包,打出来的包是最小的,虽然演示项目是可以运行的,但是本人没有在生产使用过,还请谨慎使用。 代码在这:CoreDockerImageSizeTest from:https://www.cnblogs.com/kklldog/p/netcore-docker-image-size.html
View Details就一句话:
1 |
SELECT * from PrjOlc WHERE (Prj,SubID) in (SELECT Prj,SubID FROM SP where stage='设计' and 负责人 = 'XXX000') |
from:https://www.cnblogs.com/ShengunErshu/p/14239690.html
View Details下午跑程序,在插入mysql时突然报错: ”The table‘xxxx’is full“ 而之前一直没问题的。 上网查了一下,都说临时表的问题,需要设置”tmp_table_size“: tmp_table_size 如果内存内的临时表超过该值,MySQL自动将它转换为硬盘上的MyISAM表。如果你执行许多高级GROUP BY查询并且有大量内存,则可以增加tmp_table_size的值。 http://www.2cto.com/database/201106/92968.html 感觉与我们服务器情况不太相符:我们没用到临时表,而且tmp_table_size已经非常大了。 再查到: mysql出现"the table is full"的问题,一般有两个原因: 一 .You are using the MEMORY (HEAP) storage engine; in this case you need to increase the value of the max_heap_table_size system variable. See Section 5.1.3, “Server System Variables”. 于是就修改Mysql的配置文件/etc/my.cnf,在[mysqld]下添加/修改两行: tmp_table_size = 256M max_heap_table_size = 256M 系统默认是16M,修改完后重启mysql 二.硬盘空间满了,清理硬盘即可. http://linux.net527.cn/fuwuqiyingyong/Mysql_shujuku/2011/1003/44014.html 在服务器df了一下,果然硬盘空间不够了,已经使用了100%。 追查下来,发现是mysql的日志文件将硬盘撑爆了,有大量的mysql-bin.000XXX之类的日志文件。 如何处理?很简单: (1)清除这些日志文件 (2)如果不需要的话,可以关闭mysql的bin-log功能。 具体操作: 这是数据库的操作日志,例如UPDATE一个表,或者DELETE一些数据,即使该语句没有匹配的数据,这个命令也会存储到日志文件中,还包括每个语句执行的时间,也会记录进去的 这样做主要有以下两个目的: 1:数据恢复 如果你的数据库出问题了,而你之前有过备份,那么可以看日志文件,找出是哪个命令导致你的数据库出问题了,想办法挽回损失。 2:主从服务器之间同步数据 主服务器上所有的操作都在记录日志中,从服务器可以根据该日志来进行,以确保两个同步。 处理方法分两种情况: 1:只有一个mysql服务器,那么可以简单的注释掉这个选项就行了。 vi /etc/my.cnf把里面的log-bin这一行注释掉,重启mysql服务即可。 2:如果你的环境是主从服务器,那么就需要做以下操作了。 A:在每个从属服务器上,使用SHOW SLAVE STATUS来检查它正在读取哪个日志。 B:使用SHOW MASTER LOGS获得主服务器上的一系列日志。 C:在所有的从属服务器中判定最早的日志,这个是目标日志,如果所有的从属服务器是更新的,就是清单上的最后一个日志。 D:清理所有的日志,但是不包括目标日志,因为从服务器还要跟它同步。 清理日志方法为: PURGE MASTER LOGS […]
View Details一,对记录的操作 1.创建有json字段的表
1 2 |
-- 创建表 CREATE TABLE t_json(id INT PRIMARY KEY, sname VARCHAR(20) , info JSON); |
2.插入记录
1 2 3 4 5 6 |
-- 插入含有json数组的记录 INSERT INTO t_json(id,sname,info) VALUES( 1, 'name1', JSON_ARRAY(1, "abc", NULL, TRUE, CURTIME())); -- 插入含有json对象的记录 INSERT INTO t_json(id,sname,info) VALUES( 2, 'name2', JSON_OBJECT("age", 20, "time", now())); INSERT INTO t_json(id,sname,info) VALUES( 3, 'name3', '{"age":20, "time":"2018-07-14 10:52:00"}'); |
3.查询记录
1 2 3 4 5 |
-- 查询记录 SELECT sname,JSON_EXTRACT(info,'$.age') FROM t_json; SELECT sname,info->'$.age' FROM t_json; -- 查询key SELECT id,json_keys(info) FROM t_json; |
4.修改记录
1 2 3 4 5 6 7 8 |
-- 增加键 UPDATE t_json SET info = json_set(info,'$.ip','192.168.1.1') WHERE id = 2; -- 变更值 UPDATE t_json SET info = json_set(info,'$.ip','192.168.1.2') WHERE id = 2; -- 删除键 UPDATE t_json SET info = json_remove(info,'$.ip') WHERE id = 2; |
二,创建json值函数 1.JSON_ARRAY 生成json数组
1 2 3 |
-- JSON_ARRAY(val1,val2,val3...) -- 生成一个包含指定元素的json数组。 SELECT JSON_ARRAY(1, "abc", NULL, TRUE, CURTIME()); -- [1, "abc", null, true, "10:37:08.000000"] |
2.JSON_OBJECT 生成json对象
1 2 3 |
-- JSON_OBJECT(key1,val1,key2,val2...) -- 生成一个包含指定K-V对的json object。如果有key为NULL或参数个数为奇数,则抛错。 SELECT JSON_OBJECT('age', 20, 'time', now()); -- {"id": 87, "name": "carrot"} |
3.JSON_QUOTE 加"号
1 2 3 |
-- JSON_QUOTE(json_val) -- 将json_val用"号括起来。 SELECT JSON_QUOTE('[1,2,3]'); -- "[1,2,3]" |
三,搜索json值函数 1.JSON_CONTAINS 指定数据是否存在
1 2 3 4 |
set @j = '{"a": 1, "b": 2, "c": {"d": 4}}'; -- JSON_CONTAINS(json_doc, val[, path]) -- 查询json文档是否在指定path包含指定的数据,包含则返回1,否则返回0。如果有参数为NULL或path不存在,则返回NULL。 SELECT JSON_CONTAINS(@j, '4', '$.c.d'); -- 1 |
2.JSON_CONTAINS_PATH 指定路径是否存在
1 2 3 4 5 |
-- JSON_CONTAINS_PATH(json_doc, one_or_all, path[, path] ...) -- 查询是否存在指定路径,存在则返回1,否则返回0。如果有参数为NULL,则返回NULL。 -- one_or_all只能取值"one"或"all",one表示只要有一个存在即可;all表示所有的都存在才行。 SELECT JSON_CONTAINS_PATH(@j, 'one', '$.a', '$.e'); -- 1 SELECT JSON_CONTAINS_PATH(@j, 'all', '$.a', '$.c.d'); -- 1 |
3.JSON_EXTRACT 查找所有指定数据
1 2 3 4 5 6 |
-- JSON_EXTRACT(json_doc, path[, path] ...) -- 从json文档里抽取数据。如果有参数有NULL或path不存在,则返回NULL。如果抽取出多个path,则返回的数据封闭在一个json array里。 set @j2 = '[10, 20, [30, 40]]'; SELECT JSON_EXTRACT('[10, 20, [30, 40]]', '$[1]'); -- 20 SELECT JSON_EXTRACT('[10, 20, [30, 40]]', '$[1]', '$[0]'); -- [20, 10] SELECT JSON_EXTRACT('[10, 20, [30, 40]]', '$[2][*]'); -- [30, 40] |
4.JSON_KEYS 查找所有指定键值
1 2 3 4 5 |
-- JSON_KEYS(json_doc[, path]) -- 获取json文档在指定路径下的所有键值,返回一个json array。如果有参数为NULL或path不存在,则返回NULL。 SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}'); -- ["a", "b"] SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}', '$.b'); -- ["c"] SELECT id,json_keys(info) FROM t_json; |
5.JSON_SEARCH 查找所有指定值的位置
1 2 3 4 5 6 7 8 9 10 11 12 |
-- JSON_SEARCH(json_doc, one_or_all, search_str[, escape_char[, path] ...]) -- 查询包含指定字符串的paths,并作为一个json array返回。如果有参数为NUL或path不存在,则返回NULL。 -- one_or_all:"one"表示查询到一个即返回;"all"表示查询所有。 -- search_str:要查询的字符串。 可以用LIKE里的'%'或‘_’匹配。 -- path:在指定path下查。 SET @j3 = '["abc", [{"k": "10"}, "def"], {"x":"abc"}, {"y":"bcd"}]'; SELECT JSON_SEARCH(@j3, 'one', 'abc'); -- "$[0]" SELECT JSON_SEARCH(@j3, 'all', 'abc'); -- ["$[0]", "$[2].x"] SELECT JSON_SEARCH(@j3, 'all', 'abc', NULL, '$[2]'); -- "$[2].x" SELECT JSON_SEARCH(@j3, 'all', '10'); -- "$[1][0].k" SELECT JSON_SEARCH(@j3, 'all', '%b%'); -- ["$[0]", "$[2].x", "$[3].y"] SELECT JSON_SEARCH(@j3, 'all', '%b%', NULL, '$[2]'); -- "$[2].x" |
四,修改json值函数 1.JSON_ARRAY_APPEND 指定位置追加数组元素
1 2 3 4 5 6 7 8 |
-- JSON_ARRAY_APPEND(json_doc, path, val[, path, val] ...) -- 在指定path的json array尾部追加val。如果指定path是一个json object,则将其封装成一个json array再追加。如果有参数为NULL,则返回NULL。 SET @j4 = '["a", ["b", "c"], "d"]'; -- SELECT JSON_ARRAY_APPEND(@j4, '$[1][0]', 3); -- ["a", [["b", 3], "c"], "d"] SET @j5 = '{"a": 1, "b": [2, 3], "c": 4}'; SELECT JSON_ARRAY_APPEND(@j5, '$.b', 'x'); -- {"a": 1, "b": [2, 3, "x"], "c": 4} SELECT JSON_ARRAY_APPEND(@j5, '$.c', 'y'); -- {"a": 1, "b": [2, 3], "c": [4, "y"]} SELECT JSON_ARRAY_APPEND(@j5, '$', 'z'); -- [{"a": 1, "b": [2, 3], "c": 4}, "z"] |
2.JSON_ARRAY_INSERT 指定位置插入数组元素
1 2 3 4 5 6 7 |
-- JSON_ARRAY_INSERT(json_doc, path, val[, path, val] ...) -- 在path指定的json array元素插入val,原位置及以右的元素顺次右移。如果path指定的数据非json array元素,则略过此val;如果指定的元素下标超过json array的长度,则插入尾部。 SET @j6 = '["a", {"b": [1, 2]}, [3, 4]]'; SELECT JSON_ARRAY_INSERT(@j6, '$[1]', 'x'); -- ["a", "x", {"b": [1, 2]}, [3, 4]] SELECT JSON_ARRAY_INSERT(@j6, '$[100]', 'x'); -- ["a", {"b": [1, 2]}, [3, 4], "x"] SELECT JSON_ARRAY_INSERT(@j6, '$[1].b[0]', 'x'); -- ["a", {"b": ["x", 1, 2]}, [3, 4]] SELECT JSON_ARRAY_INSERT(@j6, '$[0]', 'x', '$[3][1]', 'y'); -- ["x", "a", {"b": [1, 2]}, [3, "y", 4]] |
3.JSON_INSERT 指定位置插入
1 2 3 4 |
-- JSON_INSERT(json_doc, path, val[, path, val] ...) -- 在指定path下插入数据,如果path已存在,则忽略此val(不存在才插入)。 SET @j7 = '{ "a": 1, "b": [2, 3]}'; SELECT JSON_INSERT(@j7, '$.a', 10, '$.c', '[true, false]'); -- {"a": 1, "b": [2, 3], "c": "[true, false]"} |
4.JSON_REPLACE 指定位置替换
1 2 3 |
-- JSON_REPLACE(json_doc, path, val[, path, val] ...) -- 替换指定路径的数据,如果某个路径不存在则略过(存在才替换)。如果有参数为NULL,则返回NULL。 SELECT JSON_REPLACE(@j7, '$.a', 10, '$.c', '[true, false]'); -- {"a": 10, "b": [2, 3]} |
5.JSON_SET 指定位置设置
1 2 3 |
-- JSON_SET(json_doc, path, val[, path, val] ...) -- 设置指定路径的数据(不管是否存在)。如果有参数为NULL,则返回NULL。 SELECT JSON_SET(@j7, '$.a', 10, '$.c', '[true, false]'); -- {"a": 10, "b": [2, 3], "c": "[true, false]"} |
6.JSON_MERGE 合并
1 2 3 4 5 6 7 8 9 |
-- JSON_MERGE(json_doc, json_doc[, json_doc] ...) -- merge多个json文档。规则如下: -- 如果都是json array,则结果自动merge为一个json array; -- 如果都是json object,则结果自动merge为一个json object; -- 如果有多种类型,则将非json array的元素封装成json array再按照规则一进行mege。 SELECT JSON_MERGE('[1, 2]', '[true, false]'); -- [1, 2, true, false] SELECT JSON_MERGE('{"name": "x"}', '{"id": 47}'); -- {"id": 47, "name": "x"} SELECT JSON_MERGE('1', 'true'); -- [1, true] SELECT JSON_MERGE('[1, 2]', '{"id": 47}'); -- [1, 2, {"id": 47}] |
7.JSON_REMOVE 指定位置移除
1 2 3 4 |
-- JSON_REMOVE(json_doc, path[, path] ...) -- 移除指定路径的数据,如果某个路径不存在则略过此路径。如果有参数为NULL,则返回NULL。 SET @j8 = '["a", ["b", "c"], "d"]'; SELECT JSON_REMOVE(@j8, '$[1]'); -- ["a", "d"] |
8.JSON_UNQUOTE 去"号
1 2 3 |
-- JSON_UNQUOTE(val) -- 去掉val的引号。如果val为NULL,则返回NULL。 SELECT JSON_UNQUOTE("\"123\""); -- 123 |
五,返回json值属性的函数 1.JSON_DEPTH 深度
1 2 3 4 5 6 |
-- JSON_DEPTH(json_doc) -- 获取json文档的深度。如果参数为NULL,则返回NULL。 -- 空的json array、json object或标量的深度为1。 SELECT JSON_DEPTH('{}'), JSON_DEPTH('[]'), JSON_DEPTH('true'); -- 1 1 1 SELECT JSON_DEPTH('[10, 20]'), JSON_DEPTH('[[], {}]'); -- 2 2 SELECT JSON_DEPTH('[10, {"a": 20}]'); -- 3 |
2.JSON_LENGTH 长度
1 2 3 4 5 6 7 8 9 |
-- JSON_LENGTH(json_doc[, path]) -- 获取指定路径下的长度。如果参数为NULL,则返回NULL。 -- 长度的计算规则: -- 标量的长度为1; -- json array的长度为元素的个数; -- json object的长度为key的个数。 SELECT JSON_LENGTH('[1, 2, {"a": 3}]'); -- 3 SELECT JSON_LENGTH('{"a": 1, "b": {"c": 30}}'); -- 2 SELECT JSON_LENGTH('{"a": 1, "b": {"c": 30}}', '$.b'); -- 1 |
[…]
View Details【0】需求 【0.1】需求描述 数据库中 num字段值为: 实现的效果:需要将一行数据变成多行 【0.2】实现的SQL
1 2 3 4 5 6 |
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('7654,7698,7782,7788',',',help_topic_id+1),',',-1) AS num FROM mysql.help_topic WHERE help_topic_id < LENGTH('7654,7698,7782,7788')-LENGTH(REPLACE('7654,7698,7782,7788',',',''))+1 |
涉及的知识点 【1】字符串拆分: SUBSTRING_INDEX(str, delim, count) 参数解说 解释 str 需要拆分的字符串 delim 分隔符,通过某字符进行拆分 count 当 count 为正数,取第 n 个分隔符之前的所有字符; 当 count 为负数,取倒数第 n 个分隔符之后的所有字符。 举例
1 2 3 4 5 |
(1)获取第2个以逗号为分隔符之前的所有字符。 SELECT SUBSTRING_INDEX('7654,7698,7782,7788',',',2); <img class="medium-zoom-image" src="https://img2020.cnblogs.com/blog/1302413/202102/1302413-20210223175157464-1078941390.png" alt="" data-mce-src="https://img2020.cnblogs.com/blog/1302413/202102/1302413-20210223175157464-1078941390.png"> |
1 2 3 |
(2)获取最后一个到倒数第2个以逗号分隔符之后的所有字符 SELECT SUBSTRING_INDEX('7654,7698,7782,7788',',',-2); |
所以,我们的核心代码中的 -1 ,就是获取以逗号为分隔符的最后一个值;也就是7788 【2】替换函数:replace( str, from_str, to_str) 参数名 解释 str 需要进行替换的字符串 from_str 需要被替换的字符串 to_str 需要替换的字符串 2. 举例
1 2 3 |
(1)将分隔符逗号替换为空。 SELECT REPLACE('7654,7698,7782,7788',',',''); |
【3】获取字符串长度:LENGTH( str ) 参数名 解释 str 需要计算长度的字符串 举例
1 2 3 4 |
(1)获取 ‘7654,7698,7782,7788’ 字符串的长度 SELECT LENGTH('7654,7698,7782,7788') <img src="https://img2020.cnblogs.com/blog/1302413/202102/1302413-20210223175618490-72037422.png" alt="" data-mce-src="https://img2020.cnblogs.com/blog/1302413/202102/1302413-20210223175618490-72037422.png"> |
【4】实现的原理解析 【4.0】实现SQL 需要解析的SQL
1 2 3 4 5 6 |
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('7654,7698,7782,7788',',',help_topic_id+1),',',-1) AS num FROM mysql.help_topic WHERE help_topic_id < LENGTH('7654,7698,7782,7788')-LENGTH(REPLACE('7654,7698,7782,7788',',',''))+1 |
此处利用 mysql 库的 help_topic 表的 help_topic_id 来作为变量,因为 help_topic_id 是自增的,当然也可以用其他表的自增字段辅助。 help_topic 表: 注意,这个辅助表的ID最大长度只有642;如果过长的字符串,可能需要借助其他自增的辅助表(可以是现有表,也可以自己造一个 1,2,3,4 递增的行即可) […]
View Details