100 其他

lishihuan大约 128 分钟

项目笔记

  • extData: 扩展数据

Total control 手机投屏电脑

## java 各个中间件技术框架
网关:Nginx、Kong、Zuul
缓存:Redis、MemCached、OsCache、EhCache
搜索:ElasticSearch、Solr
熔断:Hystrix、resilience4j
负载均衡:DNS、F5、LVS、Nginx、OpenResty、HAproxy
注册中心:Eureka、Zookeeper、Redis、Etcd、Consul
认证鉴权:JWT
消费队列:RabbitMQ、ZeroMQ、Redis、ActiveMQ、Kafka
系统监控:Grafana、Prometheus、Influxdb、Telegraf、Lepus
文件系统:OSS、NFS、FastDFS、MogileFS
RPC框架: Dubbo、Motan、Thrift、grpc
构建工具:Maven、Gradle
集成部署:Docker、Jenkins、Git、Maven
分布式配置:Disconf、Apollo、Spring Cloud Config、Diamond
压测:LoadRunner、JMeter、AB、webbench
数据库:MySql、Redis、MongoDB、PostgreSQL、Memcache、HBase
网络:专用网络VPC、弹性公网IP、CDN
数据库中间件:DRDS、Mycat、360 Atlas、Cobar (不维护了)
分布式框架:Dubbo、Motan、Spring-Could
分布式任务:XXL-JOB、Elastic-Job、Saturn、Quartz
分布式追踪:Pinpoint、CAT、zipkin

vscode装个插件:live server 可以代码修改后实时更新

https://blog.csdn.net/u012012621/article/details/50998392open in new window

打jar https://www.cnblogs.com/qifengshi/p/6036870.htmlopen in new window

git 教程 https://www.bilibili.com/video/BV1pW411A7a5?p=58open in new window

https://www.bookstack.cn/read/sdky-java-note/3e78ff1a5c3d158d.mdopen in new window

定时器:@Scheduled https://blog.csdn.net/u011374582/article/details/82883620open in new window

https://blog.csdn.net/weixin_42753193/article/details/122189782open in new window

b站视频下载;https://bilibili.iiilab.com/open in new window

接口有HTTPopen in new window接口、WebService接口、FTP文件传输

gitee 作为图床有个问题,图片大小不能 超过 1M

  • 对象存储
  • docker部署

技术选型:

  1. 核心框架:Spring Boot。
  2. 安全框架:Apache Shiro。
  3. 模板引擎:Thymeleaf。
  4. 持久层框架:MyBatis。
  5. 定时任务:Quartz。
  6. 数据库连接池:Druid。
  7. 工具类:Fastjson。

翻墙:https://www.zionladdero.com/open in new window

待整理:https://blog.csdn.net/qq_33683097/article/details/81230111open in new window ssm maven 工程目录以及环境搭建(对配置文件的介绍)

整理代表:ydxjApply 巡检保人事申请

接口开发:https://blog.csdn.net/diyangxia/article/details/64122387open in new window

https://oss.console.aliyun.com/bucket/oss-cn-shanghai/lishihuan/object?path=20210408%2Fopen in new window

https://blog.csdn.net/vivivi_lau/article/details/106501715open in new window

git: https://git-scm.com/book/zh/v2open in new window

​ 图形化工具:Source Tree,[TortoiseGit](# https://www.cnblogs.com/xiuxingzhe/p/9312929.htmlopen in new window)

https://www.cnblogs.com/tugenhua0707/p/4050072.htmlopen in new window

js 实现 复制图片到剪切板 https://blog.csdn.net/MICHAELKING1/article/details/89642497open in new window

js 模板:https://blog.csdn.net/qq_42943107/article/details/91045524open in new window

css 动画;https://zh.javascript.info/css-animations#guan-jian-zhen-dong-hua-keyframesopen in new window

css 实现 图文切换:https://www.cnblogs.com/cs-whut/p/13463928.htmlopen in new window (C:\Users\lishihuan\Desktop\html动画-待整理\CSS动画实例:图文切换.html)

layer 弹窗层 https://blog.csdn.net/qq_37335220/article/details/82024066open in new window

图片 都放到一起,通过定位来 显示 当中的 具体 图片,例如ztree 图标 (用到属性 background-position)

Collections3.extractToString 对 集合的处理

使用 Fiddler 实现本地代码替换远程代码 https://zhangzw.com/posts/20190501.htmlopen in new window

idea 设置svn代码忽略:https://blog.csdn.net/idongit/article/details/103079171open in new window

https://tool.lu/tinyimage/open in new window 在线压缩图片

java 待整理: ControllerAdvice 注解

待整理 Java使用 POI 操作Excelopen in new window

待整理 盒子模型:https://www.cnblogs.com/hellocd/p/10443237.htmlopen in new window

待整理: js 闭包 https://blog.csdn.net/weixin_39214481/article/details/86164785open in new window

待整理-java 导出word:https://www.jianshu.com/p/de58ab550157open in new window

https://blog.csdn.net/xm393392625/article/details/88795547open in new window 插入图片

var data =  eval('('+evt.data+')');
JSON.parse(itemStr);

跨域:https://blog.csdn.net/qq_30546099/article/details/71404574open in new window

不同域名之间共享localStorage/sessionStorage https://www.jianshu.com/p/8c4cee29d532open in new window (解决跨域场景)

netty websocket实现心跳和断线重连

https://www.cnblogs.com/chaizezhao/articles/5291608.htmlopen in new window

app开发;https://blog.csdn.net/lucy_100/article/details/49820393open in new window

待整理,CPMS 组织树 (TreeBean)

整理 图片放大器:<div ng-include="'modules/sys/picGiger.html'"></div> 如何实现 作为公共 模块的

待整理:坐标转换(java,js) 坐标获取位置信息

Notepad++ 临时备份文件路径: C:\Users\lishihuan\AppData\Roaming\Notepad++\backup\

全景图 photo-sphere-viewer : https://www.jianshu.com/p/78208c73a896open in new windowhttps://photo-sphere-viewer.js.org/open in new window 2D:https://blog.csdn.net/haiwei_lian/article/details/109012683open in new window

J2Cache缓存:https://gitee.com/ld/J2Cacheopen in new window

快捷键:

https://blog.csdn.net/ademoa/article/details/80091939open in new window

  • Idea:

  • NotePad大小写转换open in new window

    • 小写转换大写 Ctrl + shift + U
    • 大写转换小写Ctrl + U
    • 复制当前行 Ctrl + D
    • 删除当前行 Ctrl +L (相当于剪切 + 删除行,并不是存粹的删除行)
  • ctrl+q 可以查看方法具体在哪个包里面,或是jar包

    • ctrl+alt+o 去掉没有引用的import
    • ctrl+alt+B 查看接口被哪些类实现
    • ctrl+shift+alt+u 查看类的继承关系
    • ctrl+alt+u 查看类的继承关系
    • ctrl+shift+u 代码中大小写切换
    • ctrl+O 选择父类方法去重写,这里没有继承直接显示继承自Object类的方法
  • MyEclipse :

    • Alt+Shift+L 代码等号右边自动补全
    • 收起展开所有方法的快捷键
      • 收缩所有方法:Ctrl+Shift+'/'(小键盘的‘/‘)
      • 展开所有方法:Ctrl+Shift+'*'(小键盘的)

输入法英文间距太大 open in new window(全角和半角) shift+空格

常用次:扁平化,存量,增量

坐标系

公司:
	谷歌地图:31.9344120986,117.2649120808	----> 和 腾讯和高德坐标偏移很小
	百度地图:31.9405040000,117.2714210000
	腾讯高德:31.9344119085,117.2649256012
	图吧地图:31.9378224285,117.2620342812
	谷歌地球:31.9364424285,117.2594242812   ---> 现在市面上 GPS都是用这个的
	北纬N31°56′11.19″ 东经E117°15′33.93″
坐标系说明
    WGS84坐标系	地球坐标系,国际通用坐标系  ---GPS
    GCJ02坐标系	火星坐标系,WGS84坐标系加密后的坐标系;Google国内地图、高德、QQ地图、腾讯地图 使用
    BD09坐标系	百度坐标系,GCJ02坐标系加密后的坐标系
注:
	谷歌地图API,高德地图API,腾讯地图API上取到的,都是GCJ-02坐标,他们三家都是通用的
	百度:BD-09坐标
	谷歌地球,google earth上取到的,是GPS坐标。而且是度分秒形式的经纬度坐标。在国内不允许使用。必须转换为GCJ-02坐标

数据库

数据库连接失败,navicat可以连接上,但是java程序无法连接 useSSL=false


11 oracle

plsql 中 ctrl+e 查看 历史执行的语句

shu据库:https://192.168.99.19:1158/emopen in new window

11.1

  • 命令运行 : sqlplus /nolog

  • 给system赋权 :grant sysdba to system;

sql文件导入导致plsql 等工具卡死,无法执行解决:

​ 1.cmd-->输入: conn sys/as sysdba

​ 2.输入密码 lishihuan

​ 3. sql>@'D:\aa.sql'; 执行后导入sql文件

oracle em 主机身份证明 输入的用户名和密码是 电脑端的,比如我的是 wangyilishihuna@163.com e9q......

  • cmd 执行 sql 命令

    sqlplus /nolog

    conn /as sysdba

11.1oracle 一些常用 语句

数据库创建一个用户

/*(命令行运行: sqlplus /nolog  在SQL>提示符后面运行: conn /as sysdba  命令横提示Connected说明连接成功;)*/
--1.查询用户 (检查用户对象,导库的时候可以用来检测,目标用户是否存在)
select * from dba_users; 
--2.查询表控件本地存储位置
select name from v$datafile;
--3.创建表空间
create tablespace sdt_tyxxpt datafile 'D:\ORACLE\ORADATA\ORCL\sdt_tyxxpt.dbf' size 50m autoextend on next 50m maxsize 20480m extent management local; 
--说明:(create tablespace 表空间名称 datafile '数据文件的路径' size 大小 autoextend on ;  (autoextend 自增长)命令行输入)
--4.创建用户: 创建用户语法:create user 用户名 identified by 密码 default tablespace 用户默认使用哪一个表空间; 
create user sdt_tyxxpt identified by sdt_tyxxpt default tablespace sdt_tyxxpt temporary tablespace temp;
--5.给用户授予权限 
grant connect,resource,dba to sdt_tyxxpt;
--6.删除 (https://blog.csdn.net/dongyuxu342719/article/details/81530942)
--删除用户
drop user 用户名称 cascade;
--删除表空间(同时也会删除本地存储 的dmp文件)
drop tablespace 表空间名称 including contents and datafiles cascade constraint;
-- 7.修改用户密码
alter user SDT_XJB identified by SDT_XJB;
--8.数据库em
https://localhost:1158/em;

删除用户失败,提示 ORA-01940 用户被使用在,所以需要先杀死进程

select username,sid,serial#,paddr from v$session where username='INVOICE'; alter system kill session '62,22594';

image-20210113095013789
image-20210113095013789

修改用户密码

alter user 用户名 identified by 新密码;

查询每个表的 数据量

select u.TABLE_NAME,u.NUM_ROWS from user_tables u order by u.NUM_ROWS desc

查看ORACLE表空间大小及所剩空间大小 (单位MB)

select a.tablespace_name,
       a.bytes / 1024 / 1024 "Sum MB",
       (a.bytes - b.bytes) / 1024 / 1024 "used MB",
       b.bytes / 1024 / 1024 "free MB",
       round(((a.bytes - b.bytes) / a.bytes) * 100, 2) "percent_used"
  from (select tablespace_name, sum(bytes) bytes
          from dba_data_files
         group by tablespace_name) a,
       (select tablespace_name, sum(bytes) bytes, max(bytes) largest
          from dba_free_space
         group by tablespace_name) b
 where a.tablespace_name = b.tablespace_name
 order by ((a.bytes - b.bytes) / a.bytes) desc;

SELECT UPPER(F.TABLESPACE_NAME) "表空间名",
       D.TOT_GROOTTE_MB "表空间大小(M)",
       D.TOT_GROOTTE_MB - F.TOTAL_BYTES "已使用空间(M)",
       TO_CHAR(ROUND((D.TOT_GROOTTE_MB - F.TOTAL_BYTES) / D.TOT_GROOTTE_MB * 100, 2),'990.99') "使用比",
       F.TOTAL_BYTES "空闲空间(M)",
       F.MAX_BYTES "最大块(M)"
  FROM (SELECT TABLESPACE_NAME,
               ROUND(SUM(BYTES) / (1024 * 1024), 2) TOTAL_BYTES,
               ROUND(MAX(BYTES) / (1024 * 1024), 2) MAX_BYTES
          FROM SYS.DBA_FREE_SPACE
         GROUP BY TABLESPACE_NAME) F,
       (SELECT DD.TABLESPACE_NAME,
               ROUND(SUM(DD.BYTES) / (1024 * 1024), 2) TOT_GROOTTE_MB
          FROM SYS.DBA_DATA_FILES DD
         GROUP BY DD.TABLESPACE_NAME) D
 WHERE D.TABLESPACE_NAME = F.TABLESPACE_NAME
 ORDER BY 4 DESC;

查询表空间

SELECT owner,t.table_name,
       num_rows, --记录行数
       s.bytes / 1024 / 1024 AS size_mb
FROM all_tables t
JOIN user_segments s ON t.table_name = s.segment_name
WHERE  s.segment_type = 'TABLE'
  AND t.owner = 'SDT_ZHST_2024'   -- 目标用户
ORDER BY size_mb DESC;

查询大表,并且释放表空间

  • 最优的是 通过 DROP TABLE table_name; CREATE TABLE table_name (...); -- 重新创建表
  • 通过 delete 删除后,不会自动释放空间,需要 可以 使用 SHRINK SPACE 命令
-- 启用行移动: 使用以下命令来启用表的行移动:
ALTER TABLE SYS_LOG ENABLE ROW MOVEMENT;
-- 行空间压缩操作: 启用行移动后,你可以执行 SHRINK SPACE 或 MOVE 操作来释放空间
ALTER TABLE SYS_LOG SHRINK SPACE;
ALTER TABLE SYS_LOG MOVE;
-- 禁用行移动(可选): 如果你完成了所需的操作,并且希望禁用行移动,可以执行以下命令
ALTER TABLE SYS_LOG DISABLE ROW MOVEMENT;

查询用户下的所有表

select t.* from user_tables t;

select * from all_tab_comments;--查询所有用户的表,视图等。
select * from user_tab_comments;--查询本用户的表,视图等。
select * from all_col_comments;--查询所有用户的表的列名和注释。
select * from user_col_comments;--查询本用户的表的列名和注释。
select * from all_tab_columns;--查询所有用户的表的列名等信息。
select * from user_tab_columns;--查询本用户的表的列名等信息。

/*
    ORACLE下有三个视图
        DBA_TABLES 拥有DBA角色的用户可以查看系统中的所有表
        USER_TABLES 登录数据库的当前用户拥有的所有表
        ALL_TABLES 登录数据库的当前用户有权限查看的所有表
 */

查询空表

select * from user_tables where num_rows=0;
select username,default_tablespace from user_users;-- 查询当前用户下的表空间
select name from v$datafile;-- 查询表空间的位置
SELECT FILE_NAME,TABLESPACE_NAME,AUTOEXTENSIBLE FROM dba_data_files; -- 查看表空间是否自增长

修改表空间

-- 如果是 迁移表,针对需要 修改表空间的场景,需要 分2步,1.修改表的 表空间  2.重建 索引 的 表空间
---- 1.讲表 ACT_RU_JOB 的表空间修改为   SDT_XJB
alter table ACT_RU_JOB move tablespace SDT_XJB;
---- 2. 重建 索引
select 'alter index '||index_name||' rebuild online;' from  user_indexes 
	where status <> 'VALID' and index_name not like'%$$' or tablespace_name ='SDT_IMOA';;
----- 本质执行 alter index 索引名称 rebuild online; 重建


-- 查询索引(用于检查是否有些 重构 索引 但是没有 讲表空间修改 好)
select tablespace_name,index_name,t.table_name,t.status from SYS.USER_INDEXES t;--(USER_INDEXES 查询索引信息的视图)
--批量修改(找到 索引 表空间为 SDT_IMOA 的所有索引 修改语句,将其 修改为 CPMS)
select 'alter index '||index_name||' rebuild tablespace CPMS;' from  user_indexes where tablespace_name ='SDT_IMOA';

--alter index 索引名称 rebuild online; --重建
--alter index 索引名称 rebuild tablespace 表空间; --修改表空间

增加表空间

-- 1. 创建表空间
create tablespace SDT_XJB datafile 'D:\app\Administrator\oradata\orcl\SDT_XJB02.DBF' size 1024m autoextend on next 50m maxsize 20480m extent management local; 

-- 2.增加表空间文件-针对表空间不够
alter tablespace SDT_XJB add datafile 'D:\app\Administrator\oradata\orcl\SDT_XJB02.DBF' size 30720M AUTOEXTEND on next 100m;

拼接语句查询出所有表要迁入表空间的语句,这样可以批量查询出来,修改方便。

select 'alter table  '|| table_name ||'  move tablespace 要迁入的表空间;' from dba_tables t where t.owner='要迁出的表归属用户名'; 

查询出指定表空间下的表:

select   tablespace_name,table_name  from user_tables WHERE tablespace_name='表空间名称';

查询出单一表对应的表空间:

select   tablespace_name,table_name  from user_tables where table_name='表名';

复制表,迁移表

注: 这样用于 迁移表 比 exp和imp 导出方便,如果两个数据库不在一起,可以通过建立 dblink

-- 迁移 同数据库,不同 用户下的 接地线表,即 讲 SDT_IMOA用户下的巡检保表 迁移到 巡检保下
select 
 'create table '|| table_name ||' as select * from SDT_IMOA.'|| table_name ||';'  
from user_tables where table_name like 'JDX_%';

获取 DDL 语句

SELECT DBMS_METADATA.GET_DDL('TABLE',u.table_name) FROM USER_TABLES u; 
SELECT DBMS_METADATA.GET_DDL('INDEX',u.index_name) FROM USER_INDEXES u; 

查询每个表数量

select t.table_name,t.num_rows from user_tables t;

主键、唯一性约束 、联合主键

对于除了主键还需要 其他字段做唯一性约束,(例如部门下管的行政区域,那么中间表需要 部门和行政区域合再一起做唯一性约束)

alter table zcz add constraints zcz_pk primary key (id); -- 主键
alter table zcz add constraints zcz_name_age primary key (name,age);--联合主键

--创建联合约束(下面该条sql的作用是name和age不能同时相等)
CREATE UNIQUE INDEX zcz_name_age ON zcz(NVL2(name,age,NULL),NVL2(name,name,NULL));

-- 创建unique唯一约束(不能存在 name+age相同的数据)
alter table table_name add constraint name_age_unique unique(name,age));

-- 给表添加 主键和联合约束
alter table BAS_C_LINE_RELEVANCE 
      add constraints PK_BAS_C_LINE_RELEVANCE primary key (id)
      add constraint ID_PID_UNIQUE unique(LINE_PID,LINE_ID);

查询库中的表名和表中记录数

SELECT T.TABLE_NAME, T.NUM_ROWS FROM USER_TABLES T;

表数据被删除,但是空间没被释放

truncate table  YDXJ_DW_DWJL_2018_08 DROP STORAGE;
alter TABLE YDXJ_DW_DWJL_2018_08 deallocate unused keep 1k;  

导入时报:ora-01659 表空间自增长的原因

select name from v$datafile;
alter database datafile 'D:\APP\LISHIHUAN\ORADATA\ORCL\SDT_XJB.DBF' autoextend on;

#### 创建表:

 create table 表名 (
    字段名1 字段类型 默认值 是否为空 ,
    字段名2 字段类型 默认值 是否为空,
    字段名3 字段类型 默认值 是否为空,
    ......
  );

demo 创建一个user表:

create table user (
    id number(6) primary key,   ---主键
    name varchar(50) not null,   ---姓名 不为null
    sex varchar2(6) default '男' check ( sex in ('男','女'))   ---性别 默认'男'
);

create table 表名 (     字段名1 字段类型 默认值 是否为空 ,     字段名2 字段类型 默认值 是否为空,     字段名3 字段类型 默认值 是否为空,     ......   ); demo 创建一个user表:

修改表名:

rename 旧表名 to 新表名;
rename user to newuser;

删除表

delete from 表名;
delete删除数据是一条一条的删除数据,后面可以添加where条件,不删除表结构。注意:如果表中有identity产生的自增id列,delete from后仍然从上次的数开始增加。
truncate table 表名;
truncate是一次性删掉所有数据,不删除表结构。注意:如果表中有identity产生的自增id列,truncate后,会恢复初始值。
drop table 表名;
drop删除所有数据,会删除表结构。

修改表

  • 添加新字段:
alter table 表名 add(字段名 字段类型 默认值 是否为空);
alter table user add(age number(6));
alter table user add (course varchar2(30) default '空' not null);
## 添加备注
comment on column SYS_PUSH_CONTENT.meta_info is '存储非格式化数据(用于针对不同消息展示的不同消息内容的情况)';
  • 修改字段:
alter table 表名 modify (字段名 字段类型 默认值 是否为空);
alter table user modify((age number(8));
  • 修改字段名:
alter table 表名 rename  column  列名 to 新列名;
  alter table user rename column course to newcourse;
  • 删除字段:

Oracle 取随机数(转)open in new window

--1、从表中随机取记录
     select * from (select * from staff order by dbms_random.random)      where rownum < 4  --表示从STAFF表中随机取3条记录
--2、产生随机数
        SELECT DBMS_RANDOM.RANDOM FROM DUAL;          --产生一个任意大小的随机数
        SELECT ABS(MOD(DBMS_RANDOM.RANDOM,100)) FROM DUAL;          --产生一个100以内的随机数
        SELECT TRUNC(100+900*dbms_random.value) FROM dual;          --产生一个100~1000之间的随机数
        SELECT dbms_random.value FROM dual;          --产生一个0~1之间的随机数
		SELECT dbms_random.value(10,20) FROM dual;          --产生一个10~20之间的随机数
        SELECT dbms_random.normal FROM dual;          --NORMAL函数返回服从正态分布的一组数。此正态分布标准偏差为1,期望值为0。这个函数返回的数值中有68%是介于-1与+1之间,95%介于-2与+2之间,99%介于-3与+3之间。

--3、产生随机字符串
	select dbms_random.string('P',20) from dual;          --第一个参数 P 表示 printable,即字符串由任意可打印字符构成          第二个参数表示返回字符串长度

--4、ceil( n )函数是返回大于或等于n的最小整数。
/*
DBMS_RANDOM.VALUE()是随机产生( 0,1 )之间的数。要产生两位的随机数,可以DBMS_RANDOM.VALUE()*100,这样产生( 0,100 )的随机数,当产生( 0,10)之间的数时,只要加上10就可以保证产生的数都是两位了。
*/
--ORACLE的PL/SQL提供了生成随机数和随机字符串的多种方式,罗列如下:
--1、小数( 0 ~ 1)
     select dbms_random.value from dual;
--2、指定范围内的小数 ( 0 ~ 100 )      
	select dbms_random.value(0,100) from dual
--3、指定范围内的整数 ( 0 ~ 100 )
     select trunc(dbms_random.value(0,100)) from dual
--4、长度为20的随机数字串
     select substr(cast(dbms_random.value as varchar2(38)),3,20) from dual
--5、正态分布的随机数
     select dbms_random.normal from dual
--6、随机字符串
     select dbms_random.string(opt, length) from dual
      --opt可取值如下:       'u','U'    :    大写字母       'l','L'    :    小写字母       'a','A'    :    大、小写字母       'x','X'    :    数字、大写字母       'p','P'    :    可打印字符
--7、随机日期
     select to_date(2454084+TRUNC(DBMS_RANDOM.VALUE(0,365)),'J') from dual
     通过下面的语句获得指定日期的基数
     select to_char(sysdate,'J') from dual
--8、生成GUID
     select sys_guid() from dual
--生成带分隔符(-)的GUID的自定义函数 
	create or replace function my_guid return varchar2 is     guid varchar(36);     temp varchar(32); begin     temp:=sys_guid();     guid:= substr(temp,1,8) || '-'          ||substr(temp,9,4) || '-'          ||substr(temp,13,4)|| '-'          ||substr(temp,17,4)|| '-'          ||substr(temp,21,12);     return guid; end;

11.2 数据库导入,导出

  • 导出/导入的时候考虑到用户下的表可能存在多个表空间,导致数据迁移存在异常,所以可以先查询表所涉及到的表空间,然后导入的时候合并一下
SELECT table_name, tablespace_name
FROM all_tables
WHERE owner = '用户名';

exp 导出 imp 导入

遇到密码特殊字符 使用3个双引号

exp sdt_xjb/"""Ahsbd@2021yjfgs"""@172.16.10.247:1521/yjdb file=D:\temp\ydxj_dundian_20211118.dmp tables=(xjb_dundian_%)

exp 导出 imp 导入 (EXP和IMP是客户端工具程序,它们既可以在客户端使用,也可以在服务端使用。EXP不会导出空表,可能会对存储过程有影响)

1.导出:exp sdt_imoa/sdt_imoa@192.168.10.228/yjdb2 file=d:\sdt_imoa_20180831.dmp   tables=(ydxj_dundian_%)   rows=n
    说明:	1.sdt_imoa/sdt_imoa  是用户名和密码   
           2.@192.168.10.228/yjdb2  对应连接数据库时的路径192.168.10.229:1521/yjdb
           3.tables=(BAS_TOWER_%,BAS_FROMPART_TYPE,BAS_TYPEFROM_REF)  指定导出表,支持模糊匹配
           4.rows=n 表示只要表结构,不要数据
    案例:将数据库中system用户与sys用户的表导出
   		exp system/manager@TEST file=d:\daochu.dmp owner=(system,sys)
   	案例:将数据库中的表table1中的字段filed1以"00"打头的数据导出
   		exp system/manager@TEST file=d:\daochu.dmp tables=(table1) query=\" where filed1 like '00%'\"
2.导入: imp sdt_tyxxpt/sdt_tyxxpt@61.191.56.150:1521/xjb  file=f:\sdt_imoa2018-08-31.dmp full=y  data_only=y log=E:\20191127.log ;
    说明:1.rows=n 只导表结构
         2.data_only=y 只导数据
         3.ignore=y只导入数据,不导入表结构(没有的表,创建并倒入数据,如果已经有的表,忽略创建的,但不忽略倒入)
         4. 日志输出 log
        
     案例: 将d:\daochu.dmp中的表table1 导入
 		imp system/manager@TEST  file=d:\daochu.dmp  tables=(table1)
	如果导入的表空间不同,导入数据会报错,那么可以先只导入表结构,然后通过imp  导入表数据
3.指定表空间导入,没测试过(针对全数据库导出,先需要导出特定的用户,fromuser=用户)
imp CPMS_INVO/CPMS_INVO@36.7.176.72:11521/xjb fromuser=invoice touser=CPMS_INVO  file=E:\invo_20191127.dmp  log=E:\20191127.log
fromuser指定dmp文件中要导出的用户,touser 准备导入表的 数据用户 

存在问题

--1.在建好数据库后执行如下脚本:
	alter system set deferred_segment_creation=false;
--2:批量执行空表更新语句
    --A: 查询当前用户下的所有记录数为空的表
    select TABLE_NAME from user_tables t where t.NUM_ROWS = 0 or t.NUM_ROWS is null;
    --B:生成修改语句,将SEGMENT_CREATED 修改为YES
    select 'alter table '||table_name||' allocate extent;' from user_tables t where t.NUM_ROWS = 0 or t.NUM_ROWS is null;
--参考网址:https://blog.csdn.net/whxlovexue/article/details/82378389

imp/impdp 和 EXPDP和IMPDP时应该注意的事项:

  • EXP和IMP是客户端工具程序,它们既可以在客户端使用,也可以在服务端使用。
  • EXPDP和IMPDP是服务端的工具程序,他们只能在ORACLE服务端使用,不能在客户端使用。
  • IMP只适用于EXP导出的文件,不适用于EXPDP导出文件;IMPDP只适用于EXPDP导出的文件,而不适用于EXP导出文件。

数据泵:expdp/impdp导出/导入数据

数据库恢复(导入):需要将之前的数据库清除,因为不能覆盖恢复,所以会导出失败

数据泵:expdp/impdp导出/导入数据

https://www.cnblogs.com/wanghongyun/p/6307652.htmlopen in new window

https://blog.csdn.net/gty931008/article/details/86160808open in new window

https://blog.csdn.net/gty931008/article/details/86160808open in new window

使用impdp命令,需要在oracle数据库服务器操作:
1.使用sqlplus或者Oracle客户端(PL/SQL) 链接到相应的Oracle数据库实例(进行下面第一,第二两步骤的操作)
    一、创建逻辑目录,该命令不会在操作系统创建真正的目录,最好以system等管理员创建。
    	创建:create directory dpdata1 as 'd:\temp';
    	修改:create or replace directory dpdata1 as 'E:\temp';
		删除:drop directory dpdata1;
    (查看服务器上若没有存在d:\temp目录,则手动新建,把dmp备份文件放到d:\temp目录下面)
    二、查看管理理员目录(同时查看操作系统是否存在,因为Oracle并不关心该目录是否存在,如果不存在,则出错)
    	select * from dba_directories;
2.导入数据库(桌面下执行cmd--然后命令)
--expdp导出数据
	expdp CCENSE/CCENSE@OracleDB directory = "dpdata1" dumpfile ="oracleExpdp.dmp" logfile = oracleExpdp20161103.log
	
	expdp CCENSE/CCENSE@OracleDB directory = "dpdata1" dumpfile ="oracleExpdp.dmp" logfile = oracleExpdp20161103.log schemas=SDT_IMOA  tablespaces=YOUR_TABLESPACE_NAME
		/*
		* schemas=SDT_IMOA  指定导出用户
		* tablespaces=YOUR_TABLESPACE_NAME 指定导出表空间
		*/	
--impdp导入数据
	impdp CCENSE/CCENSE@OracleDB directory="dpdata1" dumpfile="oracleExpdp.dmp" logfile = oracleImpdp20161103.log FULL=y;

/* 说明:CCENSE/CCENSE@OracleDB --->用户名/密码@数据库实例
        directory="在步骤一中创建的逻辑目录"
        dumpfile="需要导入/导出的dmp文件全称"
        logfile="日志文件"
        tables=xxx,xxx  指定导出表
        FULL=y;
        针对密码存在特殊字符的 expdp sdt_imoa/"""sdt_zhst@Ahsbd"""@xxx/yjdb
*/
impdp CPMS_0309/CPMS_0309@127.0.0.1:1521/orcl directory = dpdata1 dumpfile =CPMS_0309.dmp logfile = CPMS_0309.log REMAP_SCHEMA=CPMS:CPMS_0309 remap_tablespace=CPMS:CPMS_0309
--  讲CPMS正式数据库的表导入到 CPMS_0309用户下,impdp可以指定 需要导入的表空间和用户
	REMAP_SCHEMA=CPMS:CPMS_0309 表示 由原来的 CPMS用户导入到CPMS_0309用户下,   remap_tablespace=CPMS:CPMS_0309表示 由原来的CPMS表空间导入到CPMS_0309表空间下
	但是 可能存在 告警CPMS.xxx 表存在的问题,可以添加 TABLE_EXISTS_ACTION=REPLACE
	impdp ... REMAP_SCHEMA=CPMS:CPMS_0309 remap_tablespace=CPMS:CPMS_0309 TABLE_EXISTS_ACTION=REPLACE

--其他SQL
--表空间[10G]和临时表空间[1G]
/*表空间*/
	SELECT t.tablespace_name, round(SUM(bytes / (1024 * 1024)), 0) ts_size 
	FROM dba_tablespaces t, dba_data_files d 
	WHERE t.tablespace_name = d.tablespace_name 
	GROUP BY t.tablespace_name;

/*临时表空间*/
	select tablespace_name,file_name,bytes/1024/1024 file_size,autoextensible from dba_temp_files;

-- 查看端口是否被占用
	netstat -aon|findstr "1522"

-- 查看监听状态,启动监听,关闭监听
	lsnrctl status LISTENER2
	lsnrctl start LISTENER2 
	lsnrctl stop LISTENER2

--访问地址
	linux sqlplus ccense/ccense@//localhost:1521/OracleDB;
	win sqlplus ccense/ccense@IP:1521/OracleDB;

expdp流程
image-20210901103806295
image-20210901103806295
一、新建逻辑目录

最好以system等管理员创建逻辑目录,Oracle不会自动创建实际的物理目录“D:\oracleData”(务必手动创建此目录),仅仅是进行定义逻辑路径dump_dir;###

忘记sys用户密码的可以去下如何修改sys用户密码;建议使用pl、navicat等oracle操作工具来操作;

登陆后sql执行:create directory mydata as '逻辑目录路径'; 例如:

create directory mydata as '/data/oracle/oradata/mydata';
二、查看逻辑目录是否创建成功

执行sql:

select * from dba_directories
image-20210901104203457
image-20210901104203457
三、用expdp导出数据 用法及解释:

expdp 用户名/密码@ip地址/实例 [属性]

ip地址不写默认就是本地 userid=test/test --导出的用户,本地用户!! directory=dmpfile --导出的逻辑目录,一定要在oracle中创建完成的,并且给用户授权读写权限 dumpfile=xx.dmp --导出的数据文件的名称,如果想在指定的位置的话可以写成dumpfile=/home/oracle/userxx.dmp logfile=xx.log --日志文件,如果不写这个参数的话默认名称就是export.log,可以在本地的文件夹中找到 schemas=userxx --使用dblink导出的用户不是本地的用户,需要加上schema来确定导出的用户,类似于exp中的owner,但还有一定的区别 EXCLUDE=TABLE:"IN('T1','T2','T3')" --exclude 可以指定不导出的东西,table,index等,后面加上不想导出的表名 network_link=db_local --这个参数是使用的dblink来远程导出,需要指定dblink的名称

列出一些场景:

--1)导出用户及其对象
expdp scott/tiger@orcl schemas=scott dumpfile=expdp.dmp directory=dump_dir logfile=expdp.log;

--2)导出指定表
expdp scott/tiger@orcl tables=emp,dept dumpfile=expdp.dmp directory=dump_dir logfile=expdp.log;

--3)按查询条件导
expdp scott/tiger@orcl directory=dump_dir dumpfile=expdp.dmp tables=empquery='where deptno=20' logfile=expdp.log;

--4)按表空间导
expdp system/manager@orcl directory=dump_dir dumpfile=tablespace.dmp tablespaces=temp,example logfile=expdp.log;

--5)导整个数据库
expdp scott/123@127.0.0.1/orcl directory=dump_dir dumpfile=ly.dmp full=y  logfile=expdp.log;

一般用的都是导出整个数据库

--包含所有用户的表、视图、索引等
expdp JCPT/123@127.0.0.1/orcl directory=mydata dumpfile=ly.dmp full=y  logfile=expdp.log;
--指定用户的表、视图、索引等
expdp JCPT/123@127.0.0.1/orcl directory=mydata schemas=jcpt dumpfile=ly.dmp logfile=expdp.log;--

导出完成后:逻辑目录生成了一个 dmp文件;

IMPDP数据导入

在正式导入数据前,要先确保要导入的用户已存在,如果没有存在,请先用下述命令进行新建用户

流程:

image-20210901104726385
image-20210901104726385
一、创建表空间

使用system登录oracle,执行sql

格式: create tablespace 表间名 datafile '数据文件名' size 表空间大小

create tablespace data_test datafile 'e:\oracle\oradata\test\test.dbf' size 2000M;
-- 数据文件名 包含全路径, 表空间大小 2000M 表是 2000兆
二、创建用户并授权

格式: create user 用户名 identified by 密码 default tablespace 表空间表;

create user study identified by study default tablespace data_test;
-- 我们创建一个用户名为 study,密码为 study, 表空间为 madate-这是在上一步建好的       

授权给 用户 study ,执行sql

#给用户逻辑目录读写权限
sql>grant read,write on directory mydata to study;
#给用户表空间权限
sql>grant dba,resource,unlimited tablespace to study;
三、impdp导入

命令在cmd或者控制台输入,不是sql语句

写法:

impdp 用户名/密码@ip地址/实例 [属性]

ip地址不写默认就是本地

注释:

-- 1)导入用户(从用户scott导入到用户scott)
impdp scott/tiger@orcl directory=dump_dir dumpfile=expdp.dmp schemas=scott logfile=impdp.log;

-- 2)导入表(从scott用户中把表dept和emp导入到system用户中)
impdp system/manager@orcl directory=dump_dir dumpfile=expdp.dmp tables=scott.dept,scott.emp remap_schema=scott:system logfile=impdp.log table_exists_action=replace (表空间已存在则替换);

-- 3)导入表空间
impdp system/manager@orcl directory=dump_dir dumpfile=tablespace.dmp tablespaces=example logfile=impdp.log;

-- 4)导入整个数据库
impdb system/manager@orcl directory=dump_dir dumpfile=full.dmp full=y logfile=impdp.log;
-- 5)追加数据
impdp system/manager@orcl directory=dump_dir dumpfile=expdp.dmp schemas=systemtable_exists_action logfile=impdp.log;

日常使用的:

把用户jcpt中所有的表导入到lyxt用户下

impdp lyxt/lyxt123@127.0.0.1/orcl directory=mydata dumpfile=LY.DMP   remap_schema=jcpt:lyxt logfile=ims20171122.log table_exists_action=replace

11.10 数据误删

表中数据误删数据

场景: 现有 material_info 表 一条数据 被删除,知道大概的时间和关键字段 ‘攀登自锁器’

select * from material_info as of timestamp to_timestamp('2021-10-28 15:30:00', 'yyyy-mm-dd hh24:mi:ss')
where material_name like '%攀登自锁器%'

to_timestamp: 删除数据的大概时间 ,material_name 是表中字段,用来筛选数据的

表误删

11.52 数据库备份单个表

关键字 复制表、备份表

create table WORK_TASK_LIST__GROUNDWIRE_6 as select * from WORK_TASK_LIST__GROUNDWIRE;
create table myTable_tmpe as select * from myTable  where 1=2;-- 只备份表结构,不复制表数据
create table sys_emp_1 as  select * from sys_emp@xjbdblink;-- 使用dblink	

11.53 oracle常用 函数

  • replace 替换
select replace('123123tech', '123') from dual; --返回 'tech'============将 字符串 123123tech中123 去掉  最终结果是tech
select replace('123tech123', '123') from dual; --返回 'tech'
select replace('222tech', '2', '3') from dual; --返回 '333tech'============将字符串 222tech  中的2 替换为 3 最终结果是 333tech
select replace('0000123', '0') from dual; --返回 '123'
select replace('0000123', '0', ' ') from dual; --返回 ' 123'
  • 截取 SUBSTR & INSTR

案例:截取 人员头像中的图片名称

select name,photo,substr(photo,instr(photo, '/',-1)+1),id from SYS_USER t  where t.del_flag=0;
名称头像路径被截取文件名称
aa/userfiles/images/idCard/UserCertificate/1607081013003205402.jpg1607081013003205402.jpg
bb/userfiles/images/idCard/UserCertificate/1304011138456401591.jpg1304011138456401591.jpg
cc/userfiles/images/idCard/UserCertificate/1304110856309061782.jpg1304110856309061782.jpg

案例:身份证号加密显示

SELECT DECODE(LENGTH(IDENTIFICATION),18,REPLACE(IDENTIFICATION,SUBSTR(IDENTIFICATION, 5, 10),'**********'),
              15,REPLACE(IDENTIFICATION, SUBSTR(IDENTIFICATION, 5, 7),'*******')) 身份证号,
               DECODE(LENGTH(mobile_number),11,REPLACE(mobile_number,SUBSTR(mobile_number, 8, 11),'****'),mobile_number) 手机号
  FROM sys_emp;
  • ceil() /ceiling() 向上取整

     ceil(1.2) = 2
    
  • floor () 向下取整

     floor(1.2) = 1
    
  • round() 四舍五入

11.54 开窗函数,排序取最新

【Oracle】OVER(PARTITION BY)函数用法open in new window 开窗函数 https://www.cnblogs.com/ruiser/p/5687238.htmlopen in new window

Oracle从8.1.6开始提供分析函数

11.55 取分组取第一条记录(关键字:取最新,取第一条数据,取最后一条)

切记用 row_number 而不是 DENSE_RANK

**注:**切记 使用时不能left join 关联其他表

最优方法:
 select *
          from (select USERID,
                       JD_GPS,
                       WD_GPS,
                       dwsj,
                       row_number() over(partition by USERID order by dwsj desc) mm
                  from ydxj_dw_dwjl_2016)
         where mm = 1 // 要排过序后 才能 取第一条,不能直接就用rownum = 1    这样取出的数据,和没排序的一样
方法2
select a.*
  from ydxj_dw_dwjl_2016 a
 where not exists (select 1
          from ydxj_dw_dwjl_2016 b
         where b.userid = a.userid             
           and b.dwsj > a.dwsj)
           
方法3 用于 ,mysql没有 row_number函数时         
select  t.*
from ydxj_dw_dwjl_2016 t
where dwsj = (
    select max(t1.dwsj) from proj_base_trajectory t1 where t1.USERID = t.USERID
)           

11.60 列转行

日期行转列

SELECT TO_DATE('2021-05-01', 'YYYY-MM-DD') + ROWNUM - 1 DT FROM DUAL
          CONNECT BY LEVEL <= (TO_DATE('2021-05-31', 'YYYY-MM-DD') - TO_DATE('2021-05-01', 'YYYY-MM-DD') + 1)

指定字符串 分割,转行

SELECT  REGEXP_SUBSTR('1,2,3', '[^,]+', 1, LEVEL) NAME
FROM dual
CONNECT BY LEVEL <= REGEXP_COUNT('1,2,3', '[^,]+')

通过 GROUP_CONCAT

SELECT GROUP_CONCAT(user_id SEPARATOR ',') AS user_ids
FROM yj_zhst_user
  • 如果数据量非常大,注意 GROUP_CONCAT 的默认长度限制,在 MySQL 中可以通过以下方式增大限制:
SET SESSION group_concat_max_len = 1000000;

如果

案例:

工作票接地线表每个 条记录 多个接地线直接用逗号相连,现在要获取 当前工作票 选用的所以的接地线 思路:先讲数据行转列,然后再列转行,使用in

image-20210602194206941
image-20210602194206941
-- 先将 指定 工作票的接地线全部查询出来,然后逗号相连
with jdx_use_temp as
 (select wm_concat(t.groundwire_no) jdx_code
    from PLAN_WORK_TICKET_GROUNDWIRE t
   where del_flag = 0
     and ticket_id = 'd439b3cd225c4667a550f172844a397a')
select *
  from jdx_device t
 where t.code in
       (SELECT REGEXP_SUBSTR(jdx_code, '[^,]+', 1, LEVEL) NAME  -- 列转行,使用in
          FROM jdx_use_temp
        CONNECT BY LEVEL <= REGEXP_COUNT(jdx_code, '[^,]+'))

**注:**偶尔会出现死循环,目前遇到过一次,是因为不能加where 条件,可以使用子查询

​ 例如 FROM jdx_use_temp --> FROM (select xx from jdx_use_temp where xx=xx)

myBatis 使用collection

< foreach collection="lineIds.split(',')" item="lineId" index="index" separator=", "> 目前最优,对数据格式没有过多要求

 <select id="findLineByIds" resultMap="lineVOMap">
        select t.id,
           t.tower_pmsid,
           t.line_id,
           t.tower_name,
           t.tower_no,
           l.line_name
      from BAS_C_TOWER t,bas_c_line l
      where t.line_id=l.id
          <if test="lineIds != null and lineIds != ''">
            AND t.LINE_ID in (
              <foreach collection="lineIds.split(',')" item="lineId" index="index" separator=", ">
                    #{lineId}
                </foreach>
            )
        </if>
        <if test="towerIds != null and towerIds != ''">
            AND t.LINE_ID in (
                select distinct line_id from bas_c_tower where id in (
                    <foreach collection="towerIds.split(',')" item="towerId" index="index" separator=", ">
                        #{towerId}
                    </foreach>
                 )
            )
        </if>
        order by  t.line_id, t.tower_no
    </select>
 
    <!-- 批量更新第一种方法,通过接收传进来的参数list进行循环着组装sql -->
     <update id="updateBatch" parameterType="java.util.List" >
        <foreach collection="list" item="item" index="index" open="" close="" separator=";">
            update standard_relation
            <set >
                <if test="item.standardFromUuid != null" >
                    standard_from_uuid = #{item.standardFromUuid,jdbcType=VARCHAR},
                </if>
                <if test="item.standardToUuid != null" >
                    standard_to_uuid = #{item.standardToUuid,jdbcType=VARCHAR},
                </if>
                <if test="item.gmtModified != null" >
                    gmt_modified = #{item.gmtModified,jdbcType=TIMESTAMP},
                </if>
            </set>
            where id = #{item.id,jdbcType=BIGINT}
        </foreach>
    </update>
 
    <!-- 批量更新第二种方法,通过 case when语句变相的进行批量更新 -->
    <update id="updateBatch" parameterType="java.util.List" >
        update standard_relation
        <trim prefix="set" suffixOverrides=",">
            <trim prefix="standard_from_uuid =case" suffix="end,">
                <foreach collection="list" item="i" index="index">
                    <if test="i.standardFromUuid!=null">
                        when id=#{i.id} then #{i.standardFromUuid}
                    </if>
                </foreach>
            </trim>
            <trim prefix="standard_to_uuid =case" suffix="end,">
                <foreach collection="list" item="i" index="index">
                    <if test="i.standardToUuid!=null">
                        when id=#{i.id} then #{i.standardToUuid}
                    </if>
                </foreach>
            </trim>
            <trim prefix="gmt_modified =case" suffix="end,">
                <foreach collection="list" item="i" index="index">
                    <if test="i.gmtModified!=null">
                        when id=#{i.id} then #{i.gmtModified}
                    </if>
                </foreach>
            </trim>
        </trim>
        where
        <foreach collection="list" separator="or" item="i" index="index" >
            id=#{i.id}
        </foreach>
    </update>
批量更新第三种方法,用ON DUPLICATE KEY UPDATE
 <insert id="updateBatch" parameterType="java.util.List">
        insert into standard_relation(id,relation_type, standard_from_uuid,
        standard_to_uuid, relation_score, stat,
        last_process_id, is_deleted, gmt_created,
        gmt_modified,relation_desc)VALUES
        <foreach collection="list" item="item" index="index" separator=",">
            (#{item.id,jdbcType=BIGINT},#{item.relationType,jdbcType=VARCHAR}, #{item.standardFromUuid,jdbcType=VARCHAR},
            #{item.standardToUuid,jdbcType=VARCHAR}, #{item.relationScore,jdbcType=DECIMAL}, #{item.stat,jdbcType=TINYINT},
            #{item.lastProcessId,jdbcType=BIGINT}, #{item.isDeleted,jdbcType=TINYINT}, #{item.gmtCreated,jdbcType=TIMESTAMP},
            #{item.gmtModified,jdbcType=TIMESTAMP},#{item.relationDesc,jdbcType=VARCHAR})
        </foreach>
        ON DUPLICATE KEY UPDATE
        id=VALUES(id),relation_type = VALUES(relation_type),standard_from_uuid = VALUES(standard_from_uuid),standard_to_uuid = VALUES(standard_to_uuid),
        relation_score = VALUES(relation_score),stat = VALUES(stat),last_process_id = VALUES(last_process_id),
        is_deleted = VALUES(is_deleted),gmt_created = VALUES(gmt_created),
        gmt_modified = VALUES(gmt_modified),relation_desc = VALUES(relation_desc)
    </insert>

通过 sum 来 实现 列传行

场景说明:地图 我的附件,查询出 当前 驿站(yz)1个、人员2个 、GT 2个 、风险 2个

查询出 4条 不同类型的 记录,这种情况前端处理数据非常不方便,需要遍历。故需要将其转为 Map 对象,数据拉平

image-20210726192122389
image-20210726192122389

造数据

select 'yz' as  type,1 as count_num from dual-- 驿站
union all	
select 'user' as  type,2 as count_num from dual--人员
union all	
select 'tower' as  type,2 as count_num from dual-- GT
union all	
select 'fx' as  type,2 as count_num from dual-- 风险

目前效果

yz_numuser_numtower_numfx_num
1222

说明: 可以用decode,但是 MySql 不能使用,只能用case when

select
	sum(case when type='yz' then count_num else 0 end) as yz_num,-- 也可以用 decode 
	sum(case when type='user' then count_num else 0 end) as user_num,
	sum(case when type='tower' then count_num else 0 end) as tower_num,
	sum(case when type='fx' then count_num else 0 end) as fx_num
from (
    select 'yz' as  type,1 as count_num from dual-- 驿站
    union all	
    select 'user' as  type,2 as count_num from dual--人员
    union all	
    select 'tower' as  type,2 as count_num from dual-- GT
    union all	
    select 'fx' as  type,2 as count_num from dual-- 风险
)aa

案例: 对人员岗位归类,positionName多个岗位逗号拼接的,如果存在 总监理工程师--> 总结,否则 总监代表--> 总代 ,否则 监理员 --> 监理员 ,都不是 则返回其他

select b.emp_id,
       case
         when INSTR(b.positionName, '总监理工程师') > 0 then '总监' --总监
         when INSTR(b.positionName, '总监代表') > 0 then '总代' --总代
         when INSTR(b.positionName, '监理员') > 0 then '监理员' --监理员
         ELSE '其他' END as position
  from v_user_position b
 where b.positionName is not null

11.61 行转列

  • wm_concat 实现行转列(存在限定,拼接超过4000后异常)

    mysql 等价的是 GROUP_CONCAT 函数

select t.office_id,
       wm_concat(t.area_id) areaIds, --replace(wm_concat(area_id),',','|')  通过 replace替换成需要的连接字符串
       wm_concat(to_char(a.name)) as areaNames-- 对于中文可能会出现 转义
  from ZHCG_OFFICE_MANAGE_AREA t, sys_area a
 where t.del_flag = '0'
   and a.del_flag = '0'
   and t.area_id = a.id
 group by office_id
  • xmlagg

wm_concat ---函数实现字段合并 将返回的多条数据,,合并到一起,行转列,但是存在一个问题,拼接超过4000后异常

1.select XMLAGG(XMLELEMENT(E, name || ',')).EXTRACT('//text()').getclobval() as names from sys_user 但是好像不能使用分组函数

  1. xmlagg(xmlparse(content 拼接字段 || ','wellformed) order by 排序字段).getclobval()

案例:

SELECT 
	rtrim(xmlagg(xmlparse(content u.emp_name || ',' wellformed)  ) .getclobval(), ',') userNames,
	rtrim(xmlagg(xmlparse(content p.emp_id || ',' wellformed)  ) .getclobval(), ',') userIds,
	p.RULE_ID
 from PROJ_BASE_ATTENDANCE_PERSON p
 left join sys_emp u on u.id=p.emp_id
 group by p.RULE_ID

注: rtrim(字段, ',') 去掉右边最后一个逗号

通过xmlagg讲数据转为clob ,也会存在字符缓存去限定长度

dbms_lob.substr( substr(rtrim(xmlagg(xmlparse(content a.idopen in new window || ',' wellformed)) .getclobval(),',') ,0,200))

listagg 函数

SELECT listagg(t.ename,',') WITHIN GROUP(ORDER BY t.sal) FROM scott.emp t group by xxx;
  • 使用REGEXP_SUBSTR
--1.
SELECT REGEXP_SUBSTR ('1,2,3', '[^,]+', 1,rownum)
from dual connect by rownum<=LENGTH ('1,2,3') - LENGTH (regexp_replace('1,2,3', ',', ''))+1;


-- 2.
select REGEXP_SUBSTR(t.rolecode, '[^,]+', 1, l) type
  from (select '1,2' rolecode from dual) t,
       (SELECT LEVEL l FROM DUAL CONNECT BY LEVEL <= 100) b
 WHERE l <= LENGTH(t.rolecode) - LENGTH(REPLACE(rolecode, ',')) + 1

案例场景: 前端传递一个逗号拼接的字符串,sql中需要用in

 select pe.emp_id from SYS_EMP_POST_EMP pe 
 where pe.position_id in (
         SELECT REGEXP_SUBSTR (${positionIds}, '[^,]+', 1,rownum)
		   from dual connect by rownum <![CDATA[ <= ]]> LENGTH (${positionIds}) - LENGTH (regexp_replace(${positionIds}, ',', ''))+1
 	)   
 	
 	
--'26438E087E604071886C69BC39B8A176,4358B8B6E93F4119847FF73766B72733'

11.65 ORACLE WITH AS 用法

ORACLE WITH AS 用法open in new window

with as优点 (相当于 创建一个临时表,然后后面可以直接调用 ) 增加了sql的易读性,如果构造了多个子查询,结构会更清晰; 更重要的是:“一次分析,多次使用”,这也是为什么会提供性能的地方,达到了“少读”的目标;

with e as (select * from scott.emp e where e.empno=7499)-- 相当于创建一个临时表
select * from e;


with
     e as (select * from scott.emp),
     d as (select * from scott.dept)
select * from e, d where e.deptno = d.deptno;

11.70 resultMap

resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询

<resultMap type="cn.semdo.modules.projjl.entity.ProjJlEngineer" id="projJlEngineerMap">  
    <id column="ID" property="id"/>  
    <id column="engineerName" property="engineerName"/>  

    <collection property="projJlPositionList" ofType="cn.semdo.modules.projjl.entity.ProjJlPosition" column="engineerId">  
        <id column="positionId" property="id"/>  
        <id column="engineerId" property="engineerId"/>
        <id column="positionName" property="name"/>

    </collection>  
</resultMap>


<select id="projJlEngineerList" resultMap="projJlEngineerMap">
    SELECT 
    a.id,
    a.engineer_Name as engineerName,
    p.id AS "positionId",
    p.engineer_id AS "engineerId",
    p.name AS "positionName"
    FROM proj_jl_engineer a 
    left join PROJ_JL_POSITION p on p.engineer_id = a.id
</select>
<!--column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性-->
<resultMap id="唯一的标识" type="映射的pojo对象">
  <id column="表的主键字段,或者可以为查询语句中的别名字段" jdbcType="字段类型" property="映射pojo对象的主键属性" />
  <result column="表的一个字段(可以为任意表的一个字段)" jdbcType="字段类型" property="映射到pojo对象的一个属性(须为type定义的pojo对象中的一个属性)"/>
  <association property="pojo的一个对象属性" javaType="pojo关联的pojo对象">
    <id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo对象的主席属性"/>
    <result  column="任意表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
  </association>
  <!-- 集合中的property须为oftype定义的pojo对象的属性-->
  <collection property="pojo的集合属性" ofType="集合中的pojo对象">
    <id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
    <result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />  
  </collection>
</resultMap>

使用:Map<String, Object> getYhBihuanStatisData(JSONObject params);

    <resultMap type="java.util.Map" id="yhBihuanStatisDataResult">
        <result property="yhs" column="yhs" javaType="java.lang.Long" />
        <result property="ybh" column="ybh" javaType="java.lang.Long" />
    </resultMap>

    <select id="getYhBihuanStatisData" resultMap="yhBihuanStatisDataResult" parameterType="com.alibaba.fastjson.JSONObject">
        SELECT
            count(1) yhs,
            sum(case status when '5' then 1 else 0 end) ybh,
            sum(case  when  FIND_TIME like '2022-09%' then 1 else 0 end) byxz,
            sum(case  when  FIND_TIME like '2022-09%' and status = '5' then 1 else 0 end) bybh
        FROM yh_op_record
        WHERE del_flag = '0'
        <!-- status:隐患状态
            1:隐患库(暂存)   2:上报审核中   3:隐患管控中   4:闭环审核中
            5:已闭环(正常)  6:重复上报(DEL_FLAG设为1)  7:误报(DEL_FLAG设为1)
        -->
        AND status != '1'
        <if test="opTeamId!=null and opTeamId != ''">
            AND op_team_id = #{opTeamId}
        </if>
        <if test="opOrgId!=null and opOrgId != ''">
            AND op_org_id = #{opOrgId}
        </if>
    </select>

通过java 构造

// 以 parentId 分组(使用并行流)
Map<Long, List<TreeNode>> parentMap = nodeList.parallelStream()
    .collect(Collectors.groupingByConcurrent(TreeNode::getParentId));

// 根节点列表
List<TreeNode> rootList = parentMap.get(0L);

// 递归构造树形结构
if (rootList != null) {
    rootList.parallelStream().forEach(root -> {
        buildTree(root, parentMap);
    });
}






/**
 * 递归构造树形结构
 *
 * @param node 节点
 * @param parentMap 根据 parentId 分组的 Map
 */
private static void buildTree(TreeNode node, Map<Long, List<TreeNode>> parentMap) {
    List<TreeNode> childList = parentMap.get(node.getId());
    node.setChildren(childList);
    if (childList != null) {
        childList.forEach(child -> {
            buildTree(child, parentMap);
        });
    }
}

工具类

通过java 构造树结构

List<TreeBean> tree = JavaFormatTreeUtil.getTree(list, "id",officeId); 

JavaFormatTreeUtil.java

js 构造

	/**
	 * 组装树结构
	 * 考虑该功能可能需要离线使用,所以将数据获取到,通过js递归的形式,来组装数据
	 */
var getTree=function(treeData,parentId){
    var treeArr=[];
    for(var i=0;i<treeData.length;i++){ 
        var node=treeData[i];
        if(node.parentId==parentId ){ 
            var newNode={
                id:node.id,
                name:node.name,
                parentId:node.parentId,
                type:node.type,
                nodeType:node.nodeType,
                children:getTree(treeData,node.id)
            };
            treeArr.push(newNode);              
        }
    }
    return treeArr;
}

var treeArr=getTree(data,0);//将后台返回的数据,组装成树结构

resultMap 构造树结构

image-20230607145407562
image-20230607145407562
    <resultMap id="selectMaterialMap" type="cn.semdo.gdzjz.detectionManage.domain.vo.MaterialVO">
        <result column="dev_id" property="id"/>
        <result column="material_name" property="materialName"/>
        <result column="deviceCode" property="deviceCode"/>
        <result column="code" property="code"/>
        <collection property="expandVOList" resultMap="expandResult" javaType="java.util.List"/>
    </resultMap>
    <resultMap id="expandResult" type="cn.semdo.gdzjz.detectionManage.domain.vo.MaterialExpandVO">
        <result property="operator" column="operator"/>
        <result property="parCode" column="par_code"/>
        <result property="parName" column="par_name"/>
    </resultMap>

    <select id="selectMaterialData" resultMap="selectMaterialMap">
        select  e.par_code,e.par_name,e.operator,d.dev_id,info.material_name,t.code,info.material_Code as deviceCode
        from zjjc_detection_device_relation d
         inner join material_info info on info.id=d.dev_id
         left join material_type t on t.id=info.typeid
         left join material_info_expand e on e.material_id=d.dev_id
        where d.del_flag=0 and d.proj_id=#{proId}
    </select>

11.80 特殊字符

  • 大于、小于 <![CDATA[ <= ]]>

11.81 oracle 查询记录为空,返回默认值 --- 在创建任务单单号的时候的应用

场景: 查询时 如果查询到那么就用查询的结果,如果查询不到,那么就给定一个默认值

分2中情况

1.可能不存在返回值 ,即一条数据都没有:

案例 表 dsj_line_tower_relation 存放 线路表存在的线路和 GT表中实际的线路

select nvl(MAX(t.tower_line_id),'bf3c8dd107a04d918a1d7f59365ccc26') from dsj_line_tower_relation  t where line_id='bf3c8dd107a04d918a1d7f59365ccc26' --(使用max一定会有返回值,这样搭配 nvl 就可以实现返回值一定有结果)

传递我们指定的线路id,如果在****dsj_line_tower_relation**** 表中存在记录,那么就用查询的tower_line_id 否则,我们就返回我们传递的参数

img
img

查询时没有任何返回值,那么时不能用nvl() 来实现上面的效果

  1. 一定存在返回值,但是可能存在为空 select nvl(t.line_id,0) from ..................
img
img

11.90 存储过程

场景:每天根据 V_USER_WEIPAI 表(存放设备主人,GT,设备id,应该巡视次数)

https://blog.csdn.net/dwenxue/article/details/82257944open in new window 创建存储过程(带参数)

https://blog.csdn.net/qq_38974634/article/details/81141845open in new window

create or replace procedure PROC_WEIPAI_OP_EVERY_DAY as
 --以机代巡中 设备巡视 每天统计
    v_op_num NUMBER(10); -- 每天对应的 设备应拍次数(设置变量,下文中 会用到上面的查询结果)
    begin
      select nvl(max(OP_NUM), 3) into v_op_num from YDXJ_JOB_OP_NUM where trunc(OP_TIME) = trunc(SYSDATE); -- 每天对应的 设备应拍次数  赋值 (nvl(max(OP_NUM),0) 保证一定有值)
      DELETE FROM YDXJ_WEIPAI_OP_COUNT_DAY WHERE trunc(OP_TIME) = trunc(SYSDATE); -- 删除今天数据,防止 数据重复添加
      insert into ydxj_weipai_op_count_day (user_id, weipai_id, tower_id, op_time, should_op_num, op_num)
       select a.userid, a.WEIPAIRECORD_ID, a.tower_id, sysdate,
             v_op_num, -- 每个设备 应该巡视次数 (是上面语句中查询出来的数值)
             nvl(op_.op_num,0) --每个设备 实际巡视次数
        from V_USER_WEIPAI a
        left join (select count(op.opid) as op_num, WEIPAIRECORD_ID,op.OPUSERID---讲数据分组后 left join 会提高查询速度
                     from ydxj_job_op op
                    where trunc(OPTIME) = trunc(SYSDATE)
                      and op.WEIPAIRECORD_ID is not null
                    group by WEIPAIRECORD_ID,op.OPUSERID) op_
          on a.WEIPAIRECORD_ID = op_.WEIPAIRECORD_ID and op_.OPUSERID=a.userid;
      Commit;
    exception
  when others then
    raise;
    rollback;
end;

创建 能传递参数的 存储过程

 
create or replace procedure test_page(
    page_start in int,page_end in int,page_count out int,
    page_emps out sys_refcursor)
as
begin
    select count(*) into page_count from employees;    
    open page_emps for
        select * from 
               (select rownum rn,e.* from employees e
                 where rownum <= page_end)
        where rn >= page_start;
end test_page;      
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lxj.mapper.EmployeeMapper">
 
	<select id="getEmpById" resultType="com.lxj.bean.Employee">
		select EMPLOYEE_ID id,LAST_NAME lastName,EMAIL email from employees 
        where EMPLOYEE_ID = #{id}
	</select>
	
	<!--  public void getEmpsByPage(); 
	      statementType="CALLABLE":调用存储过程,默认是PREPARED
			call 不能丢,否则识别不出
	-->
	<select id="getEmpsByPage" statementType="CALLABLE">
	    {call test_page(
	        #{start,mode=IN,jdbcType=INTEGER},
	        #{end,mode=IN,jdbcType=INTEGER},
	        #{count,mode=OUT,jdbcType=INTEGER},
	        #{emps,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=TestPge}
	    )}
	</select>
	<resultMap type="com.lxj.bean.Employee" id="TestPge">
	   <id column="EMPLOYEE_ID" property="id"/>
	   <result column="LAST_NAME" property="lastName"/>
	   <result column="EMAIL" property="email"/>
	</resultMap>
</mapper>

11.91 数据库定时器

https://blog.csdn.net/u012209894/article/details/80432102open in new window

image-20210107084208506
image-20210107084208506

需要保证 两个数据库之间网络是相通的,而不是 执行plsql 的本地的电脑和 目标数据库之间想通

查询数据库中的dblink select * from dba_db_links;

删除 drop database LINK SDTXJB_DBLINK;

--使用:比如协同办公需要移动巡检中的线路表(bas_c_linedsa) sdt_tyxxpt.bas_c_line@SDT_TYXXPT_DBLINK
 create database link SDT_TYXXPT_DBLINK
  connect to sdt_tyxxpt identified by sdt_tyxxpt
     using '(DESCRIPTION =
      (ADDRESS_LIST =
       (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.10.225)(PORT = 1521))
      )
      (CONNECT_DATA =
       (SERVER = DEDICATED)
        (SID = yjdb2)
      )
    )';  
 create database link SDT_TYXXPT_DBLINK
  connect to sdt_tyxxpt identified by sdt_tyxxpt
     using '218.23.109.123:9522/yjdb'; 

19.110 oracle 自定义函数

参考https://blog.csdn.net/jumtre/article/details/38092067open in new window

--第一种不带参数:
create or replace function get_user 
return varchar2 is Result varchar2(50); --定义变量(返回值)
begin 
select username into Result from user_users; --into 赋值,不要忘了加分号结尾
return(Result); --返回值
end get_user;

--第二种带参:
create or replace function get_sal(empname in varchar2) 
return numbe is Result number; 
begin 
select sal into Result from emp where ename=empname; 
return(Result); 
end;

自定义函数验证是否为数字
https://blog.csdn.net/vincentlee_/article/details/15336557open in new window

CREATE OR REPLACE FUNCTION isnumeric(str IN VARCHAR2)
    RETURN NUMBER
IS
    v_str FLOAT;
BEGIN
    IF str IS NULL
    THEN
       RETURN 0;
    ELSE
       BEGIN
          SELECT TO_NUMBER (str)
            INTO v_str
            FROM DUAL;
       EXCEPTION	--异常
          WHEN INVALID_NUMBER
          THEN
             RETURN 0;
       END;
       RETURN 1;
    END IF;
END isnumeric;

经纬度距离计算

CREATE OR REPLACE FUNCTION F_DISTANCE (cx in number,cy in number,sx in number, sy in number) 
RETURN NUMBER
is
    d number;
    x number;
    y number;
    r number;
    pi number;
begin
  r:=6371229;--地球半径
  pi:=3.14159265358979323;--圆周率
  x:=(sx-cx)*pi*r*cos((sy+cy)/2*pi/180)/180;
  y:=(sy-cy)*pi*r/180;
  d:=SQRT(power(x,2)+power(y,2));
  D:=ROUND(D,2);
  RETURN d;
end ;

11.120 查询指定用户 表名+数据量+表备注

SELECT t.table_name, t.num_rows, f.comments 
FROM all_tables t
  INNER JOIN ( SELECT * FROM all_tab_comments WHERE OWNER = 'CPMS_DEV' ) f ON t.table_name = f.table_name 
WHERE t.OWNER = 'CPMS_DEV'

11.130 时间排序,存在null 导致 排列到最前面

Nulls first (记录将排在最前)和nulls last(记录将排在最后)是Oracle Order by支持的语法

select * from FORUM t order by last_reply_time desc nulls last ,id desc ;


select *    
  from (select row_number() over(partition by id order by receive_time desc nulls last) mm,a.*
          from sys_push_send_ref_temp a
 ) where mm = 1

decode

decode(filed,null,’张三’, filed)表示当filed为空时则返回’张三’,如果不为空则返回filed本来的值(不仅可以判断null,也可以判断特定的值)

使用 nvl

select * from FORUM t order by NVL(last_reply_time, to_date('1970-01-01 00:00:00','yyyy/MM/dd/hh24/mi/ss')) desc ;

11.140 案例:

消息 有多层级,现在需要 获取 根节点并展示指定用户最新的消息

with sys_push_send_ref_temp as(
     select root_modules_id as  id,root_modules_name as modulesName,root_modules_code,aa.modules_id,aa.name,root_msg_url_app as msgUrlApp,
            aa.root_modules_icon modulesIcon,bb.receive_status,bb.receive_time,bb.message,bb.CONTENT_ID
                  from (select connect_by_root id as root_modules_id,-- 
                               connect_by_root MODULES_NAME as root_modules_name,
                               connect_by_root MODULES_CODE as root_modules_code,
                               connect_by_root modules_icon as root_modules_icon,
                               connect_by_root msg_url_app as root_msg_url_app,
                               id modules_id, t.MODULES_NAME,MODULES_CODE,
                                lpad('  - - - - - - -', (level - 1) * 10, ' ') || t.MODULES_NAME as name --节点名称(带缩进)
                           from sys_Push_Modules t where del_flag = '0' 
                        	-- 核心写法,通过递归 重构 sys_Push_Modules 获取每个消息类型的根节点信息
                         start with PARENT_ID is null   --递归的写法,从指定节点,查询其下面的所有数据  
                         connect by t.PARENT_ID = prior t.id) aa
                left join (select a.receive_status,a.receive_time,c.modules_id,c.message,a.CONTENT_ID-- 用来获取 每个类型的消息
                               from sys_push_send_ref a, sys_push_content c
                              where c.id = a.content_id and a.receive = '02049cdfa3904602add9b85e2951f767') bb
                    on bb.modules_id = aa.modules_id
)
select a.id,a.modulesIcon,b.count,a.modulesName,a.receive_time,a.message,a.msgUrlApp    
from (
select id,modulesIcon,modulesName,message,msgUrlApp ,receive_time    
  from (select row_number() over(partition by id order by receive_time desc nulls last) mm,a.*
          from sys_push_send_ref_temp a
 ) where mm = 1 -- 获取每个 更节点 最新的一条消息
)a
 left join(-- 获取未读消息的数量
     select count(1) count,id  
     from sys_push_send_ref_temp a 
     where receive_status=0 and CONTENT_ID is not null
     group by id
 ) b on b.id=a.id
 order by a.receive_time desc nulls last

主要思路: 用递归遍历,connect_by_root 标记出 每个消息类型对应的 根节点 信息,这样关联消息表,就能获取每个消息对应的根节点id。然后取最新一条

123
123

11.200

between xx and yy ;

11.210 oracle 对时间的处理

1. trunc

select trunc(sysdate,'HH24')-1/24  from dual; -- 当前时间前一个小时 并且小时取整 2021/4/30 13:00:00

select TRUNC(SYSDATE, 'MM') from dual;-- 2023/8/1

--  TRUNC(SYSDATE, 'YYYY') 将当前日期截断为当前年份的第一天,然后通过加上 INTERVAL '1' YEAR - INTERVAL '1' DAY 得到当前年份的最后一天
SELECT TRUNC(SYSDATE, 'YYYY') + INTERVAL '1' YEAR - INTERVAL '1' DAY AS year_end FROM dual; -- 2023/12/31

SELECT TRUNC(ADD_MONTHS(SYSDATE, 12), 'YYYY') AS next_year_first_day FROM dual; --  下一年的最后一天

2.日期行转列

SELECT TO_DATE('2021-05-01', 'YYYY-MM-DD') + ROWNUM - 1 DT FROM DUAL
          CONNECT BY LEVEL <= (TO_DATE('2021-05-31', 'YYYY-MM-DD') - TO_DATE('2021-05-01', 'YYYY-MM-DD') + 1)

3. 两时间相加减

https://www.cnblogs.com/zhaojinhui/p/3999469.htmlopen in new window

注: Oracle date 日期相减 注意 sysdate 为date 类型,所有如果 TIMESTAMP(6) 类型责需要

sysdate-to_date(to_char(time,'yyyy-mm-dd hh24-mi-ss'),'yyyy-mm-dd hh24-mi-ss')

--oracle 两个时间相减默认的是天数
--oracle 两个时间相减默认的是天数*24 为相差的小时数
--oracle 两个时间相减默认的是天数*24*60 为相差的分钟数
--oracle 两个时间相减默认的是天数*24*60*60 为相差的秒数

--MONTHS_BETWEEN(date2,date1) 
--给出date2-date1的月份 
select months_between('19-12月-1999','19-3月-1999') mon_between from dual; 
--MON_BETWEEN 
select months_between(to_date('2000.05.20','yyyy.mm.dd'),to_date('2005.05.20','yyyy.dd')) mon_betw from dual; 
-- MON_BETW 

-- Oracle计算时间差表达式 
--获取两时间的相差豪秒数 
select ceil((To_date('2008-05-02 00:00:00' , 'yyyy-mm-dd hh24-mi-ss') - To_date('2008-04-30 23:59:59' , 'yyyy-mm-dd hh24-mi-ss')) * 24 * 60 * 60 * 1000) 相差豪秒数 FROM DUAL; 

--获取两时间的相差秒数 
select ceil((To_date('2008-05-02 00:00:00' , 'yyyy-mm-dd hh24-mi-ss') - To_date('2008-04-30 23:59:59' , 'yyyy-mm-dd hh24-mi-ss')) * 24 * 60 * 60) 相差秒数 FROM DUAL; 

--获取两时间的相差分钟数 
select ceil(((To_date('2008-05-02 00:00:00' , 'yyyy-mm-dd hh24-mi-ss') - To_date('2008-04-30 23:59:59' , 'yyyy-mm-dd hh24-mi-ss'))) * 24 * 60)  相差分钟数 FROM DUAL; 


--获取两时间的相差小时数 
select ceil((To_date('2008-05-02 00:00:00' , 'yyyy-mm-dd hh24-mi-ss') - To_date('2008-04-30 23:59:59' , 'yyyy-mm-dd hh24-mi-ss')) * 24)  相差小时数 FROM DUAL; 


--获取两时间的相差天数 
select ceil((To_date('2008-05-02 00:00:00' , 'yyyy-mm-dd hh24-mi-ss') - To_date('2008-04-30 23:59:59' , 'yyyy-mm-dd hh24-mi-ss')))  相差天数 FROM DUAL; 


---------------------------------------- 
注:天数可以2个日期直接减,这样更加方便 
---------------------------------------- 

--获取两时间月份差 
select (EXTRACT(year FROM to_date('2009-05-01','yyyy-mm-dd')) - EXTRACT(year FROM to_date('2008-04-30','yyyy-mm-dd'))) * 12 + 
       EXTRACT(month FROM to_date('2008-05-01','yyyy-mm-dd')) - EXTRACT(month FROM to_date('2008-04-30','yyyy-mm-dd')) months 
from dual; 


-------------------------------------- 
注:可以使用months_between函数,更加方便 
-------------------------------------- 

--获取两时间年份差 
select EXTRACT(year FROM to_date('2009-05-01','yyyy-mm-dd')) - EXTRACT(year FROM to_date('2008-04-30','yyyy-mm-dd')) years from dual; 


select sysdate,add_months(sysdate,12) from dual;  --加1年 
select sysdate,add_months(sysdate,1) from dual;   --加1月 
select sysdate,TO_CHAR(sysdate+7,'yyyy-mm-dd HH24:MI:SS') from dual;  --加1星期 
select sysdate,TO_CHAR(sysdate+1,'yyyy-mm-dd HH24:MI:SS') from dual;  --加1天 
select sysdate,TO_CHAR(sysdate+1/24,'yyyy-mm-dd HH24:MI:SS') from dual;  --加1小时 
select sysdate,TO_CHAR(sysdate+1/24/60,'yyyy-mm-dd HH23:MI:SS') from dual;  --加1分钟 
select sysdate,TO_CHAR(sysdate+1/24/60/60,'yyyy-mm-dd HH23:MI:SS') from dual;  --加1秒 
select   sysdate+7   from   dual;                     --加7天

4.oracle验证2个时间是否有交集,时间重叠

select * 
from test_table
where (startTime > a AND startTime < b) OR
	    (startTime < a AND endTime > b) OR
	    (endTime > a AND endTime < b)

5. 年份比较

SELECT *
FROM your_table
WHERE EXTRACT(YEAR FROM your_date) = EXTRACT(YEAR FROM SYSDATE); -- 验证your_date 是否是 今年

11.220 oracle 递归

img
img
select
connect_by_root t.name as func_name_4_root ,t.type
      ,lpad('  - - - - - - -', (level - 1) * 10, ' ') || t.name as "name" --节点名称(带缩进)
   	, SYS_CONNECT_BY_PATH(name, '/') as pName
  from sys_office t 
start with  id='xjbwgzb'  --递归的写法,从指定节点,查询其下面的所有数据  (不可为空,否则查出的数据,会重复)
connect by t.parent_id=prior t.id;

说明:

  • CONNECT_BY_ROOT 返回当前节点的最顶端节点 ---->connect_by_root t.nameopen in new window 返回的就是根节点的名称

  • CONNECT_BY_ISLEAF 判断是否为叶子节点,如果这个节点下面有子节点,则不为叶子节点

  • LEVEL 伪列表示节点深度 ------> 可以作为节点的层级

  • SYS_CONNECT_BY_PATH函数显示详细路径,并用“/”分隔----> 拼接节点,可以找到所以父节点

参考:https://blog.csdn.net/feier7501/article/details/21815691open in new window

向上递归 id 最小级别

select distinct name,type,id
 from sys_office t 
 start with id = '0192e1d8c9d34eab89ec0csedwewefweffw3434345633s' 
 connect by prior parent_id = id

向下递归 id 最大级别

select distinct name ,type,id
 from sys_office t 
 start with t.id = 'xjbwgzb' 
connect by prior id = parent_id 

案例:场景:查询人员的所属部门,office表结构为 公司-部门-团队,其中部门和团队都挂人员,而团队下的人员也要归属于部门下

select * from sys_office t
   where t.type  = '2'    --type=2 表示团队
  connect by prior t.id=t.parent_id
 start with t.id ='xxxxxx'  ---当前人员的所属机构

11.230 grant 权限 on 数据库对象 to 用户open in new window

#  给 SDT_XJB系统 添加 SDT_IMOA 用户下的SYS_USER 表的select权限
grant select on SYS_USER to SDT_IMOA ;

#
select 'grant select on '||table_name ||' to SDT_XJB ;' from all_tables where owner='SDT_IMOA';

说明: 现在需要创建一个视图,用来 协同办公和巡检保 用户表对照

分别 在 SDT_XJB用户下执行 grant select on SYS_USER to SDT_IMOA ;

​ 在 SDT_IMOA用户下执行 grant select on SYS_USER to SDT_XJB;

create or replace view v_user_xjb_imoa as(
 /* 
    巡检保和 协同办公人员对照表
 */
 select imoa_u.login_name as imoa_login_name,
        imoa_u.id         as imoa_id,
        imoa_u.name       as imoa_name,
        imoa_u.office_id  as imoa_office_id,
        xjb_u.login_name  as xjb_login_name,
        xjb_u.id          as xjb_id,
        xjb_u.name        as xjb_name,
        xjb_u.office_id   as xjb_office_id
   from SDT_IMOA.sys_user imoa_u, SDT_XJB.sys_user xjb_u
  where imoa_u.login_name = xjb_u.login_name -- imoa_u.del_flag=0 and xjb_u.del_flag=0

);

11.240 聚合

数据库中找到表中重复数据 oracle查询重复数据方法open in new window

找到表中 model_name 出现重复的记录

select * from BAS_TOWER_TYPE where MODEL_NAME in (
select MODEL_NAME from BAS_TOWER_TYPE where model_name='J27102'
group by MODEL_NAME 
having count(id) > 1
)

11.250 查询数据库中表是否被锁

    select object_name,machine,s.sid,s.serial#
    from v$locked_object l,dba_objects o ,v$session s
    where l.object_id = o.object_id and l.session_id=s.sid;

11.500 数据库异常

1.数据库 连接不上,也不报错

可能是 listener.log 文件已经达到4G 无法再继续写入参考:https://blog.csdn.net/obgnahs/article/details/81410569open in new window

解决:删除D:\app\Administrator\diag\tnslsnr\计算机名\listener\trace目录下listener.log文件

重启service服务及数据库实例服务

2. 数据库锁表

查看Oracle被锁的表以及如何解锁:https://blog.csdn.net/liusa825983081/article/details/80448945open in new window

  • 1.查看是否有被锁的表:
select b.owner,b.object_name,a.session_id,a.locked_mode
from v$locked_object a,dba_objects b
where b.object_id = a.object_id
  • 2.查看是哪个进程锁的
select b.username,b.sid,b.serial#,logon_time
from v$locked_object a,v$session b
where a.session_id = b.sid order by b.logon_time
usernamesidserial#logon_time
SDT_XJB435292532021/10/8 9:04:22

杀死进程

alter system kill session 'sid,serial#';
-- alter system kill session '435,29253';

Oracle ORA-12514 解决办法

  1. 找到listener.ora监听文件,具体位置如:
C:\app\Administrator\product\11.2.0\dbhome_1\network\admin\listener.ora

2.在lisener.ora文件中添加下方加红加粗部分:

# listener.ora Network Configuration File: C:\app\Administrator\product\11.2.0\dbhome_1\network\admin\listener.ora
# Generated by Oracle configuration tools.

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (SID_NAME = CLRExtProc)
      (ORACLE_HOME = C:\app\Administrator\product\11.2.0\dbhome_1)
      (PROGRAM = extproc)
      (ENVS = "EXTPROC_DLLS=ONLY:C:\app\Administrator\product\11.2.0\dbhome_1\bin\oraclr11.dll")
    )
   
    # ==============添加下面的语句===========
    (SID_DESC = 
      (GLOBAL_DBNAME = ORCL) 
      (ORACLE_HOME = C:\app\Administrator\product\11.2.0\dbhome_1) 
      (SID_NAME = ORCL) 
    )
     # =================================
  )

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = WIN-UBKOOK0BP2K.geostar.com.cn)(PORT = 1521))
    )
  )

ADR_BASE_LISTENER = C:\app\Administrator

ORA-01033 问题解决办法

https://www.cnblogs.com/iqingchun/p/13538103.htmlopen in new window

Oracle归档日志满了导致无法启动ora-03113

案例:(使用过)
# 1.登陆(https://www.cnblogs.com/gmq-sh/p/5980127.html)
sqlplus /nolog 
SQL> connect /as sysdba
# 2.检查flash recovery area的使用情况,可以看见archivelog已经很大了,达到99.94
SQL> select * from V$FLASH_RECOVERY_AREA_USAGE;
123
123
# 3.计算flash recovery area已经占用的空间
SQL> select sum(percent_space_used)*3/100 from v$flash_recovery_area_usage;
12
12
# 4.修改FLASH_RECOVERY_AREA的空间修改为8GB,修改前确认磁盘有足够空间
SQL> ALTER SYSTEM SET DB_RECOVERY_FILE_DEST_SIZE=8g

# 5.清理一下archivelog归档日志,生产环境建议备份
## 5.1查询日志目录位置
show parameter recover;
## 5.2删除归档日志,orcl是数据库实例名 (手动删除文件)
D:\app\Administrator\flash_recovery_area\orcl\ARCHIVELOG


# 6.使用rman 操作
$ rman
RMAN> connect target sys/sys_passwd

crosscheck backup; 
delete obsolete; 
delete expired backup; 
crosscheck archivelog all; 
delete expired archivelog all; 
#### 此步会有提示,输入 YES 回车
host;   //退出rman

# 7.确认是否操作成功
sqlplus /nolog
SQL>  connect /as sysdba
SQL> select * from V$FLASH_RECOVERY_AREA_USAGE;

ORACLE 归档日志打开关闭方法

sql> archive log list;   #查看是否是归档方式  
sql> alter system set log_archive_start=false scope=spfile; #禁用自动归档  
sql> shutdown immediate;  
sql> startup mount;    #打开控制文件,不打开数据文件  
sql> alter database noarchivelog; #将数据库切换为非归档模式  
sql> alter database open;   #将数据文件打开  
sql> archive log list;   #查看此时便处于非归档模式

//=== 待测试

sqlplus / as sysdba
shutdown abort     ----关闭进程
startup mount       ---- 装载数据库
select * from v$recovery_file_dest; ---查询归档日志
ALTER SYSTEM SET DB_RECOVERY_FILE_DEST_SIZE=10737418240; --设置归档日志空间为10G




https://www.cnblogs.com/-txf-/p/11572942.html
oracle归档日志满了无法启动数据库问题解决记录
sqlplus /nolog
conn /as sysdba

上面的登录方式提示12560协议适配器错误

sqlplus sys/sys@orcl as sysdba    这个语句就可以直接登录
conn sys/sys as sysdba;

然后将oracle关闭shutdown immediate;
再启动到mount模式 statrup mount

更改数据库的归档模式

SELECT * FROM  V$FLASH_RECOVERY_AREA_USAGE;--查询归档日志大小

select log_mode from v$database;--查询归档模式

alter database noarchivelog;--更改数据库到非归档模式

接下来删除过多的归档日志文件

先物理删除日期较前的归档日志(可以剪切到别处),归档日志文件位置D:\app\Administrator\flash_recovery_area\orcl\ARCHIVELOG

再打开一个cmd窗口

执行 $ rman target / nocatalog;
crosscheck archivelog all;--检查归档日志文件
delete expired archivelog all;--删除过期的日志文件

接下来重新启动oracle即可

rman 执行 可能会报错,前面加上$

include 的用法

<sql id="sqlid">
    res_type_id,res_type
</sql>

<select id="queryPubResType" parameterType="com.property.vo.PubResTypeVO" resultMap="PubResTypeList">
    select  a.res_type_id,
    <include refid="sqlid">
        <property name="AI_RES_TYPE_ID" value="a.res_type_id"/>
        <property name="lng" value="#{lngId}"/>
        <property name="female" value="'女'"/>
    </include> as res_type
    from    pub_res_type a
</select>


<select id="queryPubResType" parameterType="com.property.vo.PubResTypeVO" resultMap="PubResTypeList">
    select  a.res_type_id,
    <include refid="sqlid"/>
    from pub_res_type a
</select>

验证是否包含(是否有交集)

image-20221118164302860
image-20221118164302860
 -- 场景1
(#{选择结束点}  BETWEEN 开始点 and 结束点)
  -- 场景2、3  
or (#{选择开始点} BETWEEN 开始点 and 结束点)
-- 场景4 
or (
	#{选择结束点} >= 结束点 and #{选择开始点} <![CDATA[ <= ]]> 开始点
)

表空间相关

查询表空间大小

SELECT
    a.tablespace_name, -- 表空间名称	
    b.bytes / 1024 / 1024 AS total_size_mb, -- 总大小 (MB)	
    (b.bytes - NVL(c.bytes, 0)) / 1024 / 1024 AS used_size_mb, -- 已使用 (MB)
    NVL(c.bytes, 0) / 1024 / 1024 AS free_size_mb -- 可用 (MB)
FROM
    dba_tablespaces a
JOIN
    (SELECT tablespace_name, SUM(bytes) AS bytes
     FROM dba_data_files
     GROUP BY tablespace_name) b ON a.tablespace_name = b.tablespace_name
LEFT JOIN
    (SELECT tablespace_name, SUM(bytes) AS bytes
     FROM dba_free_space
     GROUP BY tablespace_name) c ON a.tablespace_name = c.tablespace_name
ORDER BY
    a.tablespace_name;

表空间满了

-- 查询表空间对应的文件位置
select name from v$datafile;
--  查询总量 size_mb
SELECT file_name,
bytes/1024/1024 AS size_mb,
(bytes - NVL(free_bytes, 0))/1024/1024 AS used_mb
FROM (
SELECT df.file_name,
df.bytes,
SUM(fs.bytes) AS free_bytes
FROM dba_data_files df
LEFT JOIN dba_free_space fs ON df.file_id = fs.file_id
WHERE df.file_name = 'D:\APP\LISHIHUAN\ORADATA\ORCL\SDT_IMOA.DBF'
GROUP BY df.file_name, df.bytes
);

-- 2560M 这个数值要比上面查询到的size_mb 大
ALTER DATABASE DATAFILE 'D:\APP\LISHIHUAN\ORADATA\ORCL\SDT_IMOA.DBF' RESIZE 2560M;

查询表空间中表、索引占用空间

SELECT
    a.tablespace_name,
    b.segment_name,
    b.segment_type,
    SUM(b.bytes) / 1024 / 1024 AS used_size_mb
FROM
    dba_segments b
JOIN
    dba_tablespaces a ON b.tablespace_name = a.tablespace_name
WHERE a.tablespace_name = 'SDT_IMOA'  -- 替换为你的表空间名称
GROUP BY
    a.tablespace_name, b.segment_name, b.segment_type
ORDER BY
    used_size_mb DESC;

创建只读用户

  • 原始用户 SDT_YJWZ
  • 创建只读用户semdo_wz
-- 1. 创建用户
CREATE USER semdo_wz IDENTIFIED BY semdo_wz;
-- 2. 授予该用户连接到数据库的权限
GRANT CONNECT TO semdo_wz;
-- 3. 授权
-- 3.1 指定表
GRANT SELECT ON schema_name.table_name TO semdo_wz;
-- 3.2 如果您希望用户具有访问所有表的权限,可以授予 ALL 权限。
GRANT SELECT ANY TABLE TO semdo_wz;
-- 4. 查询
sqlplus query_user/password@your_database
---- 查询表
SELECT * FROM SCHEMA_NAME.table_name; ## SCHEMA_NAME 指的是 用户SDT_YJWZ
GRANT SELECT ON SDT_YJWZ.material_code_relation TO semdo_wz;
GRANT SELECT ON SDT_YJWZ.material_sub_detail TO semdo_wz;

要为 semdo_wz 用户创建一个只具有查询功能的数据库用户,你需要按照以下步骤操作:

步骤 1:创建 semdo_wz 用户

首先,管理员需要在数据库中创建一个新用户 semdo_wz,并为该用户分配密码。你可以在 SQL*Plus 或者 SQL Developer 中使用以下命令:

CREATE USER semdo_wz IDENTIFIED BY password;

这里,password 是你为 semdo_wz 用户选择的密码。

步骤 2:授予连接和查询权限

接下来,你需要为 semdo_wz 用户授予必要的权限,以便它能够登录并执行查询。

  1. 授予连接权限: 使用户能够连接到数据库。

    GRANT CONNECT TO semdo_wz;
    
  2. 授予查询权限: 为了让 semdo_wz 用户能够查询表数据,你需要授予 SELECT 权限。假设 semdo_wz 用户需要查询 SDT_YJWZ 用户下的表,你可以授予这些表的查询权限。

    如果你希望用户查询 SDT_YJWZ schema 下的所有表,你可以执行如下命令(对于每个表):

    GRANT SELECT ON SDT_YJWZ.table_name TO semdo_wz;
    

    如果你希望为所有表授予查询权限,可以使用动态查询脚本:

    BEGIN
       FOR r IN (SELECT table_name FROM all_tables WHERE owner = 'SDT_YJWZ') LOOP
          EXECUTE IMMEDIATE 'GRANT SELECT ON SDT_YJWZ.' || r.table_name || ' TO semdo_wz';
       END LOOP;
    END;
    

    这段 PL/SQL 脚本会自动为 SDT_YJWZ 用户下的所有表授予 SELECT 权限给 semdo_wz 用户。

步骤 3:可选 — 授予特定角色

如果你不想逐个表授予权限,也可以为 semdo_wz 用户分配一个角色,角色中包含所需的权限。

比如,创建一个角色并授予 SELECT 权限,然后将角色分配给 semdo_wz 用户:

  1. 创建角色:

    CREATE ROLE query_role;
    
  2. 授予角色查询权限:

    GRANT SELECT ON SDT_YJWZ.table_name TO query_role;
    
  3. 将角色分配给用户:

    GRANT query_role TO semdo_wz;
    

步骤 4:测试用户权限

创建好 semdo_wz 用户并授予权限后,测试该用户是否能够登录并查询数据:

  1. 登录:

    sqlplus semdo_wz/password@127.0.0.1:1521/orcl
    
  2. 执行查询:**需要注意查询表的时候,需要带上用户名 **

    SELECT * FROM SDT_YJWZ.table_name;  
    

如果一切正常,semdo_wz 用户应该能够查询 SDT_YJWZ 用户下的表。

步骤 5:配置 JDBC

在你的 Java 应用程序中,你需要将数据库连接信息配置为使用 semdo_wz 用户。假设你的 jdbc.url 和数据库连接信息如下:

jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521/orcl
jdbc.username=semdo_wz
jdbc.password=password

确保在 jdbc.usernamejdbc.password 中使用 semdo_wz 用户的凭据。

总结

  1. 创建用户:使用 CREATE USER 创建 semdo_wz 用户。
  2. 授予权限:授予连接权限和 SELECT 权限。
  3. 配置 JDBC:在你的应用程序中更新数据库连接信息,使用 semdo_wz 用户。

通过这些步骤,你将能够创建一个只具有查询权限的用户 semdo_wz,并配置其数据库连接。

oracle 删除表等并且释放空间

  • 删除表、视图、索引等
BEGIN
   -- 删除表
   FOR r IN (SELECT table_name, owner FROM all_tables WHERE owner = 'SDT_ZHST_2024') LOOP
      EXECUTE IMMEDIATE 'DROP TABLE ' || r.owner || '.' || r.table_name || ' CASCADE CONSTRAINTS';
   END LOOP;

   -- 删除视图
   FOR r IN (SELECT view_name, owner FROM all_views WHERE owner = 'SDT_ZHST_2024') LOOP
      EXECUTE IMMEDIATE 'DROP VIEW ' || r.owner || '.' || r.view_name;
   END LOOP;

   -- 删除索引
   FOR r IN (SELECT index_name, owner FROM all_indexes WHERE owner = 'SDT_ZHST_2024') LOOP
      EXECUTE IMMEDIATE 'DROP INDEX ' || r.owner || '.' || r.index_name;
   END LOOP;

   -- 删除存储过程、函数、包等
   FOR r IN (SELECT object_name, object_type, owner FROM all_objects WHERE owner = 'SDT_ZHST_2024' AND object_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE')) LOOP
      EXECUTE IMMEDIATE 'DROP ' || r.object_type || ' ' || r.owner || '.' || r.object_name;
   END LOOP;
END;
/

  • 删除用户
DROP USER SDT_ZHST_2024 CASCADE;
  • 手动删除数据文件

如果删除表空间时仍然没有删除数据文件,您可以手动删除数据文件。要确保数据文件已经从表空间中移除,可以执行以下命令:

  1. 首先,使用 ALTER DATABASE 命令将数据文件脱离表空间:

    ALTER DATABASE DATAFILE 'D:\app\lishihuan\oradata\orcl\SDT_ZHST_2024.DBF' OFFLINE;
    
  2. 然后,删除数据文件:

    HOST del D:\app\lishihuan\oradata\orcl\SDT_ZHST_2024.DBF;
    

    HOST 命令用于在 SQL*Plus 中执行操作系统级别的命令,删除数据文件。


12. MySql

注: java 程序配置 MySql数据库时需要注意

mysql 权限:https://yunwei.blog.csdn.net/article/details/106424312open in new window

因为在mysql5中,jdbc的驱动是com.mysql.jdbc.Driver,
而mysql6以及以上是com.mysql.cj.jdbc.Driver

老板:
jdbc.type=mysql
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.2.149:3306/cpms_cz_dev?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true
jdbc.username=root
jdbc.password=root

?nullCatalogMeansCurrent=true ​ 新版本-8:需要加 ?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone =Asia/Shanghai

jdbc.type=mysql
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.2.149:3306/cpms_cz_dev?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true
#url: jdbc:mysql://localhost:3306/restful_crud?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone =Asia/Shanghai
jdbc.username=root
jdbc.password=root

xml文件中 & === >  &amp;

navicat使用open in new window

12.1 基本使用

  • cmd登陆:mysql -h 192.168.10.229 -p 9060 -uroot -pSemdo@123

    查看数据库的语句是SHOW DATABASES,查看数据库语法格式是:

SHOW DATABASES [LIKE '数据库名'];

mysql> SHOW DATABASES LIKE '%test%';
  • 创建用户
CREATE USER 'lishihuan'@'172.16.10.229' IDENTIFIED BY '123456';
  • 授权

  • 删除用户

 DROP USER 'lishihuan'@'172.16.10.229';
  • 查询建表语句
SHOW CREATE TABLE table_name;
  • 查询字段 属性
desc table_name;
  • 切换数据库
use sdt_xjb;
  • 删除表:DROP TABLE my_table;

  • 删除数据(分场景,如果全删,用 truncate)

    • 删除整表数据 truncate table my_table;
    • 删除部分数据delete from my_table where id='1';
    delete from sys_job_log; -- 遇到无法删除,因为磁盘存储空间满了
    truncate table sys_job_log;
    optimize table sys_job_log;
    
  • 查询进程

show processlist;
  • 添加字段
## 表BAS_OP_TEAM 添加 字段LONGITUDE_GB  并且插入位置再PHONE字段后面
ALTER TABLE BAS_OP_TEAM ADD LONGITUDE_GB varchar(200) comment '所在位置经度(国标)' after PHONE;

ALTER TABLE OD_SHIFT_RECORD ADD `CODE` varchar(50) not null comment '班次CODE【唯一标识】' after START_DATE;
  • 复制表结构
-- 备份表机构
create table yj_zhst_user_dept like yj_zhst_user ;

12.5 MySql ddl 语句

添加联合约束

ALTER TABLE climate_collect_pjrelation ADD unique(DEVICE_ID, ENGINEER_ID)

设置主键自增长

alter table workflow_leave modify id int auto_increment primary key;
alter table workflow_leave modify id int auto_increment;

删除时存在外键

使用delete 无法讲id自增长 序号清除

-- 禁用外键约束
SET FOREIGN_KEY_CHECKS = 0;

-- 执行 TRUNCATE 操作
TRUNCATE TABLE `yjydxj-cloud`.`emg_bus_imit_actv_matr_rec`;

-- 启用外键约束
SET FOREIGN_KEY_CHECKS = 1;

12.9 数据类型-整数

字段类型名称字节数表示范围
tinyint迷你整型1个字节=8位0-255
smallint小整型2个字节0-65535
mediumint中整型3个字节-
int整型(标准整型)4个字节-
bigint大整型8个字节-

12.10 列转行

select a.id,a.name,a.label,substring_index(substring_index(a.value_item,';',b.help_topic_id+1),';',-1) value_item 
from YH_BASE_DEAL_ARCH_CLASS a 
join mysql.help_topic b on b.help_topic_id < (length(a.value_item) - length(replace(a.value_item,';',''))+1)
 order by a.id;

12.20 内置函数

1. 空间查询 st_distance_sphere

效率高

SELECT st_distance_sphere(POINT(117.2649120808,31.9344120986),point(117.2594242812,31.9364424285))  

自定义函数-空间计算

CREATE DEFINER=`root`@`%` FUNCTION `F_DISTANCE`(`cx` double,`cy` double,`sx` double,`sy` double) RETURNS double
begin
  DECLARE d FLOAT(10,2);
  DECLARE x FLOAT(10,2);
  DECLARE y FLOAT(10,2);
	DECLARE r FLOAT(10,2);
	DECLARE pi FLOAT(10,2);
  set r=6371229;
  set pi=3.14159265358979323;
  set x=(sx-cx)*pi*r*cos((sy+cy)/2*pi/180)/180;
  set y=(sy-cy)*pi*r/180;
  set d=SQRT(power(x,2)+power(y,2));
  set D=ROUND(D,2);
  RETURN d;
end

2. GREATEST()函数返回输入参数最大值

-- 查询'zt_time' , 'zy_time' , 'zn_time'中最大一个
SELECT
	GREATEST(zt_time, zy_time, zn_time)
FROM
	'XXX'

limit 取前多少条数据

 select * from dw.dwd_sdxs_job_op  limit  100;

空间函数,计算 坐标距离 (米)

计算地球两点之间的球面距离,单位为 米。传入的参数分别为X点的经度,X点的纬度,Y点的经度,Y点的纬度。

select st_distance_sphere(point(116.35620117,39.939093), point(116.4274406433,39.9020987219));

截取 substring 和 instr

 select substring(hour_part,1,instr(hour_part,':')-1),hour_part from dw.dwd_sdxs_user_rec_hour h where to_days(data_dt)=to_days(now())
hourhour_part
99:00-10:00
1010:00-11:00

保留小数

select round(12.34345,2) -- 12.34

判断是否存在自定字符

	case when LOCATE('±',l.volt_name)>0 then REPLACE(l.volt_name,'±','交流') else concat('直流',l.volt_name) end,

GREATEST()函数查找给定数字之间的最大数字。

SELECT GREATEST(10, 20, 30, 40, 50) 

least 函数查找给定数字之间的最小数字。

SELECT least(10, 20, 30, 40, 50) 

COALESCE

按照参数的顺序逐个评估参数,并返回第一个非空值

  • 用途:

1)将控制替换成其他值

2)返回第一个非空值

  • 表达式

    COALESCE是一个函数, (expression_1, expression_2, ...,expression_n)依次参考各参数表达式,遇到非null值即停止并返回该值。如果所有的表达式都是空值,最终将返回一个空值。使用COALESCE在于大部分包含空值的表达式最终将返回空值。

  • SQL实例

select coalesce(success_cnt, 1) from tableA
-- 以上写法将会判断certificate_file和labor_file字段是否为空,如果其中任意一个字段非空,则返回1,否则返回0。
SELECT CASE WHEN COALESCE(certificate_file, labor_file) IS NOT NULL THEN 1 ELSE 0 END as idPermitApply, a.*  FROM zjjc_person a;

COALESCE()函数可以用来完成几乎所有的空值处理,不过在很多数据库系统中都提供了它的简化版,这些简化版中只接受两个变量,其参数格式如下: MYSQL: IFNULL(expression,value) MSSQLServer: ISNULL(expression,value) Oracle: NVL(expression,value)

这几个函数的功能和COALESCE(expression,value)是等价的。

比如SQL语句用于返回人员的“重要日期”,如果出生日期不为空则将出生日期做为“重要日期”,如果出生日期为空则返回注册日期的值:

-- MYSQL: 
SELECT FBirthDay,FRegDay, IFNULL(FBirthDay,FRegDay)  AS ImportDay FROM T_Person 
-- MSSQLServer 
SELECT FBirthDay,FRegDay, ISNULL(FBirthDay,FRegDay)  AS ImportDay  ROM T_Person 
-- Oracle
SELECT FBirthDay,FRegDay, NVL(FBirthDay,FRegDay)  AS ImportDay  ROM T_Person 


12.30 创建函数

set global log_bin_trust_function_creators=1;

如果创建的是带参 函数,需要给参数指定 类型同时指定大小,否则会报下面的错误

1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')

CREATE DEFINER = CURRENT_USER FUNCTION `simpleFun`(name VARCHAR(20))
	RETURNS varchar(30)
BEGIN
	#Routine body goes here...
	RETURN name;
END;

声明参数

drop function if exists hello;
-- a、b 都是函数的参数
create function hello(a varchar(20),b varchar(20)) returns varchar (255)
begin
    
    -- declare 声明参数,default:设置声明参数的默认值
    declare x varchar(255) default ' x  ';
    declare y varchar(255) default b;
    declare c varchar(255) default ' 2017-01-02  ';
    declare d varchar(255);
    -- set 给声明的参数赋值
    SET d = concat(x,a,b,y,c);
    return d;
end;

12.40 定义变量

# 定义变量
set @dt = now();
select date_add(@dt, interval 1 day); -- add 1 day
select date_add(@dt, interval 1 hour); -- add 1 hour


# 赋值
set @:= 1;
select @dt:= @dt+3; 
SELECT  @dt:=5;

12.50 with as (创建临时表)

如果查询 报没有登陆数据库,可以通过 use DATABASES; 相当于先登陆一个数据库,创建的临时表就在 这个数据库下

with ydxj_ttsh_temp as -- ydxj_ttsh_temp 相当于一张临时表
(select t.line1_tower_id
    from sdt_xjb.ydxj_ttsh t
 )
select * from ydxj_ttsh_temp;
  • 需要定义多个 临时表,可有用逗号相连, with xx as () , yy as ()
with temp1 as -- ydxj_ttsh_temp 相当于一张临时表
(select t.line1_tower_id
    from table_name1 t
 ), temp2 as (
 	select * from table_name2
 )
select * from temp1 p1
left join temp2 p2 on p1.id=p2.id;

12.60 组内排序

场景:要求显示每个部门年龄第二大的人

第一步,分组;第二步,组内按照年龄排序;第三步,组内排序后的数据加上序号

SELECT rownum, id, NAME, age, deptId, empno 
FROM
	(SELECT
		(@i := #变量Qi的值,同一个部门内累加,部门变化则从0开始计数
		CASE WHEN @deptId = deptId # 判断deptId是否发生变化
			THEN @i + 1 #没有发生变化则将排序加1
			ELSE 1 #发生变化则从1开始重新排序
		END) rownum,t_emp.*,
	( @deptId := deptId ) #将当前数据的部门号赋值给变量@deptId,即读取当前部门]的deptId号
	
FROM t_emp,
	( SELECT @i := 0) AS a #定义变量并赋初值
	
GROUP BY deptId, id 
ORDER BY deptId, age DESC # 年龄降序排列
) tb 
 WHERE rownum = 2 #年龄第几大,该处就写几

12.70 数据拉平,每条记录的上一条记录

查找每条巡视记录的上一次同类型的巡视

select op.opid,op.optime ,bb.pre_optime
from sdt_xjb.ydxj_job_op op
LEFT JOIN (
	SELECT aa.major, aa.opobjid, aa.op_type, aa.optime,max(bb.optime) as pre_optime FROM 
	sdt_xjb.ydxj_job_op aa 
	left join sdt_xjb.ydxj_job_op bb ON bb.major = aa.major 
		AND bb.opobjid = aa.opobjid -- 找到当前每条记录的 前一条 巡视记录
		AND bb.op_type = aa.op_type 
		AND bb.optime < aa.optime 
	group by aa.major, aa.opobjid, aa.op_type, aa.optime 
	) bb on  bb.major=op.major and bb.opobjid=op.opobjid   and   bb.optime= op.optime
use sdt_xjb;
with act_hi_actinst_temp as (  -- 创建临时表
	select ht.id_,ht.proc_inst_id_,ht.task_id_,act_id_,ht.act_name_,ht.start_time_,ht.end_time_,ht.duration_,ht.act_type_,
	ROW_NUMBER() over(partition by ht.proc_inst_id_ order by ht.start_time_ ) as order_num 
	from 		sdt_xjb.act_hi_actinst ht 
	where
		ht.act_type_ != 'exclusivegateway' 
		and ht.proc_inst_id_ in (
			select distinct t.proc_inst_id_
          from sdt_xjb.act_hi_actinst t
         where t.proc_def_id_ like '%record_upgrades%'
           and t.act_type_ != 'exclusivegateway'
           and  (date_format(t.start_time_, '%y-%m') = date_format(now(), '%y-%m')  or date_format(t.end_time_, '%y-%m') = date_format(now(), '%y-%m'))
		) 
)
select 
	t1.id_ as id,
	qx.id as record_id,-- 对应的缺陷id
	t1.order_num,
	t1.proc_inst_id_ as proc_inst_id ,    
	t1.act_type_ as node_type,
	t1.act_name_  as curr_node_name,-- 当前节点名称
	t2.act_name_ as next_node_name, -- 下个节点名称
	hd.userId as user_id,         
	hd.userName as user_name,       
	t1.start_time_,     
	t1.end_time_,       
	t1.duration_,       
	hd.remark as remark
from act_hi_actinst_temp t1
left join act_hi_actinst_temp t2 on t1.proc_inst_id_=t2.proc_inst_id_ and t1.order_num=t2.order_num-1  -- 自己关联自己,获取每步的下个节点
inner join sdt_xjb.ydxj_danger_record qx on qx.proc_ins_id=t1.proc_inst_id_
-- 查审批过程 执行人和 备注
left join (select t.act_inst_id_,
				max(case when t.name_='userId' then t.text_ else '' end) as userId,
				max(case when t.name_='current' then t.text_ else '' end) as userName,
				max(case when t.name_='remark' then t.text_ else '' end)  as remark
				from sdt_xjb.act_hi_detail t 
				group by t.act_inst_id_) hd
on hd.act_inst_id_=t1.id_

12.80 组内排序 开窗函数

-- 下例展示了按照property列分组对x列排名:
select x, y, dense_rank() over(partition by x order by y) as rank from int_t;

在MYSQL的最新版本MYSQL8已经支持了排名函数RANKDENSE_RANKROW_NUMBER。但是在就得版本中还不支持这些函数,只能自己实现。实现方法主要用到了条件判断语句(CASE WHENIF)和添加临时变量。但是8版本一下的无法使用,所以在排序取最新记录时,可有通过 下面2个方法

-- 说明: 通过时间存在不合理,毕竟不是唯一性,如果并发量大,这样就存在相同时间,会导致查询不准确,所以可以通过id来实现(前提id是自增长)
select  t.*
from ydxj_dw_dwjl_2016 t
where dwsj = (
    select max(t1.dwsj) from proj_base_trajectory t1 where t1.USERID = t.USERID
)   


select a.*
  from ydxj_dw_dwjl_2016 a
 where not exists (select 1
          from ydxj_dw_dwjl_2016 b
         where b.userid = a.userid             
           and b.dwsj > a.dwsj)

1. 排名分类

1.1 区别RANK,DENSE_RANK和ROW_NUMBER
  • RANK并列跳跃排名,并列即相同的值,相同的值保留重复名次,遇到下一个不同值时,跳跃到总共的排名。
  • DENSE_RANK并列连续排序,并列即相同的值,相同的值保留重复名次,遇到下一个不同值时,依然按照连续数字排名。
  • ROW_NUMBER连续排名,即使相同的值,依旧按照连续数字进行排名。

区别如图:

image-20210224090835726
image-20210224090835726
1.2 分组排名

将数据分组后排名,区别如图:

image-20210224090939521
image-20210224090939521

2. 准备数据

创建一张分数表,里面有字段:分数score,课程号course_id和学生号student_id。 执行如下SQL语句,进行导入数据。

create table score(
  student_id varchar(10),
  course_id varchar(10),
  score decimal(18,1)
);

insert into score values('01' , '01' , 80);
insert into score values('01' , '02' , 90);
insert into score values('01' , '03' , 99);
insert into score values('02' , '01' , 70);
insert into score values('02' , '02' , 60);
insert into score values('02' , '03' , 80);
insert into score values('03' , '01' , 80);
insert into score values('03' , '02' , 80);
insert into score values('03' , '03' , 80);
insert into score values('04' , '01' , 50);
insert into score values('04' , '02' , 30);
insert into score values('04' , '03' , 20);
insert into score values('05' , '01' , 76);
insert into score values('05' , '02' , 87);
insert into score values('06' , '01' , 31);
insert into score values('06' , '03' , 34);
insert into score values('07' , '02' , 89);
insert into score values('07' , '03' , 98);
insert into score values('08' , '02' , 89);
insert into score values('09' , '02' , 89);

查看数据:

image-20210224091534272
image-20210224091534272

3. 不分组排名

3.1 连续排名
  1. 使用ROW_NUMBER实现:
SELECT score,
ROW_NUMBER() OVER (ORDER BY score DESC) ranking
FROM score;
  1. 使用变量实现:
SELECT s.score, (@cur_rank := @cur_rank + 1) ranking
FROM score s, (SELECT @cur_rank := 0) r
ORDER BY score DESC;

结果如图:

image-20210224091602071
image-20210224091602071
3.2 并列跳跃排名
  1. 使用RANK实现:
SELECT course_id, score,
RANK() OVER(ORDER BY score DESC)
FROM score;

  1. 使用变量IF语句实现:
SELECT s.score,
@rank_counter := @rank_counter + 1,
IF(@pre_score = s.score, @cur_rank, @cur_rank := @rank_counter) ranking,
@pre_score := s.score
FROM score s, (SELECT @cur_rank :=0, @pre_score := NULL, @rank_counter := 0) r
ORDER BY s.score DESC;

  1. 使用变量CASE语句实现:
SELECT s.score,
@rank_counter := @rank_counter + 1,
(
  CASE
  WHEN @pre_score = s.score THEN @cur_rank
  WHEN @pre_score := s.score THEN @cur_rank := @rank_counter
  END
) ranking
FROM score s, (SELECT @cur_rank :=0, @pre_score := NULL, @rank_counter := 0) r
ORDER BY s.score DESC;

结果如图:

image-20210224091638096
image-20210224091638096
3.3 并列连续排名
  1. 使用DENSE_RANK实现:
SELECT course_id, score,
DENSE_RANK() OVER(ORDER BY score DESC) FROM score;

  1. 使用变量IF语句实现:
SELECT s.score,
IF(@pre_score = s.score, @cur_rank, @cur_rank := @cur_rank + 1) ranking,
@pre_score := s.score
FROM score s, (SELECT @cur_rank :=0, @pre_score = NULL) r
ORDER BY s.score DESC;

  1. 使用变量CASE语句实现:
SELECT s.score,
(
  CASE
  WHEN @pre_score = s.score THEN @cur_rank
  WHEN @pre_score := s.score THEN @cur_rank := @cur_rank + 1
  END
) ranking
FROM score s, (SELECT @cur_rank :=0, @pre_score = NULL) r
ORDER BY s.score DESC;

结果如图:

image-20210224091715704
image-20210224091715704

4. 分组排名

4.1 分组连续排名
  1. 使用ROW_NUMBER实现:
SELECT course_id, score,
ROW_NUMBER() OVER (PARTITION BY course_id ORDER BY score DESC) ranking FROM score;
  1. 使用变量IF语句实现:
SELECT s.course_id, s.score,
IF(@pre_course_id = s.course_id, @cur_rank := @cur_rank + 1, @cur_rank := 1) ranking,
@pre_course_id := s.course_id
FROM score s, (SELECT @cur_rank := 0, @pre_course_id := NULL) r
ORDER BY course_id, score DESC;
4.2 分组并列跳跃排名
  1. 使用RANK实现:
SELECT course_id, score,
RANK() OVER(PARTITION BY course_id ORDER BY score DESC)
FROM score;
  1. 使用变量IF语句实现:
SELECT s.course_id, s.score,
IF(@pre_course_id = s.course_id,
   @rank_counter := @rank_counter + 1,
   @rank_counter := 1) temp1,
IF(@pre_course_id = s.course_id,
   IF(@pre_score = s.score, @cur_rank, @cur_rank := @rank_counter),
   @cur_rank := 1) ranking,
@pre_score := s.score temp2,
@pre_course_id := s.course_id temp3
FROM score s, (SELECT @cur_rank := 0, @pre_course_id := NULL, @pre_score := NULL, @rank_counter := 1)r
ORDER BY s.course_id, s.score DESC;
4.3 分组并列连续排名
  1. 使用DENSE_RANK实现:
SELECT course_id, score,
DENSE_RANK() OVER(PARTITION BY course_id ORDER BY score DESC)
FROM score;
  1. 使用变量IF语句实现:
SELECT s.course_id, s.score,
IF(@pre_course_id = s.course_id,
   IF(@pre_score = s.score, @cur_rank, @cur_rank := @cur_rank + 1),
   @cur_rank := 1) ranking,
@pre_score := s.score,
@pre_course_id := s.course_id
FROM score s, (SELECT @cur_rank :=0, @pre_score = NULL, @pre_course_id := NULL) r
ORDER BY course_id, score DESC;
12345678

可以将上述的IF条件提取出来:

SELECT s.course_id, s.score,
IF(@pre_score = s.score, @cur_rank, @cur_rank := @cur_rank + 1) temp1,
@pre_score := s.score temp2,
IF(@pre_course_id = s.course_id, @cur_rank, @cur_rank := 1) ranking,
@pre_course_id := s.course_id
FROM score s, (SELECT @cur_rank :=0, @pre_score = NULL, @pre_course_id := NULL) r
ORDER BY course_id, score DESC;

普通写法

需求:每组的第一条数据

你可以使用MySQL中的GROUP BY子句和MIN()函数来实现这个目标。假设你想要根据字段UNIQUE_CODE查找每组的第一条数据,你可以按以下步骤执行:

SELECT *
FROM wl_work_op_rec
WHERE (UNIQUE_CODE, YOUR_DATE_FIELD) IN (
    SELECT UNIQUE_CODE, MIN(YOUR_DATE_FIELD)
    FROM wl_work_op_rec
    GROUP BY UNIQUE_CODE
);

在这个查询中,你需要将YOUR_DATE_FIELD替换为你想要用来判断第一条数据的日期字段,比如时间戳或日期。这个查询首先在wl_work_op_rec表中找到每个UNIQUE_CODE对应的最小日期,然后将这些最小日期与对应的UNIQUE_CODE匹配,以获得每组的第一条数据。

案例:计算累计量

现在有个sql需要计算,计划和实施量 截至到每周的数量

select
    TEAM_ID,TEAM_NAME,M_ID,	M_NAME,	S_ID,S_NAME,YEAR,WEEK_NUM,
    plan_qty_W,  OP_QTY_W, -- 计划量/实施量
    CAST(PLAN_QTY_SUM_BY as UNSIGNED) PLAN_QTY_SUM_BY , -- 这里通过CAST 是因为mysql默认变量是double类型,所以计算的结果会有误差
    CAST(OP_QTY_SUM_BY as UNSIGNED) OP_QTY_SUM_BY
from (
    SELECT
    TEAM_ID,TEAM_NAME,M_ID,M_NAME,S_ID,S_NAME,YEAR,WEEK_NUM,
    plan_qty_W,  -- 每周的计划量
    OP_QTY_W, -- 每周的实施量
    @cumulative_plan_qty_W := IF(@prev_group = CONCAT(TEAM_ID,'_', M_ID,'_', S_ID,'_', YEAR), @cumulative_plan_qty_W + plan_qty_W, plan_qty_W) AS PLAN_QTY_SUM_BY, -- 累计计划量
    @cumulative_OP_QTY_W := IF(@prev_group = CONCAT(TEAM_ID,'_', M_ID,'_', S_ID,'_', YEAR), @cumulative_OP_QTY_W + OP_QTY_W, OP_QTY_W) AS OP_QTY_SUM_BY,-- 累计实施量
    @prev_group := CONCAT(TEAM_ID,'_', M_ID,'_', S_ID,'_', YEAR) -- 用于分组,以 YJ站+主项工作+子项工作+年份,进行分组,然后累计 每个周次WEEK_NUM 下的计划和实施
    from (SELECT @cumulative_plan_qty_W := 0, @cumulative_OP_QTY_W := 0, @prev_group := '') AS dummy,
    WL_SUM_T_W_S a
) aa
order by TEAM_ID,TEAM_NAME,M_ID,	M_NAME,	S_ID,S_NAME,YEAR,WEEK_NUM -- 不用排序,这里只是用于方便查看数据的

12.90 mysql 对时间处理

常用日期处理,可以将其设置为 视图

create or replace view v_common_date as(
select 
		record_date,-- 获取今天凌晨
		DATE_ADD(record_date,interval -day(record_date)+1 day) firstDay,-- 今天对应的所在月第一天
		DATE_ADD(DATE_ADD(record_date,interval 1 month),interval -day(record_date)+1 day) as lastDay,-- 下个月第一天凌晨(也即本月最后一天)
		yesterday,-- 昨天	
		tomorrow-- 明天	
from(select a.record_date, -- 获取今天凌晨
				date_add(a.record_date,INTERVAL -1 DAY ) as yesterday,-- 昨天	
				date_add(a.record_date,INTERVAL 1 DAY ) as tomorrow-- 明天	
	from (select FROM_UNIXTIME(UNIX_TIMESTAMP(CAST(NOW()AS DATE))) record_date) a -- 获取今天凌晨
	)aa
)

record_datefirstDaylastDayyesterdaytomorrow
2022-04-06 00:00:002022-04-01 00:00:002022-05-01 00:00:002022-04-05 00:00:002022-04-07 00:00:00
select DATE_ADD(curdate(),interval -day(curdate())+1 day);-- 本月第一天 2022-04-01
select DATE_ADD(DATE_ADD(curdate(),interval 1 month),interval -day(curdate())+1 day);-- 下个月的第一天 2022-04-01

获取当前时间

select curdate(); -- 2021-03-02
select now();  -- 2021-03-02 11:52:56
-- 查看当天日期
select current_date(); -- 2021-03-03

-- 查看当天时间
select current_time(); -- 11:08:16

-- 查看当天时间日期
select current_timestamp();-- 2021-03-03 11:08:42

-- 获取当前时间的年份
select YEAR(now());
select MONTH(now());

返回日期在本年的第几天,范围为1到366。

SELECT DAYOFYEAR('2008-02-03'); # 34

SELECT DAYOFYEAR(now()); # 137

SELECT DAYOFYEAR(sysdate()); # 137

TIMESTAMPDIFF: MySql计算两日期时间之间相差的天数,秒数,分钟数,周数,小时数

语法为: TIMESTAMPDIFF(unit,datetime_expr1,datetime_expr2);

返回日期或日期时间表达式 datetime_expr1 和datetime_expr2the 之间的整数差。

其中unit单位有如下几种,分别是:FRAC_SECOND (microseconds), SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, YEAR 。 该参数具体释义如下: FRAC_SECOND 表示间隔是毫秒 SECOND 秒 MINUTE 分钟 HOUR 小时 DAY 天 WEEK 星期 MONTH 月 QUARTER 季度 YEAR 年

例如:
#计算两日期之间相差多少周
select timestampdiff(week,'2011-09-30','2015-05-04');
#计算两日期之间相差多少天
select timestampdiff(day,'2011-09-30','2015-05-04');
另外计算两日期或时间之间相差多少天还可以使用 to_days 函数,但是该函数不用于阳历出现(1582)前的值,原因是当日历改变时,遗失的日期不会被考虑在内。因此对于1582 年之前的日期(或许在其它地区为下一年 ), 该函数的结果实不可靠的。具体用法如:
to_days(end_time) - to_days(start_time);
  
#计算两日期/时间之间相差的秒数:
select timestampdiff(SECOND,'2011-09-30','2015-05-04');
另外还可以使用 MySql 内置函数 UNIX_TIMESTAMP 实现,如下:
SELECT UNIX_TIMESTAMP(end_time) - UNIX_TIMESTAMP(start_time); 
  
#计算两日期/时间之间相差的时分数: 
select timestampdiff(MINUTE,'2011-09-30','2015-05-04');
另外还可以如下实现:
SELECT SEC_TO_TIME(UNIX_TIMESTAMP(end_time) - UNIX_TIMESTAMP(start_time));

  1. 相差多少天 可以使用 to_days 函数 to_days(end_time) - to_days(start_time);

    或者使用 DATEDIFF 函数:SELECT DATEDIFF('2020-01-13','2018-10-01');

  2. 相差的秒数 可以使用 UNIX_TIMESTAMP 函数:SELECT UNIX_TIMESTAMP(end_time) - UNIX_TIMESTAMP(start_time);

  3. 相差分支数 可以使用 SEC_TO_TIME 函数:SELECT SEC_TO_TIME(UNIX_TIMESTAMP(end_time) - UNIX_TIMESTAMP(start_time));

  4. now() 函数返回的是当前时间的年月日时分秒,如:2008-12-29 16:25:46;

  5. CURDATE() 函数返回的是年月日信息: 如:2018-6-17

  6. **CURTIME()**函数返回的是当前时间的时分秒信息,如:22:50

  7. 格式化时间:包含年月日时分秒日期格式化成年月日日期,可以使用DATE(time)函数,如DATE(now()) 返回的是 2018-6-17

    ​ 说明:需要格式化时间并且还要求不改变date格式的就可以用 select DATE('2021-02-12 00:00:00')

    ​ 如果时间格式字符串 date_format(now(), '%Y%m%d')

  8. unix_timestamp(日期,format) 把日期转化为时间戳 到秒的

  9. CURDATE() 获取的时间虽然和DATE(now()) 看起来一样,但实际值不一样。

案例

-- 1.查看当天日期
select current_date();

-- 2、 查看当天时间
select current_time();

-- 3、查看当天时间日期
select current_timestamp();

-- 4、查询当天记录
select * from 表名 where to_days(时间字段名) = to_days(now());

-- 5、查询昨天记录
select * from 表名 where to_days( now( ) ) – to_days( 时间字段名) <= 1

-- 6、查询7天的记录
select * from 表名 where date_sub(curdate(), interval 7 day) <= date(时间字段名) 

-- 7、查询近30天的记录
select * from 表名 where date_sub(curdate(), interval 30 day) <= date(时间字段名)

-- 8、查询本月的记录
select * from 表名 where date_format( 时间字段名, '%y%m' ) = date_format( curdate( ) , '%y%m' )

-- 9、查询上一月的记录
select * from 表名 where period_diff( date_format( now( ) , '%y%m' ) , date_format( 时间字段名, '%y%m' ) ) =1

-- 10、查询本季度数据
select * from 表名 where quarter(create_date)=quarter(now());

-- 11、查询上季度数据
select * from 表名 where quarter(create_date)=quarter(date_sub(now(),interval 1 quarter));

-- 12、查询本年数据
select * from 表名  where year(create_date)=year(now());

-- 13、查询上年数据
select * from 表名 where year(create_date)=year(date_sub(now(),interval 1 year));

-- 14、查询当前这周的数据
select * from 表名 where yearweek(date_format(submittime,'%y-%m-%d')) = yearweek(now());

-- 15、查询上周的数据
select * from 表名 where yearweek(date_format(submittime,'%y-%m-%d')) = yearweek(now())-1;

-- 16、查询当前月份的数据
select * from 表名   where date_format(submittime,'%y-%m')=date_format(now(),'%y-%m')

-- 17、查询距离当前现在6个月的数据
select name,submittime from enterprise where submittime between date_sub(now(),interval 6 month) and now();

-- 获取本月第一天
select DATE_ADD(curdate(),interval -day(curdate())+1 day)   

-- 获取当月最后一天
select last_day(curdate());   

-- 获取下个月的第一天
select date_add(curdate()-day(curdate())+1,interval 1 month ) 

-- 获取当前月的天数
select DATEDIFF(date_add(curdate()-day(curdate())+1,interval 1 month ),DATE_ADD(curdate(),interval -day(curdate())+1 day)) from dual 

注:spring-mvc 中,SQlMap 再是用获取本月第一天时引用以下语句 :
SELECT DATE_ADD(CURDATE(),INTERVAL 1-DAYOFMONTH(CURDATE()) DAY);  -- 获取本月第一天

Mysql 获取昨日、今日、明日凌晨时间 ,本月第一天,今年第一天

https://blog.csdn.net/qq_40058321/article/details/102224125open in new window

# 获取今日凌晨时间
UNIX_TIMESTAMP(CAST(SYSDATE()AS DATE))
# 获取昨日凌晨时间
UNIX_TIMESTAMP(CAST(SYSDATE()AS DATE) - INTERVAL 1 DAY)
# 获取明日凌晨时间
UNIX_TIMESTAMP(CAST(SYSDATE()AS DATE) + INTERVAL 1 DAY)
# 获取的是时间戳
 
# 时间戳转换成datetime, datetime也就是我们说的日期格式 年-月-日 时-分-秒
FROM_UNIXTIME();
# datetime转时间戳
UNIX_TIMESTAMP();
 
# 测试
SELECT FROM_UNIXTIME(UNIX_TIMESTAMP(CAST(SYSDATE()AS DATE))) 今日;-- 2019-10-06 00:00:00
SELECT FROM_UNIXTIME(UNIX_TIMESTAMP(CAST(SYSDATE()AS DATE) - INTERVAL 1 DAY)) 昨日;-- 2019-10-05 00:00:00
SELECT FROM_UNIXTIME(UNIX_TIMESTAMP(CAST(SYSDATE()AS DATE) + INTERVAL 1 DAY)) 明日;-- 2019-10-07 00:00:00 

# 本月第一天
select DATE_ADD(curdate(),interval -day(curdate())+1 day)
-- 今年第一天
SELECT DATE_FORMAT(DATE_SUB(CURDATE(),INTERVAL dayofyear(now())-1 DAY),'%Y-%m-%d') AS c_year
-- 去年第一天
SELECT DATE_FORMAT((DATE_SUB((DATE_SUB(NOW(),INTERVAL dayofyear(now())-1 DAY)),INTERVAL 1 YEAR)),'%Y-%m-%d') AS last_year

格式化日期 DATE_FORMAT

# DATE_FORMAT(datetime ,format)用法,转换日期格式
DATE_FORMAT('2019-10-07 00:00:00', '%Y-%c-%d');-- 2019-10-07

 
# 常用的:
# '%Y-%c-%d %H:%i:%S'  
#  年-月-日  时:分:秒
# '%Y-%c-%d'
#  年-月-日
# '%H:%i:%S'
#  时:分:秒

可以使用的格式有:

格式描述
%a缩写星期名
%b缩写月名
%c月,数值
%D带有英文前缀的月中的天
%d月的天,数值(00-31)
%e月的天,数值(0-31)
%f微秒
%H小时 (00-23)
%h小时 (01-12)
%I小时 (01-12)
%i分钟,数值(00-59)
%j年的天 (001-366)
%k小时 (0-23)
%l小时 (1-12)
%M月名
%m月,数值(00-12)
%pAM 或 PM
%r时间,12-小时(hh:mm:ss AM 或 PM)
%S秒(00-59)
%s秒(00-59)
%T时间, 24-小时 (hh:mm:ss)
%U周 (00-53) 星期日是一周的第一天
%u周 (00-53) 星期一是一周的第一天
%V周 (01-53) 星期日是一周的第一天,与 %X 使用
%v周 (01-53) 星期一是一周的第一天,与 %x 使用
%W星期名
%w周的天 (0=星期日, 6=星期六)
%X年,其中的星期日是周的第一天,4 位,与 %V 使用
%x年,其中的星期一是周的第一天,4 位,与 %v 使用
%Y年,4 位
%y年,2 位

eg:

SELECT
  id,
  DATE_FORMAT(create_time, '%Y-%c-%d %H:%i:%S')
FROM epc_mes_item
WHERE create_time = '2018-11-19 23:00:20';

SELECT
  id,
  DATE_FORMAT(create_time, '%Y-%c-%e')
FROM epc_mes_item
WHERE DATE_FORMAT(create_time, '%Y-%c-%d') >= '2018-11-19';

date_add 、DATE_SUB 固定时间加 天,小时

  • date_add
select date_add(日期, interval 1 day); 日期加天

select date_add(日期, interval 1 hour); 日期加小时

select date_add(日期, interval 1 minute); 日期加分

select date_add(日期, interval 1 second);日期加秒

select date_add(日期, interval 1 microsecond); 日期加微秒

select date_add(日期, interval 1 week); 日期加周

select date_add(日期, interval 1 month); 日期加月

select date_add(日期, interval 1 quarter); 日期加季度

select date_add(日期, interval 1 year); 日期加年
  • DATE_SUB
SELECT * FROM 表名 WHERE 时间字段>DATE_SUB(CURDATE(), INTERVAL 1 YEAR) 一年
SELECT * FROM 表名 WHERE 时间字段>DATE_SUB(CURDATE(), INTERVAL 1 WEEK)  一周
SELECT * FROM 表名 WHERE 时间字段 >DATE_SUB(CURDATE(), INTERVAL 3 MONTH) 三个月
1、当前日期
select DATE_SUB(curdate(), INTERVAL 0 DAY) ;
 
2、明天日期
select DATE_SUB(curdate(), INTERVAL -1 DAY) ;
 
3、昨天日期
select DATE_SUB(curdate(), INTERVAL 1 DAY) ;
 
4、前一个小时时间
select date_sub(now(), interval 1 hour);
 
5、后一个小时时间
select date_sub(now(), interval -1 hour);
 
6、前30分钟时间
select date_add(now(), interval -30 minute)
 
7、后30分钟时间
select date_add(now(), interval 30 minute)

使用按理:

select a.record_date, -- 获取今天凌晨
				date_add(a.record_date,INTERVAL -1 DAY ) as yesterday,-- 昨天	
				date_add(a.record_date,INTERVAL 1 DAY ) as tomorrow-- 明天	
from (select FROM_UNIXTIME(UNIX_TIMESTAMP(CAST(NOW()AS DATE))) record_date) a -- 获取今天凌晨
use sdt_xjb;
with record_date_temp as (-- 用来事先 定义好 当前凌晨,用于过滤时间
	select record_date,
		DATE_ADD(record_date,interval -day(record_date)+1 day) monthFirstDay,-- 今天对应的所在月第一天
		yesterday,-- 昨天	
		tomorrow-- 明天	
from(select a.record_date, -- 获取今天凌晨
				date_add(a.record_date,INTERVAL -1 DAY ) as yesterday,-- 昨天	
				date_add(a.record_date,INTERVAL 1 DAY ) as tomorrow-- 明天	
	from (select FROM_UNIXTIME(UNIX_TIMESTAMP(CAST(NOW()AS DATE))) record_date) a -- 获取今天凌晨
	) aa
)

select * from record_date_temp

12.92 时间查询

 AND a.op_date LIKE concat( DATE_FORMAT(CURDATE(), '%Y-%m'), '%' )

12.93 查询本月每天

  • 查询当月的每一天,由1号开始,到当月结束日期
SELECT
date_add(DATE_ADD(curdate(), INTERVAL - DAY(curdate()) + 2 DAY), INTERVAL (cast( help_topic_id AS signed INTEGER ) - 1 ) DAY ) DAY 
from mysql.help_topic
where help_topic_id  < day(last_day(curdate()))
order by help_topic_id
  • 查询当天起未来一个月(30天)的每一天
select 
date_add(curdate(), interval(cast(help_topic_id as signed integer) ) day) day
from mysql.help_topic
where help_topic_id  < day(last_day(curdate()))
order by help_topic_id

Mysql中Cast()函数的用法

Cast(字段名 as 转换的类型 ),其中类型可以为:字符类型转换

CHAR字符型
DATE日期型
DATETIME日期和时间型
DECIMALfloat型(没测试出来效果)
SIGNEDint
TIME时间型
-- CHAR 字符型

select cast('2022-07-13 18:16:29' as DATE) ; -- 2022-07-13
select cast('2022-07-13 18:16:29' as DATETIME) ; -- 2022-07-13 18:16:29
select cast('2022-07-13 18:16:29' as TIME) ; -- 18:16:29

select cast('2022-07-13 18:16:29' as char);  -- 2022-07-13 18:16:29;
select cast('2022-07-13 18:16:29' as DECIMAL) ; -- 2022
select cast('2022-07-13 18:16:29' as SIGNED) ; -- 2022


-- MySQL 中的变量来计算累计值。由于变量默认是 double 类型,对于一些小数运算可能会出现舍入误差
select CAST(74.000000000000000000000 AS UNSIGNED); -- 74

12.100MySql 对小数的处理

  • round 函数,四舍五入
select round(12.34512,3) -- 12.345
  • FLOOR 表示向下取整,只返回值X的整数部分,小数部分舍弃

  • CEILING 表示向上取整,只返回值X的整数部分,小数部分舍弃。

12.110 MySQL

longtext类型 不能用 is null 来 实现过滤 为空的字段 使用函数: length(xx) =0 来过滤数据

select  * from t_restree k where length(k.c_extended) =0;

12.120 根据不同维度 对数据汇总,行转列 的一种写法

场景:ads_sdxs_index_day表根据 巡视类型 专业、专项 记录每天 的应巡、已巡、人巡、无人机巡视的次数。现在需要统计,专业、专项、全部、人巡、机巡 巡视完成率

  • 数据源:
SELECT
	major_id,-- 巡视类型(1:专业2:专项)
	major_name,
	todo_xscs,-- 应巡
	done_xscs_month,-- 已巡
	rg_done_xscs_month,-- 人工巡视
	wrj_done_xscs_month-- 无人机巡视
	-- round( done_xscs_month / todo_xscs * 100, 2 ) AS wcl 
FROM ads.ads_sdxs_index_day 
WHERE TO_DAYS( data_dt )= TO_DAYS(now())
巡视类型id巡视类型应巡已巡人工巡视无人机巡视
2专项巡视544011076621076620
1专业巡视1821217287128574430
  • 效果:需要统计,专业、专项、全部、人巡、机巡 巡视完成率
select
            CONCAT(zy_wcl,'%') as zy_wcl,
            CONCAT(zx_wcl,'%') as zx_wcl,
            CONCAT(all_wcl,'%') as all_wcl,
            CONCAT(jx_wcl,'%') as jx_wcl,
            CONCAT(rx_wcl,'%') as rx_wcl,
            done_xscs_month,todo_xscs
        from (select
                sum(case when major_id =1 then wcl else 0 end )as zy_wcl,/*专业巡视*/
                sum(case when major_id =2 then wcl else 0 end )as zx_wcl,/*专项巡视*/
                round(sum(done_xscs_month)/sum(todo_xscs)*100,2) as all_wcl,/*总巡视*/
                round(sum(rg_done_xscs_month)/sum(todo_xscs)*100,2) as rx_wcl,/*人工巡视*/
                round(sum(wrj_done_xscs_month)/sum(todo_xscs)*100,2) as jx_wcl,/*无人机巡视*/
                sum(done_xscs_month) done_xscs_month,
				sum(todo_xscs) todo_xscs
            from(
                select major_id,major_name,todo_xscs,done_xscs_month,rg_done_xscs_month,wrj_done_xscs_month, round(done_xscs_month/todo_xscs*100,2) as wcl
                FROM ads.ads_sdxs_index_day WHERE TO_DAYS( data_dt )= TO_DAYS(now())
            ) aa
		)a
zy_wcl(专业巡视完成率)zx_wcl(专项巡视完成率)all_wcl(巡视完成率)jx_wcl(人巡完成率)rx_wcl(机巡完成率)done_xscs_month(巡视次数)todo_xscs(应巡次数)
94.92%197.9%172.08%6.09%165.99%12494972613

12.125 使用LIMIT 分组

注意事项,需要对写的sql 添加 order by ,否则会查询失败

12.126

SELECT * from bas_tower where  FIND_IN_SET(id, '623,23');

12.127 mysql排序字段为空的排在最后面

oracle 可以通过 实现oracle的nulls [first|last] 实现

select *from (
select -1 sort_num ,start_device from BAS_LINE_EXT where id=81 
	union all 
	select null sort_num ,end_device from BAS_LINE_EXT where id=81
)aa
order by  sort_num is null,sort_num

12.130

**left join中 由于 没有和主表关联,导致 使用 逻辑运算符(> = <)出错 **

SELECT
	m.tower_id,m.tower_name, m.tower_no,m.line_id,m.line_name,m.section_id,m.section_name
FROM dw.dwd_sdxs_plan_month m
LEFT JOIN (
	SELECT op.major,op.tower_id,
	count(1) AS done_xscs
	FROM dw.dwd_sdxs_job_op op
	WHERE date_format( op.op_time, '%Y-%m' ) = date_format( now(), '%Y-%m' )
	GROUP BY op.major, op.tower_id
) c ON c.major = m.major  AND c.tower_id = m.tower_id
WHERE date_format( m.data_dt, '%Y-%m' ) = date_format( now(), '%Y-%m' )
AND m.todo_xscs > 0
AND todo_xscs > ifnull(done_xscs,0) /* 获取 应巡大于已巡的次数的记录*/
/**
	AND todo_xscs > ifnull(done_xscs,0) 而不能 用 AND todo_xscs > done_xscs
	因为 left join 可能没关联上,顾使用 AND todo_xscs > done_xscs 导致 针对done_xscs 为null 的记录被忽略
	所以 使用  AND (todo_xscs > done_xscs or done_xscs is null)
			或者 AND todo_xscs > ifnull(done_xscs,0) 都行
	
*/

批量修改

update DS_BASE_ORG_RULE r 
left join DS_BASE_ORG_RULE r1  on r.id=(r1.id+1)
set r.start_time =r1.start_time  , r.end_time=r1.end_time, r.name=r1.name
where r.id>10;

批量查询

<select id="selectAllItemsByIds" parameterType="java.util.List" resultMap="BaseResultMap">selectid, field1, field2from item_info<where>id in<foreach collection="list" item="item" open="(" separator="," close=")">#{item}</foreach></where>
</select>

批量删除

<delete id="deleteUsersByNames" parameterType="java.util.List">delete from item_infowhere username in<foreach collection="list" item="item" open="(" separator="," close=")">#{item}</foreach>
</delete>

批量更新

<update id="updateItemsById"><foreach collection="list" item="item" index="index" open="" close="" separator=";">UPDATE item_info<set>field1 = #{item.field1}</set>WHERE id = #{id}</foreach>
</update>




    <insert id="updateInsert">
        <foreach collection="list" item="item" index="index" open="" close="" separator=";">
            update bas_line_contrast
            <set >
                <if test="item.secretName != null" >
                    secret_name = #{item.secretName,jdbcType=VARCHAR},
                </if>
            </set>
            where id = #{item.id}
        </foreach>
    </insert>
<update id="updateMateriaStatus">
    UPDATE zjjc_material_info
    SET
    <foreach collection="list" item="item" open="sh_state = case id" close="END">
        WHEN #{item.materialId} THEN #{item.shState}
    </foreach>
    WHERE id IN
    <foreach collection="list" item="item" separator="," open="(" close=")">
        <if test="item.materialId != null">
            #{item.materialId}
        </if>
    </foreach>
</update>

批量插入

<insert id="insertItemsByBatch" parameterType="java.util.List">insert into item_info (id,field1,field2)values<foreach collection="list" item="item" index="index" separator=",">(#{item.id,jdbcType=BIGINT},#{item.field1,jdbcType=VARCHAR},#{item.field2,jdbcType=VARCHAR})</foreach>
</insert>
  • 支持批量插入后返回主键id,针对父子表的情况

1212
1212

2.

http://mylishihuan.gitee.io/web_book/src/java/数据库/MySql卸载和安装.htmlopen in new window

linux下开启、关闭、重启mysql服务

https://blog.csdn.net/weixin_44708715/article/details/90702843open in new window

通过:SHOW VARIABLES LIKE "%char%"; 查询文件位置

一、 启动

1、使用 service 启动:service mysql start 2、使用 mysqld 脚本启动:/etc/inint.d/mysql start 3、使用 safe_mysqld 启动:safe_mysql&

二、停止

1、使用 service 启动:service mysql stop 2、使用 mysqld 脚本启动:/etc/inint.d/mysql stop 3、mysqladmin shutdown

三、重启

1、使用 service 启动:service mysql restart 2、使用 mysqld 脚本启动:/etc/inint.d/mysql restart

注:如果启动报下面的错误,可以使用: systemctl start mysqld

[root@bgtest01 mysql]# service mysql start
Redirecting to /bin/systemctl start mysql.service

mysql 忽略大小写

# Copyright (c) 2014, 2021, Oracle and/or its affiliates.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2.0,
# as published by the Free Software Foundation.
#
# This program is also distributed with certain software (including
# but not limited to OpenSSL) that is licensed under separate terms,
# as designated in a particular file or component or in included license
# documentation.  The authors of MySQL hereby grant you an additional
# permission to link the program and your derivative works with the
# separately licensed software that they have included with MySQL.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License, version 2.0, for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

#
# The MySQL  Server configuration file.
#
# For explanations see
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

[mysqld]
lower_case_table_names=1
log-bin=mysql-bin
server-id=1
pid-file	= /var/run/mysqld/mysqld.pid
socket		= /var/run/mysqld/mysqld.sock
datadir		= /var/lib/mysql
#log-error	= /var/log/mysql/error.log
# By default we only accept connections from localhost
#bind-address	= 127.0.0.1
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
event_scheduler=1
skip-grant-tables
max_connections=2000
group_concat_max_len=10240000
default-time-zone='+08:00'
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

mysql ifnull 判断空字符

varchar 字段不为null,此时无法使用ifnull

image-20220914112552954
image-20220914112552954

如果为null --> IFNULL(a,b) a 为null 则 取b

如果为空:ifnull(if(length(trim(字段))>0,字段,null),'1') num

小数的处理

99.9999 输出 99.9999

99.0000 输出 99

select 0+cast(90.9999 as char ); -- 90.9999
select 0+cast(90.0000 as char ); -- 90
select 0+cast(ifnull(字段,0) as char ); -- 90

常见异常

#### sql injection violation, comment not allow

sql 中添加的注释,导致执行失败

查询 大表

-- information_schema
select table_name,table_rows from tables order by table_rows desc ;

15.50 初始化异常

echarts 无法获取属性“getAttribute”的值: 对象为 null 或未定义 错误解决方法

造成这种错误的原因是

echarts.js引用放在head中或者放在body中HTML代码的前面了,造成加载时阻塞后面的html。

解决方法就是将echarts.js的引用放在</body>之前就可以了

创建 函数报错

参考open in new window

1418 - This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you might want to use the less safe log_bin_trust_function_creators variable)

原因 MySQL开启bin-log后,调用存储过程 或者函数以及触发器时,会出现错误号为1418的错误:

详细说明:

这是我们开启了bin-log, 我们就必须指定我们的函数是否是 1 DETERMINISTIC 不确定的 2 NO SQL 没有SQl语句,当然也不会修改数据 3 READS SQL DATA 只是读取数据,当然也不会修改数据 4 MODIFIES SQL DATA 要修改数据 5 CONTAINS SQL 包含了SQL语句

**解决:**创建子程序(存储过程、函数、触发器)时,声明为DETERMINISTIC或NO SQL与READS SQL DATA中的一个,就是在begin关键在之前声明,我这里声明为DETERMINISTIC

函数

解决不了 空字符串

select number_convert(12.345) ; -- 12.345 select number_convert(12.00) ; -- 12 select number_convert(10) ; -- 10 select number_convert('10.00') ; -- 10 select number_convert(null) ; -- 0 select number_convert('') ;-- Data truncated for column 'str' at row 1

-- 解决 小数的问题
CREATE FUNCTION number_convert (str double) RETURNS double
DETERMINISTIC --  添加这行,否则无法添加
begin

  DECLARE d double;
  set d= 0+cast(ifnull(str,0) as char );
  RETURN d;

end

if 的写法

begin
   IF num is not null THEN
		RETURN 0+cast(ifnull(num,0) as char );
	ELSEIF xxxxx THEN
    	RETURN xxx;
    ELSE
			RETURN 0;
    END IF;
end

函数

  • 判断是否为数值
CREATE DEFINER=`root`@`%` FUNCTION `isnumeric`(`str` int) RETURNS int(1)
BEGIN
		DECLARE temp int(1);
    DECLARE hasSqlError int DEFAULT FALSE;
    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET hasSqlError=TRUE;

		set temp = to_number(str);
    IF hasSqlError THEN
			RETURN 0;
    ELSE
			RETURN 1;
    END IF;
END

从身份证中拿到年纪

CREATE FUNCTION CalculateAgeFromIdentity(identity VARCHAR(18))
RETURNS INT
BEGIN
    DECLARE age INT;
    
    IF identity REGEXP '^[0-9]{17}[0-9X]$' THEN
        SET age = YEAR(CURDATE()) - YEAR(STR_TO_DATE(SUBSTRING(identity, 7, 8), '%Y%m%d')) - (RIGHT(CURDATE(), 5) < RIGHT(STR_TO_DATE(SUBSTRING(identity, 7, 8), '%Y%m%d'), 5));
    ELSE
        SET age = NULL;
    END IF;
    
    RETURN age;
END

存储过程


递归

这个查询会从指定的起始 menu_id(例如 2338)开始,逐级查找其子菜单

SELECT DISTINCT t1.* 
FROM sys_menu t1
JOIN (
    SELECT * FROM (
        SELECT * FROM sys_menu
        ORDER BY menu_id, parent_id
    ) AS t2
    JOIN (
        SELECT @pv := '2338' -- 设置起始 menu_id
    ) AS t3
    WHERE FIND_IN_SET(parent_id, @pv) > 0
    AND @pv := CONCAT(@pv, ',', menu_id)
) AS t4 ON t1.menu_id = t4.menu_id;

案例使用

1) 场景:检测设备状态

设备有效期 validity_date 如果小于今天,则算为超期

如果在30内,则算为 预警

否则数据正常

UPDATE zjjc_material_info
SET EFFE_STATE =
    CASE
        WHEN validity_date < CURDATE() THEN '03' -- 超期
        WHEN DATEDIFF(validity_date, CURDATE()) <= 30 THEN '02' -- 预警
        ELSE '01' -- 有效
    END

查询每张表的大小

SELECT 
	table_name AS '表名', 
	round((( data_length + index_length ) / 1024 / 1024 ), 2  ) AS '大小(MB)' 
FROM information_schema.TABLES 
WHERE table_schema = 'yjydxj-cloud' 
ORDER BY ( data_length + index_length ) DESC;

提示时间格式不对 1292

MySQL #1292 – Incorrect date value: ‘0000-00-00’

打开mysql目录下的my.ini文件,找到sql-mode /etc/my.conf

sql_mode="STRICT_ALL_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ZERO_DATE,NO_ZERO_IN_DATE,NO_AUTO_CREATE_USER

NO_ZERO_DATE,NO_ZERO_IN_DATE,删掉保存重启mysql即可

systemctl start mysqld # 启动
systemctl restart mysqld;
systemctl stop mysqld; ## 关闭

MySQL日志

场景:需要查看mysql的插入语句,通过日志来观察,sql的执行

额外补充:如果考虑到日志文件太大,可以删除,然后重新创建 再执行 mysql -u root -pSemdo@2021 -e "FLUSH LOGS;" 刷新日志保证日志的重新写入

  1. 使用mysql -u yourusername -p命令来登录,其中yourusername是你的MySQL用户名。
  2. 启用查询日志。你可以使用SET GLOBAL general_log = 'ON';命令来启动查询日志。(临时开启)
  3. 检查日志文件的位置。执行SHOW VARIABLES LIKE 'general_log_file';,你会看到日志文件的路径。
  4. 使用tail -f /path/to/your/logfile命令来查看日志文件,其中/path/to/your/logfile是你的日志文件路径。这个命令会显示日志文件的最新内容。
  5. 对于插入语句,你可以在日志文件中搜索INSERT关键字

查询大表

SELECT 
    table_schema AS `Database`,
    table_name AS `Table`,
    ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS `Size_MB`
FROM 
    information_schema.tables
WHERE 
    table_schema = 'yjydxj-cloud'  -- 用你自己的数据库名替换
		-- and table_name='sys_job_log'
GROUP BY 
    table_schema, table_name
ORDER BY 
    `Size_MB` DESC;

  • 删除表并且释放空间
TRUNCATE TABLE xxx;

执行表迁移卡死,定位到:未提交的事务阻塞 导致

通过 下面的查询 找出异常的阻塞的事务

SELECT * FROM information_schema.INNODB_TRX
WHERE trx_state = 'RUNNING'; -- 可以查询出 trx_id

通过 trx_id 进行关闭

-- 终止事务(替换为实际的 trx_id)
KILL 事务ID_1;

如果失败 通过 SHOW PROCESSLIST; 找出相应的进程

SHOW PROCESSLIST;

# 通过 进程id 进行关闭 
KILL 进程id; -- 替换为实际存在的 ID

关于 ON DUPLICATE KEY UPDATE 的解释

能实现INSERT 不用考虑是是否已经存在的问题,如果存在执行update

在之前的代码中,ON DUPLICATE KEY UPDATE 是用来处理插入时主键冲突的情况的。如果历史表中已经存在某条记录(例如主键 ID 冲突),则会执行更新操作,而不是插入重复记录。

示例:

sql浅色版本

INSERT INTO OD_ROUND_RECORD_HIS (ID, DEL_FLAG, UPDATE_DATE)
VALUES (123, 1, NOW())
ON DUPLICATE KEY UPDATE
    DEL_FLAG = VALUES(DEL_FLAG),
    UPDATE_DATE = VALUES(UPDATE_DATE);
  • 如果 ID = 123 的记录在历史表中不存在,则插入新记录。
  • 如果 ID = 123 的记录在历史表中已存在,则更新 DEL_FLAGUPDATE_DATE 字段。

MyBatis

集合:<if test="arr != null and arr.size() > 0">

数组:<if test="col != null and col.length > 0">
    <!-- 批量修改-->
    <update id="batchUpdate">
        <foreach collection="zjjcPersonList" item="item" index="index" open="" close="" separator=";">
            UPDATE zjjc_person
            <trim prefix="SET" suffixOverrides=",">
                <if test="item.empNo != null">emp_no = #{item.empNo},</if>
                <if test="item.name != null">name = #{item.name},</if>
                <if test="item.sex != null">sex = #{item.sex},</if>
                <if test="item.year != null">year = #{item.year},</if>
                <if test="item.phone != null">phone = #{item.phone},</if>
            </trim>
            WHERE id = #{item.id}
        </foreach>
    </update>

    <!-- 批量新增-->
    <insert id="batchInsert">
        insert into zjjc_person( emp_no,name,sex,year,phone )
        values
        <foreach collection="zjjcPersonList" separator="," item="item">
            ( #{item.empNo},#{item.name},#{item.sex},#{item.year},#{item.phone} )
        </foreach>
    </insert>
    
    
    
    <update id="updateMateriaStatusOfSuccess">
        UPDATE zjjc_material_info
        SET
            is_filings = '1',
            sh_state = '0',
            <foreach collection="list" item="item" open="certificate = case id" close="end">
                WHEN #{item.materialId} THEN #{item.testFileCode}
            </foreach>
            ,
            <foreach collection="list" item="item" open="certificate_file = case id" close="end">
                WHEN #{item.materialId} THEN #{item.testFile}
            </foreach>
            ,
           
            <foreach collection="list" item="item" open="validity_date = case id" close="end">
                WHEN #{item.materialId} THEN #{item.validityDate}
            </foreach>
        WHERE id IN
        <foreach collection="list" item="i" separator="," open="(" close=")">
            #{i.materialId}
        </foreach>
    </update>
<if test="ids != null ">
    and a.id in
    <foreach item="id" collection='ids.split(",")' open="(" separator="," close=")">
        #{id}
    </foreach>
</if>

concat

concat 如果拼接的项中有空,则返回空

concat( u.name, a.telephone) AS extData1, -- 如果telephone 为空,则 整个返回空
-- 正确写法
concat( u.name, IFNULL(a.telephone,'') ) AS extData1,
<select id="selectByFileIds" resultMap="...">
    SELECT * FROM table_name
    <where>
        <if test="fileIds != null and fileIds.size() > 0">
            AND file_id IN
            <foreach item="item" collection="fileIds" open="(" separator="," close=")">
                #{item}
            </foreach>
        </if>
    </where>
</select>

17 java

idea 修改 maven配置后,肯能出现 项目报错,重启,清楚缓存都不能正常编译,手动刷新maven

image-20210207150540404
image-20210207150540404

查找pom用的jar https://mvnrepository.com/open in new window

jar包冲突https://blog.csdn.net/noaman_wgs/article/details/81137893open in new window

17.1 事物

参考网址 : https://blog.csdn.net/zhou_java_hui/article/details/53302278open in new window

场景1: A类方法,调用B类b方法,如果b异常,想通过try catch 来自定义异常,不进行事物回滚

@Transactional(
			//propagation=Propagation.NESTED,
			propagation= Propagation.NESTED,
			rollbackFor={Exception.class}
	)
public void b(){
    // b方法
}

17.2 HttpClient4.x进行Get/Post请求并使用ResponseHandler处理响应

导包

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.4</version>
</dependency>
<!--StringUtil-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>${commons-lang3.version}</version>
</dependency>
<!--java.net-->
<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>1.4.1</version>
</dependency>
<!--http客户端-->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.4</version>
</dependency>

17.3 Java获取 当前访问的服务路径

String url =  request.getRequestURL().toString();
String baseUrl =url.substring(0,url.indexOf("/a/"));

17.4 获取当前ip&MAC地址

https://www.jb51.net/article/121571.htmopen in new window

	/**
	 * 获得用户远程地址
	 */
	public static String getRemoteAddr(HttpServletRequest request){
		String remoteAddr = request.getHeader("X-Real-IP");
        if (isNotBlank(remoteAddr)) {
        	remoteAddr = request.getHeader("X-Forwarded-For");
        }else if (isNotBlank(remoteAddr)) {
        	remoteAddr = request.getHeader("Proxy-Client-IP");
        }else if (isNotBlank(remoteAddr)) {
        	remoteAddr = request.getHeader("WL-Proxy-Client-IP");
        }
        return remoteAddr != null ? remoteAddr : request.getRemoteAddr();
	}

17.6 java @ResponseBody返回值中去掉NULL字段

https://www.cnblogs.com/zhangpengshou/p/5630723.htmlopen in new window

需要同时添加两个位置:

  • 1、annotation-driven过滤
<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                    <property name="dateFormat">
                        <bean class="java.text.SimpleDateFormat">
                            <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" />
                        </bean>
                    </property>
                    <property name="serializationInclusion">
                        <util:constant static-field="com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL" />
                    </property>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>
  • RequestMapping过滤
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="messageConverters">
        <list>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean id="jacksonObjectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
                        <property name="dateFormat">
                            <bean class="java.text.SimpleDateFormat">
                                <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" />
                            </bean>
                        </property>
                        <property name="serializationInclusion">
                            <util:constant static-field="com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL" />
                        </property>
                    </bean>
                </property>
                <property name="supportedMediaTypes">
                    <list>
                        <value>application/json;charset=UTF-8</value>
                        <value>application/x-www-form-urlencoded</value>
                    </list>
                </property>
            </bean>
            <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>image/jpeg</value>
                        <value>image/png</value>
                    </list>
                </property>
            </bean>
        </list>
    </property>
</bean>

springboot 的处理方式

https://www.cnblogs.com/qinxu/p/10208858.htmlopen in new window

spring:
# jackson时间格式化
  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss
    default-property-inclusion: non_null

17.10java之 Timer 类的简单使用案例open in new window

Timer来讲就是一个调度器 ,类似于js中的 定时器setInterval


阻塞

public static void main(String[] args) {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(5 * 1000); //设置暂停的时间 5 秒
                System.out.println(sdf.format(new Date()) + "--循环执行第" + (i+1) + "次");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

17.20 List转为String

public static String listToString(List list, char separator) {
    return StringUtils.join(list.toArray(), separator);
}
/**
	将transmissionHis.getCollectTypes() 拆分开,分为2个字段
*/
public void collectTypeClassified(TransmissionHis transmissionHis){
    if(StringUtils.isNotEmpty(transmissionHis.getCollectTypes())){
        String[] collectTypeArr = transmissionHis.getCollectTypes().split(",");
        List<String> collectTypelist = Lists.newArrayList();// 收藏
        List<String> deviceTypelist = Lists.newArrayList();// 设备
        for (String collectType: collectTypeArr) {
            if("14".equals(collectType)||"16".equals(collectType)){
                deviceTypelist.add(collectType);
            }else{
                collectTypelist.add(collectType);
            }
        }
        transmissionHis.setCollectTypes(listToString(collectTypelist,','));
        transmissionHis.setDevicetypes(listToString(deviceTypelist,','));
    }
}

17.30 java对时间处理

    /**
     * 获取本月的第一天和最后一天的日期,输出格式例如20160801,
     * 其中第一天的key为firstDay,最后一天的key为lastDay
     * @return dateMap
     * @author Gavin Ma
     */
    public static Map<String, String> getCurrentMonthFEDay() {
        Map<String, String> dateMap = new HashMap<String, String>();
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); 

        //获取前月的第一天
        Calendar   cal_1=Calendar.getInstance();//获取当前日期 
        cal_1.add(Calendar.MONTH, -1);
        cal_1.set(Calendar.DAY_OF_MONTH,1);//设置为1号,当前日期既为本月第一天 
        String firstDay = format.format(cal_1.getTime());
        //获取前月的最后一天
        Calendar cale = Calendar.getInstance();   
        cale.set(Calendar.DAY_OF_MONTH,0);//设置为1号,当前日期既为本月第一天 
        String lastDay = format.format(cale.getTime());
        dateMap.put("firstDay", firstDay);
        dateMap.put("lastDay", lastDay);
        return dateMap;
    }

计算2个时间段的交集时长

public static void main(String[] args) {
        long l = calculateOverlap("7:00", "9:00", "9:12", "9:30");
        System.out.println(l);
    }

    public static long calculateOverlap(String start1Str, String end1Str, String start2Str, String end2Str) {
        // 创建一个DateTimeFormatter来解析没有秒的时间
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("H:mm");
        // 使用formatter将字符串转化为LocalTime对象
        LocalTime start1 = LocalTime.parse(start1Str, formatter);
        LocalTime end1 = LocalTime.parse(end1Str, formatter);
        LocalTime start2 = LocalTime.parse(start2Str, formatter);
        LocalTime end2 = LocalTime.parse(end2Str, formatter);
        // 计算交叉时长
        LocalTime overlapStart = start1.isAfter(start2) ? start1 : start2;
        LocalTime overlapEnd = end1.isBefore(end2) ? end1 : end2;

        if (overlapStart.isBefore(overlapEnd)) {
            return Duration.between(overlapStart, overlapEnd).toMinutes();
        } else {
            return 0;
        }
    }

17.100 file

场景:文件的导出

  • 以流的形式输出到浏览器导出文件
  • 指定盘符导出
// 1.指定盘符导出,简单格式
public static void excelTest0() throws IOException {
    //创建HSSFWorkbook对象  
    HSSFWorkbook wb = new HSSFWorkbook();  
    //创建HSSFSheet对象  
    HSSFSheet sheet = wb.createSheet("sheet0");  
    //创建HSSFRow对象  
    HSSFRow row = sheet.createRow(0);  
    //创建HSSFCell对象  
    HSSFCell cell=row.createCell(0);  
    //设置单元格的值  
    cell.setCellValue("单元格中的中文");  
    //输出Excel文件  
    FileOutputStream output=new FileOutputStream("d:\\workbook.xls");  
    wb.write(output);  
    output.flush();  
}


//2. B/S模式中采用的输出方式,而不是输出到本地指定的磁盘目录。该代码表示将details.xls的Excel文件通过应答实体(response)输出给请求的客户端浏览器,客户端可保存或直接打开。
public static String excelTest2(HttpServletRequest request,HttpServletResponse response) throws IOException{
		    //创建HSSFWorkbook对象  
    HSSFWorkbook wb = new HSSFWorkbook();  
    //创建HSSFSheet对象  
    HSSFSheet sheet = wb.createSheet("sheet0");  
    //创建HSSFRow对象  
    HSSFRow row = sheet.createRow(0);  
    //创建HSSFCell对象  
    HSSFCell cell=row.createCell(0);  
    //设置单元格的值  
    cell.setCellValue("单元格中的中文");  
	  
	//输出Excel文件  
    OutputStream output=response.getOutputStream();  
    response.reset();  
    response.setHeader("Content-disposition", "attachment; filename=details.xls");  
    response.setContentType("application/msexcel");          
    wb.write(output);  
    output.close();  
    return null;  
}

17.110 用map对数据进行分类统计

1)案例1.用一个字段作为统计条件(以设备名称)

业务场景:大批量的导入设备,要求导入的数据中设备名称不能重复,所以要对导入的所以数据进行判断,看是否有重复

Map<String, List<NvrDevice>> map=new HashMap<String, List<NvrDevice>>();//=========NvrDevice为要导入的对象,将对象放到map里面,同时将设备名称作为map的key

===========这样就会将同一个key的对象放到同一个map中的list对象里

===========map里面存放多条不同key组成的键值对,,一个key 是 nvrName 讲相同的key的NvrDevice 合并再一起,塞在list中

//用来判断导入的数据中'名称'是否存在重复
for (NvrDevice nvrDevice2 : nvrDeviceList) {
	if(!map.containsKey(nvrDevice2.getNvrName())){//============containsKey是用来判断某个key是否存在
		map.put(nvrDevice2.getNvrName(), new ArrayList<NvrDevice>());//=====不存在就新new一个map对象,并将此时的设备名称作为key存入。
	}
	map.get(nvrDevice2.getNvrName()).add(nvrDevice2);//==最后map存放的就是唯一key的list对象,而list对象中可能存再设备名称相同的NvrDevice对象
}
for (String nvrName : map.keySet()) {//======================以key来遍历map集合
	if(map.get(nvrName).size()>1){//=======================表示对于设备名称的存在多条
		for (NvrDevice nvrDevice3 : map.get(nvrName)) {
			nvrDevice3.setResult(nvrDevice3.getResult()+nvrName+"有重复的;");
		}
	}
}
///=========================================验证导入数据是否存在重复,============================================
List<String> nameList=new ArrayList<String>()
for(NvrDevice nvrDevice2 : nvrDeviceList){//遍历对象,取除name,如果list里面没有,那么就将name 放到list里面,如果有表示存在重复
	String name= nvrDevice2.getNvrName();
	if (names.contains(fileName)) {// Java中list集合ArrayList 中contains包含的使用
		//说明存在
	}
	nameList.add(name);
}

2)案例2:

业务场景:服务商领用备品备件--存在自己的库存中使用,最后登记已经使用的数量。(所以思路是:以服务商和备件名来统计,具有相同的服务商和备件分别统计出,出库的量和已经使用的量)

img
img

//gcywBpbjCkRecord是出库记录 以他为主表。将具有相同的服务商和备件的使用量放到对应的出库记录中(相应的出库记录中增加一个存放使用量的字段)

public String fwsStock(GcywBpbjCkRecord gcywBpbjCkRecord, Model model){
		//一。获取对应服务商--备件的出库量(通过服务商和备件这两个条件去分类统计库存量分别是多少)
		List<GcywBpbjCkRecord> listData=new ArrayList<GcywBpbjCkRecord>();//最后存放数据的,将具有相同服务商--备件的记录中的数据存放在其中一个
		List<GcywBpbjCkRecord> list=gcywBpbjCkRecordService.findList(gcywBpbjCkRecord);//获取所有的信息
		for (GcywBpbjCkRecord fws : list) {
			if(!mapFws.containsKey(fws.getBcampany())){//通过服务商来第一层统计
				mapFws.put(fws.getBcampany(), new HashMap<String, List<GcywBpbjCkRecord>>());
			}
			if(mapFws.containsKey(fws.getBcampany())){
				if(!mapFws.get(fws.getBcampany()).containsKey(fws.getBpbjId())){
					mapFws.get(fws.getBcampany()).put(fws.getBpbjId(), new ArrayList<GcywBpbjCkRecord>());
				}
				mapFws.get(fws.getBcampany()).get(fws.getBpbjId()).add(fws);
			}
		}
		for(String fwsName:mapFws.keySet()){
			for (String bpbjName : mapFws.get(fwsName).keySet()) {
				Double num=0.0;
				for (GcywBpbjCkRecord ckRecord : mapFws.get(fwsName).get(bpbjName)) {
					num+=ckRecord.getBpbjNum();//=========1.数据中农具有相同服务商--备件的对象都存放在一个map中,然后遍历,将出库量的记录累加
				}
                //============2。将统计后的数据赋值给list中的第一个对象,作为有效数据listData.add(mapFws.get(fwsName).get(bpbjName).get(0));
                mapFws.get(fwsName).get(bpbjName).get(0).setBpbjNum(num);
			}
		}
		System.out.println(listData);
		//二。从使用记录中获取对应服务商--备件使用的实际量
		List<GcywBpbjUseSituation> listData2=new ArrayList<GcywBpbjUseSituation>();
		Map<String,List<GcywBpbjUseSituation>> map2=new HashMap<String, List<GcywBpbjUseSituation>>();
		Map<String,Map<String,List<GcywBpbjUseSituation>>> mapFws2=new HashMap<String, Map<String,List<GcywBpbjUseSituation>>>();
		List<GcywBpbjUseSituation> list2 = gcywBpbjUseSituationService.findList(new GcywBpbjUseSituation());
		for (GcywBpbjUseSituation fws : list2) {
			if(!mapFws2.containsKey(fws.getFwsId())){
				mapFws2.put(fws.getFwsId(), new HashMap<String, List<GcywBpbjUseSituation>>());
			}
			if(mapFws2.containsKey(fws.getFwsId())){
				if(!mapFws2.get(fws.getFwsId()).containsKey(fws.getBpbjId())){
					mapFws2.get(fws.getFwsId()).put(fws.getBpbjId(), new ArrayList<GcywBpbjUseSituation>());
				}
				mapFws2.get(fws.getFwsId()).get(fws.getBpbjId()).add(fws);
			}
		}
		for(String fwsName:mapFws2.keySet()){
			for (String bpbjName : mapFws2.get(fwsName).keySet()) {
				Double num=0.0;
				for (GcywBpbjUseSituation gcywBpbjUse : mapFws2.get(fwsName).get(bpbjName)) {
					num+=gcywBpbjUse.getBpbjNumber();
				}
				mapFws2.get(fwsName).get(bpbjName).get(0).setBpbjNumber(num);
				listData2.add(mapFws2.get(fwsName).get(bpbjName).get(0));
			}
		}
	//三。用来整合上面的2个map集合,将实际使用量在写到对应的出库记录中
		Map<String,Map<String, GcywBpbjUseSituation>> maps=new HashMap<String, Map<String,GcywBpbjUseSituation>>();
		for (GcywBpbjUseSituation fws : listData2) {
			if(!maps.containsKey(fws.getFwsId())){
				maps.put(fws.getFwsId(), new HashMap<String, GcywBpbjUseSituation>());
			}
			if(maps.containsKey(fws.getFwsId())){
				if(!maps.get(fws.getFwsId()).containsKey(fws.getBpbjId())){
					maps.get(fws.getFwsId()).put(fws.getBpbjId(), fws);
				}
			}
		}
		for(GcywBpbjCkRecord ckRecord :listData){
			if(maps.containsKey(ckRecord.getBcampany())){
				if(maps.get(ckRecord.getBcampany()).containsKey(ckRecord.getBpbjId())){
					System.out.println(maps.get(ckRecord.getBcampany()).get(ckRecord.getBpbjId()).getBpbjNumber());
					ckRecord.setBpbjNumber(maps.get(ckRecord.getBcampany()).get(ckRecord.getBpbjId()).getBpbjNumber());
				}
			}
		}
		System.out.println(listData2);
		model.addAttribute("gcywBpbjCkRecord",gcywBpbjCkRecord);
		model.addAttribute("listData",listData);
		return "modules/gcyw/gcywBpbjFwsStock";
	}

java中Map遍历的四种方式open in new window

在java中所有的map都实现了Map接口,因此所有的Map(如HashMap, TreeMap, LinkedHashMap, Hashtable等)都可以用以下的方式去遍历。

方法一:在for循环中使用entries实现Map的遍历:

/**
* 最常见也是大多数情况下用的最多的,一般在键值对都需要使用
 */
Map <String,String>map = new HashMap<String,String>();
map.put("熊大", "棕色");
map.put("熊二", "黄色");
for(Map.Entry<String, String> entry : map.entrySet()){
    String mapKey = entry.getKey();
    String mapValue = entry.getValue();
    System.out.println(mapKey+":"+mapValue);
}

方法二:在for循环中遍历key或者values,一般适用于只需要map中的key或者value时使用,在性能上比使用entrySet较好;

Map <String,String>map = new HashMap<String,String>();
map.put("熊大", "棕色");
map.put("熊二", "黄色");
//key
for(String key : map.keySet()){
    System.out.println(key);
}
//value
for(String value : map.values()){
    System.out.println(value);
}

方法三:通过Iterator遍历;

Iterator<Entry<String, String>> entries = map.entrySet().iterator();
while(entries.hasNext()){
    Entry<String, String> entry = entries.next();
    String key = entry.getKey();
    String value = entry.getValue();
    System.out.println(key+":"+value);
}

方法四:通过键找值遍历,这种方式的效率比较低,因为本身从键取值是耗时的操作;

for(String key : map.keySet()){
    String value = map.get(key);
    System.out.println(key+":"+value);
}

17.120 生成json 文件

- 指定盘符生成json文件
- map对数据的 处理
	/**
	 根据 不同电压 等级,组装 地图需要的线路数据
		List<LineVO> lineList = findLineByIds(towerVO);
		查询出来的数据是利用resultMap  构成 线路--GT 结构(线路pojo对象 包含一个GT的List集合)
		遍历结果集: 利用map讲同一个电压等级下的线路合并到一起。最后再遍历这个map,生成不同电压等级下的 线路json文件
        	例如:35KV.json  
            [{
                "type": "Feature",
                "geometry": {
                  "type": "LineString",
                  "coordinates": [[118.5705817,31.00465868],[118.5715242,31.00233154],[118.5722901,31.00044132],......]
                },
                "properties": {
                  "ID":"ys500sdwsdsidjqsxian",
                 "NAME":"35kV吉泉线接地极",
                 "voltageGrade":"35kV"
                }
              }]	
	
	**/
	public String createLineFileForMap(TowerVO towerVO) throws Exception {
		String filePath = "";
		try {
			List<LineVO> lineList = findLineByIds(towerVO);
			filePath = SettingUtils.getCfgSettingByCodeForSelf("platform_address").getValue()+"/lineDataFor3DMap/data";// 指定导出文件
			/**
			 * json 格式中 第一层 线路组成的数组
			 *  每个线路对象 3个参数,第一个type 类型
			 *  第二个 坐标信息 geometry,又分为 2个对象,第一个是 type='LineString'类型 ,第二个是 坐标信息 [[],[],[]......]
			 *  第三个 属性信息properties
			 * 需要以每个不同 电压等级 创建一个 对于的JSON 文件,然后把 同一个电压等级下的线路组装在一起,再写入到该json文件中
			 */
			Map<String,JSONArray> voltageGradeMap=Maps.newHashMap();
			//JSONArray lineJsonList = new JSONArray();
			for (LineVO lineVO:lineList) {
				//============containsKey是用来判断某个key是否存在
				if(!voltageGradeMap.containsKey(lineVO.getVoltageGrade())){
					//=====不存在就新new一个map对象,并将此时的设备名称作为key存入。
					voltageGradeMap.put(lineVO.getVoltageGrade(), new JSONArray());
				}
				//1.组装第一层 type='Feature'
				JSONObject lineObject = new JSONObject();
				lineObject.put("type","Feature");//
				//2. 线路属性信息(计划存储,电压等级,线路id,线路名称)
				JSONObject linePropertiesObject = new JSONObject();
				linePropertiesObject.put("ID",lineVO.getLineId());// 线路id
				linePropertiesObject.put("NAME",lineVO.getLineName());
				linePropertiesObject.put("linePmsid",lineVO.getLinePmsid());
				linePropertiesObject.put("voltageGrade",lineVO.getVoltageGrade());
				lineObject.put("properties",linePropertiesObject);// 线路 属性 信息
				//3.坐标信息
				JSONArray coordinatesList = new JSONArray();
				for (TowerVO tower: lineVO.getTowerList()) {
					JSONArray towerPointList = new JSONArray();
					towerPointList.add(0,Float.parseFloat(tower.getLongitudeGps()));// 经度
					towerPointList.add(1,Float.parseFloat(tower.getLatitudeGps()));// 纬度
					coordinatesList.add(towerPointList);
				}
				// 组装 坐标信息
				JSONObject geometryObject = new JSONObject();
				geometryObject.put("type","LineString");
				geometryObject.put("coordinates",coordinatesList);
				lineObject.put("geometry",geometryObject);
				//lineJsonList.add(lineObject);
				voltageGradeMap.get(lineVO.getVoltageGrade()).add(lineObject);
			}
			System.out.println(voltageGradeMap);
			for(String key : voltageGradeMap.keySet()){
				JSONArray lineJsonList = voltageGradeMap.get(key);
				createJsonFile(lineJsonList,filePath+"/"+key+".json");
			}
			logger.info("##==三维地图生成不同电压等级下的线路JSON文件===="+"导出线路成功:"+filePath);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("##==三维地图生成不同电压等级下的线路JSON文件===="+"导出线路失败:"+e);
			throw new Exception("文件生成失败"+e);
		}
		System.out.println("线路JSON文件生成完成!");
		return filePath;
	}
	/**
	 * 将JSON数据格式化并保存到文件中
	 * @param jsonData 需要输出的json数
	 * @param filePath 输出的文件地址
	 * @return
	 */
	public static boolean createJsonFile(Object jsonData, String filePath) {
		String content = JSON.toJSONString(jsonData, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
				SerializerFeature.WriteDateUseDateFormat);
		// 标记文件生成是否成功
		boolean flag = true;
		// 生成json格式文件
		try {
			// 保证创建一个新文件
			File file = new File(filePath);
			if (!file.getParentFile().exists()) { // 如果父目录不存在,创建父目录
				file.getParentFile().mkdirs();
			}
			if (file.exists()) { // 如果已存在,删除旧文件
				file.delete();
			}
			file.createNewFile();
			// 将格式化后的字符串写入文件
			Writer write = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
			write.write(content);
			write.flush();
			write.close();
		} catch (Exception e) {
			flag = false;
			e.printStackTrace();
		}
		return flag;
	}

17.125 java io

//获取指定的文件夹下的所有pdf 文件
	public static ArrayList<String> getFiles(String path) {//D:\myEclipse\apache-tomcat-7_myElipse\apache-tomcat-7.0.73\webapps\SDT_IMOA\//work/zip/20190424163926/LA-R-2019-012-万善昆-1000kV圩淮Ⅰ线
	    ArrayList<String> files = new ArrayList<String>(); 
	    File file = new File(path);
	    File[] tempList = file.listFiles();

	    for (int i = 0; i < tempList.length; i++) {
	        if (tempList[i].isFile()) {
	        	 String fileName = tempList[i].getName();//获取文件名
	        	
	        	 String fileType=fileName.substring(fileName.lastIndexOf(".")+1);//获取文件的后缀名
	        	 if("pdf".equals(fileType)||"PDF".equals(fileType)){
	        		 files.add(tempList[i].toString());
	        	 }
	        }
	        
	    }
	   System.out.print("文     件:" + files);
	    return files;
	}

17.126 Java IO 创建文件解决文件名重复问题

文件名在操作系统中不允许出现 / \ ” : | * ? < > 故将其以空替代

**注:**文件创建时需要注意

  • 名称特殊字符(例如 \ 会导致文件被当作文件夹截取 )
  • 文件名称重复
Pattern pattern = Pattern.compile("[\\s\\\\/:\\*\\?\\\"<>\\|]");
Matcher matcher = pattern.matcher(fileName);
fileName= matcher.replaceAll(""); // 将匹配到的非法字符以空替换

两个思路:

​ 1.定义一个全局变量Map,以文件名称为key,序号为value. 调用方法前,先初始化map,因为全局变量,防止历史对数据的影响。方法调用需要传递fileName,判断当前文件名对应的key是否存在,如果存在则value+1,并拼好名称,返回

​ 2.传递一个路径,判断文件是否存在,循环,直到文件名称没有重复返回

  • 方法1:
	/**
	 * 防止文件名称重复,导致java io 读写失败
	 * 考虑有些 是 添加到 压缩包中,所以无法通过 io 来验证是否存在
	 * @param filePath
	 * @param fileName
	 * @return
	 * List<String> fileNameList=new ArrayList<String>();// 防止文件名重复 导致 程序异常,故需对 每个 文件名进行判断
	 
	 */
	public static String getFileNameByList(List<String> fileNameList, String fileName){
		fileName = StringEscapeUtils.unescapeHtml(fileName);
		Matcher matcher = pattern.matcher(fileName);//判断是否存在特殊字符 做空字符串替换
		fileName= matcher.replaceAll(""); // 将匹配到的非法字符以空替换
		//文件名,如spring
		String name = fileName.substring(0,fileName.indexOf("."));
		//文件后缀,如.jpeg
		String suffix = fileName.substring(fileName.lastIndexOf("."));
		int i = 1;
		//若文件存在重命名
		while(fileNameList.contains(fileName)) {
			String newFilename = name+"("+i+")"+suffix;
			i++;
			fileName=newFilename;
		}
		return fileName;
	}
  • 方法2:
	// 防止文件名称重复,导致java io 读写失败
	public String getFileName(String fileName,String filePath){
		//源文件
		File descFile  = new File(filePath+ File.separator+fileName);
		int i = 1;
		//若文件存在重命名
		while(descFile .exists()) {
			String newFilename = fileName+"("+i+")";
			descFile = new File(filePath+ File.separator+newFilename);
			i++;
		}
		return fileName;
	}
package practice.IO;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
 * @author 言曌
 * @date 2017/12/2 上午10:59
 */
public class Demo {
    /**
     * 将 /Users/liuyanzhao/Desktop/spring.jpeg 文件
     * 拷贝到 /Users/liuyanzhao/Desktop/io/中
     * 需要避免文件名重复覆盖的情况
     */
    public static void main(String args[]) throws IOException {
        //源文件
        File sourceFile = new File("/Users/liuyanzhao/Desktop/spring.jpeg");
        //文件的完整名称,如spring.jpeg
        String filename = sourceFile.getName();
        //文件名,如spring
        String name = filename.substring(0,filename.indexOf("."));
        //文件后缀,如.jpeg
        String suffix = filename.substring(filename.lastIndexOf("."));
        //目标文件
        File descFile = new File("/Users/liuyanzhao/Desktop/io"+File.separator+filename);
        int i = 1;
        //若文件存在重命名
        while(descFile.exists()) {
            String newFilename = name+"("+i+")"+suffix;
            String parentPath = descFile.getParent();
            descFile = new File(parentPath+ File.separator+newFilename);
            i++;
        }
        descFile.createNewFile();  //新建文件
        FileInputStream fin = new FileInputStream(sourceFile);
        FileOutputStream fout = new FileOutputStream(descFile);
        byte[] data = new byte[512];
        int rs = -1;
        while((rs=fin.read(data))>0) {
            fout.write(data,0,rs);
        }
        fout.close();
        fin.close();
    }
}

17.127 java 生成文件

--------------待整理:js分公司 附件上传

以流的形式传递到前端,执行下载,后端可有用重定向,这样可以 通过addMessage(redirectAttributes, "导出失败!"); 返回前端报错提示

https://blog.csdn.net/weixin_44176169/article/details/105320502open in new window (单文件,多文件上传 MultipartFile 、MultipartHttpServletRequest )

仿阿里云的写法,讲需要上传的附件,分为,“上传字符串”,“上传Byte数组”,“上传网络流”,"本地附件" https://help.aliyun.com/document_detail/84781.html?spm=a2c4g.11186623.6.961.5a7c16906I5hslopen in new window

// 填写网络流地址。
InputStream inputStream = new URL("https://www.aliyun.com/").openStream();

已流的形式 浏览器输出

指定盘符 生成本地文件

通过url 下载附件

public static String getRelativeFilePath(String fileName, String rootDirPath, String relativePath){
		//判断是否存在特殊字符 做空字符串替换
		Matcher matcher = pattern.matcher(fileName);
		//URLEncoder.encode(newFileName,"UTF-8");//针对中文乱码
		fileName= matcher.replaceAll(""); // 将匹配到的非法字符以空替换
		/**
		 * 判断附件上传,指定位置的文件夹是否存在
		 */
		File saveDirFile = new File(rootDirPath+File.separator+relativePath);//E:/CPMS/CPMS_FILES/CPMS_JSFGS/proj/20200426/lihuan
		if (!saveDirFile.exists()) {//不存在指定文件夹 先创建
			saveDirFile.mkdirs();
		}
		return relativePath+File.separator+fileName;// 文件的相对路径(用于返回结果)
	}


	/**
	 *  将url 资源 下载到本地,并返回 相对路径
	 *  调用者 在使用时需要 自己补上 rul前缀 Global.getConfig("towerViewImg")+filePath
	 * @param URL
	 * @param rootDirPath :文件上传根目录 E:/CPMS/CPMS_FILES/CPMS_JSFGS
	 * @param relativePath:文件所在 文件夹相对路径 /proj/20200426/lihuan
	 * @return
	 * @throws Exception
	 */
	public static String uploadFileByURL(String URL,String rootDirPath,String relativePath) throws Exception {
		//文件名
		String fileName=URL.substring(URL.lastIndexOf("/")+1);
		String relativeFilePath=getRelativeFilePath(fileName,rootDirPath,relativePath);// 获取相对路径,用于  返回给 前端

		java.net.URL url = new URL(URL);
		HttpURLConnection urlCon = (HttpURLConnection) url.openConnection();
		urlCon.setConnectTimeout(6000);
		urlCon.setReadTimeout(6000);
		int code = urlCon.getResponseCode();
		if (code != HttpURLConnection.HTTP_OK) {
			throw new Exception("文件读取失败");
		}

		//读文件流
		DataInputStream in = new DataInputStream(urlCon.getInputStream());
		DataOutputStream out = new DataOutputStream(new FileOutputStream(rootDirPath+File.separator+relativeFilePath));
		byte[] buffer = new byte[2048];
		int count = 0;
		while ((count = in.read(buffer)) > 0) {
			out.write(buffer, 0, count);
		}
		out.close();
		in.close();
		relativeFilePath = relativeFilePath.replaceAll("\\\\", "/");
		return "/" + relativeFilePath;
	}
  • 打包下载( 通过url 下载文件 案例是 通过 oss需要整理成 普通url)

    不生成 本地文件,直接以流的形式输出

public static void downForZipAsFlowByOSS(HttpServletRequest req, HttpServletResponse response,List<String> fileUrlList, String fileName ){
		// 创建临时文件
		File zipFile = null;
		try {
			//临时文件名称
			zipFile = File.createTempFile("downZipTemp", ".zip");
			FileOutputStream f = new FileOutputStream(zipFile);
			/**
			 * 作用是为任何OutputStream产生校验和
			 * 第一个参数是制定产生校验和的输出流,第二个参数是指定Checksum的类型 (Adler32(较快)和CRC32两种)
			 */
			CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
			// 用于将数据压缩成Zip文件格式
			ZipOutputStream zos = new ZipOutputStream(csum);
			AliOssClient aliOssClient= createAliOssClient();
			OSS ossClient = aliOssClient.getOssClient();
			String bucket_domain_url = SettingUtils.getCfgSettingByCode("BUCKET_DOMAIN_URL").getValue();
			for (String ossFile : fileUrlList) {
				// 路径前面 加 ‘/’ 会导致下载失败,所以需要 判断然后剔除掉多余的 /
				ossFile = getRelativeFilePthByOSS(ossFile);
				// 获取Object,返回结果为OSSObject对象
				OSSObject ossObject = ossClient.getObject(aliOssClient.getBucketName(),ossFile);//bucket_domain_url
				// 读去Object内容 返回
				InputStream inputStream = ossObject.getObjectContent();
				// 对于每一个要被存放到压缩包的文件,都必须调用ZipOutputStream对象的putNextEntry()方法,确保压缩包里面文件不同名
				String name=ossFile.substring(ossFile.lastIndexOf("/")+1);
				zos.putNextEntry(new ZipEntry(name));
				int bytesRead = 0;
				// 向压缩文件中输出数据
				while ((bytesRead = inputStream.read()) != -1) {
					zos.write(bytesRead);
				}
				inputStream.close();
				zos.closeEntry(); // 当前文件写完,定位为写入下一条项目
			}
			zos.close();
			String header = req.getHeader("User-Agent").toUpperCase();
			if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
				fileName = URLEncoder.encode(fileName, "utf-8");
				//IE下载文件名空格变+号问题
				fileName = fileName.replace("+", "%20");
			} else {
				fileName = new String(fileName.getBytes(), "ISO8859-1");
			}
			response.reset();
			response.setContentType("text/plain");
			response.setContentType("application/octet-stream; charset=utf-8");
			response.setHeader("Location", fileName);
			response.setHeader("Cache-Control", "max-age=0");
			response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
			FileInputStream fis = new FileInputStream(zipFile);
			BufferedInputStream buff = new BufferedInputStream(fis);
			BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
			byte[] car = new byte[1024];
			int l = 0;
			while (l < zipFile.length()) {
				int j = buff.read(car, 0, 1024);
				l += j;
				out.write(car, 0, j);
			}
			// 关闭流
			fis.close();
			buff.close();
			out.close();
			ossClient.shutdown();
			// 删除临时文件
			zipFile.delete();
		} catch (IOException e1) {
			e1.printStackTrace();
		}catch (Exception e) {
			e.printStackTrace();
		}

	}
  • 导出word

17.140 java 对数字的处理

场景:excel 导入时,会出现导入某些,可能会是文本或者数字,这样就会出现,如果是数字那么会被转成科学计数法

//验证是否为数字,如果是数字类型的防止数字被转为科学计数法类型
public static String switchNum(String str){
	Boolean strResult = str.matches("-?[0-9]+.?[0-9]*[Ee]?[+-]?\\d+");    
 	if(strResult == true) {//说明是数字
		return (new BigDecimal(str)).toString();//将科学计数法转为数字
 	}
 	return str;
}
public static void main(String[] args) {
// String num="9999999999.222222";
// String num2="10";
// String num3="9.999999999222221E9";
// String num4="xxxxxx";
// System.out.println(switchNum(num));
// System.out.println(switchNum(num2));
// System.out.println(switchNum(num3));
// System.out.println(switchNum(num4));
}
  • 加减乘除算法

DoubleUtilopen in new window

package cn.semdo.common.utils;
 
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
 
/**
 * 精确计算 double and float
 * Created by lxk on 2017/9/27
 */
public class DoubleUtil implements Serializable {
    private static final long serialVersionUID = -3345205828566485102L;
    // 默认除法运算精度
    private static final Integer DEF_DIV_SCALE = 2;
 
    /**
     * 提供精确的加法运算。
     *
     * @param value1 被加数
     * @param value2 加数
     * @return 两个参数的和
     */
    public static Double add(Double value1, Double value2) {
        BigDecimal b1 = new BigDecimal(Double.toString(value1));
        BigDecimal b2 = new BigDecimal(Double.toString(value2));
        return b1.add(b2).doubleValue();
    }
 
    /**
     * 提供精确的减法运算。
     *
     * @param value1 被减数
     * @param value2 减数
     * @return 两个参数的差
     */
    public static double sub(Double value1, Double value2) {
        BigDecimal b1 = new BigDecimal(Double.toString(value1));
        BigDecimal b2 = new BigDecimal(Double.toString(value2));
        return b1.subtract(b2).doubleValue();
    }
 
    /**
     * 提供精确的乘法运算。
     *
     * @param value1 被乘数
     * @param value2 乘数
     * @return 两个参数的积
     */
    public static Double mul(Double value1, Double value2) {
        BigDecimal b1 = new BigDecimal(Double.toString(value1));
        BigDecimal b2 = new BigDecimal(Double.toString(value2));
        return b1.multiply(b2).doubleValue();
    }
 
    /**
     * 提供(相对)精确的除法运算,当发生除不尽的情况时, 精确到小数点以后10位,以后的数字四舍五入。
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @return 两个参数的商
     */
    public static Double divide(Double dividend, Double divisor) {
        return divide(dividend, divisor, DEF_DIV_SCALE);
    }
 
    /**
     * 提供(相对)精确的除法运算。 当发生除不尽的情况时,由scale参数指定精度,以后的数字四舍五入。
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @param scale    表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static Double divide(Double dividend, Double divisor, Integer scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(dividend));
        BigDecimal b2 = new BigDecimal(Double.toString(divisor));
        return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
    }
 
    /**
     * 提供指定数值的(精确)小数位四舍五入处理。
     *
     * @param value 需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static double round(double value, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(Double.toString(value));
        BigDecimal one = new BigDecimal("1");
        return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
    }
	//================自定义:double类型数据转为Sting ,计算返回String类型================================
    /**
     * 提供精确的加法运算。
     *
     * @param value1 被加数
     * @param value2 加数
     * @return 两个参数的和
     */
    public static String addByStr(String value1, String value2) {
    	if(StringUtils.isEmpty(value1)){//如果传递的未null 则会报空指针异常
    		value1="0";
    	}
    	if(StringUtils.isEmpty(value2)){
    		value2="0";
    	}
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.add(b2).toString();
    }
    /**
     * 提供精确的乘法运算。
     *
     * @param value1 被乘数
     * @param value2 乘数
     * @return 两个参数的积
     */
    public static String mulByStr(String value1, String value2) {
    	if(StringUtils.isEmpty(value1)){//如果传递的未null 则会报空指针异常
    		value1="0";
    	}
    	if(StringUtils.isEmpty(value2)){
    		value2="0";
    	}
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.multiply(b2).toString();
    }
//=========================验证是否为数字,如果是数字类型的防止数字被转为科学计数法类型======================================
    //考虑 科学计数法的使用,将数据转为 String原样输出
  	public static String switchNum(String str){
  		//Boolean strResult = str.matches("-?[0-9]+.?[0-9]*[Ee]?[+-]?\\d+");
  		Boolean strResult = str.matches("[\\+\\-]?[\\d]+([\\.][\\d]*)?([Ee][+-]?[\\d]+)?"); 
  		 if(strResult == true) {//说明是数字
  			return (new BigDecimal(str)).toString();
  		 }
  		 return str;
  	}
   /* public static void main(String[] args) {
		String num="9999999999.222222";
		String num2="10";
		String num3="9.999999999222221E9";
		BigDecimal b2 = new BigDecimal(num3);
		System.out.println(b2.toString()+"//======================");
		
		
		System.out.println(Double.valueOf(num));
		System.out.println(DoubleUtil.addByStr(num,num2));
		System.out.println(DoubleUtil.mulByStr(num,num2));
		System.out.println("//========================================");
		System.out.println(DoubleUtil.addByStr(num3,num2));
		System.out.println(DoubleUtil.mulByStr(num3,num2));
		
	}*/
}

17.200 获取项目路径

获取项目本地决定路径到tomcat下的wabapp下对应的项目下:request.getServletContext().getRealPath("/")

17.210 转义和反转义

转义和反转义(在做文件导入的时候可能会用到,可能需要阻止其转义,例如 ± ,可能需要原样保存到数据库中 )

  • 在页面中用htmlEscape="false" 来转义展示
<form:input path="rwdName" htmlEscape="false" maxlength="100" />
  • StringEscapeUtils.unescapeHtml 或者HtmlUtils.htmlUnescape

导包: import org.apache.commons.lang3.StringEscapeUtils;

//导包: import org.apache.commons.lang.StringEscapeUtils;
public static void main(String[] args) {     
	  String s = "<pre class=\"brush: java;\">";  
	  System.out.println(StringEscapeUtils.escapeHtml(s));  =========&lt;pre class=&quot;brush: java;&quot;&gt;
	          
	   String u = "<pre class=\"brush: java;\">";  
	   System.out.println(StringEscapeUtils.unescapeHtml(u));  =======<pre class="brush: java;">	
} 
//StringEscapeUtils.unescapeHtml(u)方法写在实体类中的对应的set方法中 
 public void setName(String name) {
           this.name = StringEscapeUtils.unescapeHtml(name);
}  
  • HtmlUtils.htmlUnescape
import org.springframework.web.util.HtmlUtils;
HtmlUtils.htmlUnescape()  //将数据库中已经转义的还原。 例如:&plusmn; 转为需要的 ±

17.220 java 包含

  • List.contains();
  • Collections3.extractToList(list, "savePath");
public static void main(String[] args){
    List<String>list = new ArrayList<String>();
    list.add("草莓");  //向列表中添加数据
    list.add("香蕉");  //向列表中添加数据
    list.add("菠萝");  //向列表中添加数据
    for(int i=0;i<list.size();i++){  //通过循环输出列表中的内容
    System.out.println(i+":"+list.get(i));
  }
  String o = "苹果";
  System.out.println("list对象中是否包含元素"+o+":"+list.contains(o));  //判断字符串中是否包含指定字符串对象
}

17.230 linux 系统下部署 jdk

https://blog.csdn.net/qq_34021712/article/details/69816837open in new window

https://blog.csdn.net/sxdtzhaoxinguo/article/details/43731677open in new window

1.下载linux下jdk安装包jdk-7u67-linux-i586.tar.gz 2.安装步骤 第一步:创建文件夹

mkdir /usr/local/java

第二步:将下载jdk安装包,上传到该目录下 第三步:解压缩

cd /usr/local/java   #进入目录
tar -zxvf jdk-7u67-linux-i586.tar.gz #解压缩
rm -rf jdk-7u67-linux-i586.tar.gz  #解压缩之后删掉,节省空间
ls -l #查看一下

第四步:备份环境变量文件

cd /etc 
cp profile profile_bak  #如果出错误了,还可以恢复

第五步:复制路径到环境变量中 路径为:/usr/local/java/jdk1.7.0_67

编辑profile文件

# 进入编辑默认
vi /etc/profile
# 输入 i
i
# 保存或取消
esc退出  :wq 确认  q! 取消

在profile文件的末尾加入如下命令:

JAVA_HOME=/usr/local/java/jdk1.7.0_67
JRE_HOME=$JAVA_HOME/jre
PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
CLASSPATH=:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
export JAVA_HOME JRE_HOME PATH CLASSPATH

3.测试安装是否成功 先使用命令 source /etc/profile 更新一下 再使用

java -version

17.240 cmd 命令模式下 查询进程和杀死进程

netstat -aon|findstr 5037

tasklist|findstr 21096

taskkill /f /t /im adb.exe
image-20210322172145307
image-20210322172145307

17.250 java 连接数据库

springboot mysql连接 https://blog.csdn.net/weixin_33901995/article/details/113326874open in new window

spring 项目 mysql 连接超时问题

https://blog.csdn.net/u013378306/article/details/80035912open in new window

spring boot项目

注意 本文针对springboot 1.3.5版本,不同版本配置会有差别

注意: 此处单为是毫秒 ms

spring boot 如果不设置spring.datasource.type,默认是使用tomcat-jdbc连接池

  • 使用tomcat-jdbc在application.properties
#验证连接的有效性  此处已定要设为true,不然 time-between-eviction-runs-millis 不起作用
  
spring.datasource.test-while-idle=true  
  
#获取连接时候验证,如果无效,则生成新的连接使用,会影响性能  (此处设为true最为保险,但是为影响性能)
spring.datasource.test-on-borrow=true  
  
  
spring.datasource.validation-query=SELECT 1 FROM DUAL  
  
#空闲连接回收的时间间隔,与test-while-idle一起使用,设置5分钟  
spring.datasource.time-between-eviction-runs-millis=300000  
  
#连接池空闲连接的有效时间 ,设置30分钟  
spring.datasource.min-evictable-idle-time-millis=1800000  

具体参数配置参看https://blog.csdn.net/u013378306/article/details/78085366open in new window

  • 使用druid

注意: 此处单为是毫秒 ms

pom.xml

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.23</version>
        </dependency>

application.properties

#druid datasouce database settings begin
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/spring_boot?characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
 
# 下面为连接池的补充设置,应用到上面所有数据源中
# 初始化大小,最小,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
<!-- 空闲时检测,此处已定要设为true,不然 timeBetweenEvictionRunsMillis不起作用-->
spring.datasource.testWhileIdle=true
#获取连接时候验证,如果无效,则生成新的连接使用,会影响性能  (此处设为true最为保险,但是为影响性能)
spring.datasource.testOnBorrow=false
<!-- 返回给线程池时检测连接有效性 -->
spring.datasource.testOnReturn=false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
spring.datasource.useGlobalDataSourceStat=true
#druid datasouce database settings end

17.270 JAVA中json转Map,jsonArray转List集合,List集合转json

https://blog.csdn.net/weixin_33446857/article/details/79171122open in new window

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
 
 
import org.apache.commons.lang.StringUtils;
import org.zgr.pack.entity.test.TestJsonToList;
 
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
 
 
public class Util {
	    //json字符串转换为MAP
		public static Map jsonStrToMap(String s) {
			Map map = new HashMap();
			//注意这里JSONObject引入的是net.sf.json
			net.sf.json.JSONObject json = net.sf.json.JSONObject.fromObject(s);
			Iterator keys = json.keys();
			while (keys.hasNext()) {
				String key = (String) keys.next();
				String value = json.get(key).toString();
				if (value.startsWith("{") && value.endsWith("}")) {
					map.put(key, jsonStrToMap(value));
				} else {
					map.put(key, value);
				}
 
			}
			return map;
		}
		
		// 将jsonArray字符串转换成List集合
		public static List jsonToList(String json, Class beanClass) {
			if (!StringUtils.isBlank(json)) {
				//这里的JSONObject引入的是 com.alibaba.fastjson.JSONObject;
				return JSONObject.parseArray(json, beanClass);
			} else {
				return null;
			}
		}
		
		//List集合转换为json
		public static JSON listToJson(List list) {
			JSON json=(JSON) JSON.toJSON(list);
			return json;
		}
		
		
		public static void main(String[] args) {
			
			System.out.println("---------------------json字符串转换为MAP---------------------");
			JSONObject jsonObject=new JSONObject();
			jsonObject.put("abc", 123);
			jsonObject.put("def", 456);
			System.out.println("A==========json====="+jsonObject);
			Map map=Util.jsonStrToMap(jsonObject.toString());
			System.out.println("B==========def======"+map.get("def"));
			
			
			System.out.println("---------------------将jsonArray字符串转换成List集合---------------------");
			String str="[{\"year\":\"2015\",\"month\":10,\"count\":47},{\"year\":2017,\"month\":12,\"count\":4}]";
			//这里需要指定泛型,我们建立一个实体类TestJsonToList
			List<TestJsonToList> list=Util.jsonToList(str, TestJsonToList.class);
			System.out.println("C==========取list第二个元素的year====="+list.get(1).getYear());
			
			
			System.out.println("---------------------将list集合转换成json---------------------");
			//这里的JSONObject引入的是 com.alibaba.fastjson.JSONObject;
			JSON json=Util.listToJson(list);
			System.out.println("D==========json====="+json);
		}
}

实体类

public class TestJsonToList {
	private String year;      //年
    private String month;     //月
    private String count;     //数据
	public String getMonth() {
		return month;
	}
	public void setMonth(String month) {
		this.month = month;
	}
	public String getYear() {
		return year;
	}
	public void setYear(String year) {
		this.year = year;
	}
	public String getCount() {
		return count;
	}
	public void setCount(String count) {
		this.count = count;
	}
	
    //构造方法
    public TestJsonToList(String year, String month, String count) {  
        this.year = year;  
        this.month = month;  
        this.count = count;  
    }  
    //默认构造方法
    public TestJsonToList() {  
      
    }  
}

json转List集合,和List集合转json时需要注意,使用的是阿里的fastJson.jar包,不要引错了,Maven项目对应引入:

 <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.8</version>
</dependency>

java 构造JSONObject 有序添加数据

com.alibaba.fastjson.JSONObject jsonObject= new com.alibaba.fastjson.JSONObject(new LinkedHashMap());//
jsonObject.put("title","");

17.290 maven jar 冲突

https://blog.csdn.net/loongshawn/article/details/50831890open in new window

项目jar包冲突,需要找到冲突的源在哪个依赖,然后过滤掉这个依赖即可。接下来进入本地的工程目录,执行以下命令,获取依赖树:

​ 项目根目录下执行 mvn dependency:tree

<!-- OSS Java SDK -->
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>2.2.0</version>        
    <exclusions> <!-- 过滤掉 aliyun-sdk-oss.jar中冲突 httpclient.jar -->
        <exclusion>
        	<groupId>org.apache.httpcomponents</groupId> 
        	<artifactId>httpclient</artifactId>                    
        </exclusion> 
    </exclusions> 
</dependency>   

19.300 阿里云OSS

https://blog.csdn.net/jklizxcqwe/article/details/103122163open in new window

待整理:阿里云OSS附件上传 https://blog.csdn.net/z1c5809145294zv/article/details/108344155open in new window

https://aliyun_portal_storage.oss.aliyuncs.com/oss_api/oss_javahtml/index.html

https://blog.csdn.net/u014559227/article/details/70888249open in new window

https://blog.csdn.net/linlin_0904/article/details/84583676open in new window

https://blog.csdn.net/fangside/article/details/113767360open in new window //简单完整案例

https://blog.csdn.net/weixin_39507514/article/details/90799073open in new window base64

https://blog.csdn.net/yuanyuan5201/article/details/109292046open in new window oss压缩下载

  • 需要注意的是。上传的文件名中不能有 + 号,否则无法访问 ;
  • 路径不能有反斜杠,正确的 形式 proj/202104/xx.txt ,OSS会自动创建 文件夹 proj和202104
	private static String BUCKETNAME="lishihuan";// Buket名称
	private static String ACCESSKEYID="LTAI5t75a7pjcLnuhY4pPVyK";//AccessKey ID
	private static String ACCESSKEYSECRET="NZIXJbkW7V2necehbaGVZHh9G0IrOj";//AccessKey Secret
	private static String ENDPOINT="https://oss-cn-shanghai.aliyuncs.com";// 域名
	private static String BUCKET_DOMAIN_URL="https://lishihuan.oss-cn-shanghai.aliyuncs.com";// EndPoint(地域节点)
//appFileHost=http://${appBucket}.${endpoint}

需要5个参数,endpoint,accessKeyId,accessKeySecret 三个用来连接OssClient客户端。bucketName 域名是用来上传数据 BUCKET_DOMAIN_URL

OSS client = new OSSClientBuilder().build(this.endpoint,this.accessKeyId,this.accessKeySecret);// 创建连接
/**
* bucketUrl 相对路径+文件名 ( proj/202104/xx.txt)
*/
client.putObject(this.bucketName, bucketUrl, new File(fileRootPath));// 上传 文件
client.shutdown();

//前端访问
String url = BUCKET_DOMAIN_URL+"/"+bucketUrl;

17.310 java 对集合的处理

整理 Collections3.java

2. 前端处理 后端方回的list集合

var listJson=${fns:toJson(list)};

Redis 作为缓存

https://blog.csdn.net/lydms/article/details/105224210open in new window

17.331 SpringBoot设置gzip压缩选项--提高数据传输速度

  • application.yml配置

server:
	compression:
    	enabled: true
    	mime-types : text/html,text/xml,text/plain,text/css,application/javascript
  • 在application.properties中添加配置信息:
#开启压缩功能
server.compression.enabled=true
#压缩数据类型
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain
#开启压缩最小数据字节数默认2048个字节
server.compression.min-response-size=2048

17.340 返回值 T

	public static <T> T getData(String key) {
		CacheData<T> data = CACHE_DATA.get(key);
		// 数据未过期则返回
		if (data != null && (data.getExpire() <= 0 || data.getSaveTime() >= System.currentTimeMillis())) {
			return data.getData();
		} else {// 数据过期则清除key
			clear(key);
		}
		return (T) data;
	}

17.450 java 使用Map 做临时缓存

https://blog.csdn.net/u012948161/article/details/103315227open in new window

https://www.cnblogs.com/dcrenl/p/14005001.html(整理:spring框架中@PostConstruct的实现原理)open in new window

@PostConstruct注解 ;项目 实例化Ben 开始执行,可以用于项目启动就搭配 全局缓存

案例:spring boot 项目,项目启动后缓存 地图数据缓存-搭配 异步 方法 执行不影响 主线程

  • 配置:[线程](#17.471 java 线程)

  • 配置定时器(每十分钟执行一次)

package cn.semdo.modules.job.controller;

import cn.semdo.modules.dataCenter.xjbBasGroup.dao.MapBasDao;
import cn.semdo.modules.dataCenter.xjbBasGroup.service.MapBasService;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;


/**
 * 用来更新 地图用到的 数据 缓存对象
 * 每10 分钟执行一次
  */
@Component
public class MapDataUpdateCacheJob {
	@Autowired
	private MapBasService mapBasService;
	Logger log = LoggerFactory.getLogger(MapDataUpdateCacheJob.class);


	// PostConstruct 注解 本意是项目启动后执行 将地图数据缓存完成,但是 影响 主线程
	@PostConstruct
	public void init() {
		log.info("==============开始执行地图数据缓存记录 任务开始&异步==============");
		try {
			mapBasService.mpDataUpdateCache();
		} catch (Exception e) {
			e.printStackTrace();
			log.info("==============地图数据缓存 执行异常:"+e.getMessage()+"==============");
		}
	}
	//@PreDestroy
	public void destroy() {
		//系统运行结束
	}
	@Scheduled(cron = "0 0/10 * * * ?")
	public void executor() {
		init();
	}
}

  • 业务数据
@Async
	public void mpDataUpdateCache() {
		long start = System.currentTimeMillis();
		// 缓存线路
		JSONObject lineData = createLineByType();
		MemoryCacheUtils.setData("lineData2", lineData, 0);
		System.out.println("线路缓存完成");

		//1. 缓存GT
		List<MapPointVo> towerData =mapBasDao.getTowerData(null);
		MemoryCacheUtils.setData("towerData2", towerData, 0);
		System.out.println("GT缓存完成");
		// 2.
		JSONObject skData = findSkData();
		MemoryCacheUtils.setData("skData", skData, 0);
		System.out.println("三跨缓存完成");
		//3. 密集通道
		Map<String, JSONArray> map = classifyMjtdData();
		MemoryCacheUtils.setData("mjtdMap", map, 0);
		System.out.println("密集通道缓存完成");
		
		System.out.println("线程" + Thread.currentThread().getName() + " 执行异步任务:" + DateUtils.getDateTime());
		long end = System.currentTimeMillis();
		System.out.println("任务全部完成,总耗时:" + (end - start) + "毫秒");
		log.info("==============地图数据缓存 执行完成==============");
	}

17.470

public Future<String> doTaskOne() throws Exception {
    System.out.println("开始做任务一");
    long start = System.currentTimeMillis();
    Thread.sleep(random.nextInt(10000));
    long end = System.currentTimeMillis();
    System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
    return new AsyncResult<>("任务一完成");
}

17.471 java 线程

https://blog.csdn.net/asd136912/article/details/87716215open in new window

package cn.semdo.config;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
@EnableAsync
public class AsyncTaskConfig implements AsyncConfigurer {

	// ThredPoolTaskExcutor的处理流程
	// 当池子大小小于corePoolSize,就新建线程,并处理请求
	// 当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去workQueue中取任务并处理
	// 当workQueue放不下任务时,就新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize,就用RejectedExecutionHandler来做拒绝处理
	// 当池子的线程数大于corePoolSize时,多余的线程会等待keepAliveTime长时间,如果无请求可处理就自行销毁

	@Override
	@Bean
	public Executor getAsyncExecutor() {
		ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();
		//设置核心线程数
		threadPool.setCorePoolSize(10);
		//设置最大线程数
		threadPool.setMaxPoolSize(100);
		//线程池所使用的缓冲队列
		threadPool.setQueueCapacity(10);
		//等待任务在关机时完成--表明等待所有线程执行完
		threadPool.setWaitForTasksToCompleteOnShutdown(true);
		// 等待时间 (默认为0,此时立即停止),并没等待xx秒后强制停止
		threadPool.setAwaitTerminationSeconds(60);
		//  线程名称前缀
		threadPool.setThreadNamePrefix("Derry-Async-");
		// 初始化线程
		threadPool.initialize();
		return threadPool;
	}

	@Override
	public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
		return null;
	}
}

17.470 java 利用map 分组

public JSONObject createGeoJSONForPointByType(List<MapPointVo> queryList){
		// 数据分组
		Map<String, List<MapPointVo>> groupMap= Maps.newHashMap();
		for (MapPointVo mapPointVo : queryList) {
			String key = mapPointVo.getSubType();
			//============containsKey是用来判断某个key是否存在
			if(!groupMap.containsKey(key)){ //=====不存在就新new一个map对象,并将此时的设备名称作为key存入。
				groupMap.put(key, Lists.newArrayList());
			}
			groupMap.get(key).add(mapPointVo);
		}

		JSONObject GeoJSONMap = new JSONObject();
		for (String key : groupMap.keySet()) {
			List<MapPointVo> groupArr = groupMap.get(key);
			GeoJSONMap.put(key,createGeoJSONForPoint(groupArr));
		}
		return GeoJSONMap;
}

java 合集工具类

Collections3

SpringBoot返回结果如果为null或空值不显示处理方法

https://blog.csdn.net/lihua5419/article/details/81223045open in new window

https://blog.csdn.net/weixin_43969830/article/details/103203986open in new window

https://www.cnblogs.com/qinxu/p/10208858.htmlopen in new window

第一种方法:在application.yml配置文件中

spring:
  jackson:
    default-property-inclusion: non_null

第二种:新建配置类:(去掉 null 或 "" 字段)

package com.zpark.tools;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

/**
 * @author cosmo
 * @Title: JacksonConfig
 * @ProjectName  
 * @Description:  
 * @date  
 */

@Configuration
public class JacksonConfig {
   
    @Bean
    @Primary
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        //通过该方法对mapper对象进行设置,所有序列化的对象都将按改规则进行系列化,属性为NULL 不序列化
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        return objectMapper;
    }    //Include.Include.ALWAYS 默认     //Include.NON_DEFAULT    属性为默认值不序列化    //nclude.NON_EMPTY       属性为 空("") 或者为 NULL 都不序列化    //Include.NON_NULL       属性为NULL 不序列化
}

替换非空:

public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
            @Override
            public void serialize(Object o, JsonGenerator jsonGenerator,SerializerProvider serializerProvider)throws IOException, JsonProcessingException {
                jsonGenerator.writeString("");
            }
        });
        return objectMapper;
    }

17.480 Springboot读取本地json文件

image-20210803094026124
image-20210803094026124
  • 导入依赖
<!--        json-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.49</version>
        </dependency>

<!--        io-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>

  • 读取
File jsonFile = ResourceUtils.getFile("classpath:static/publicfile/yjxmb.json");
//将json转化为String
String json = FileUtils.readFileToString(jsonFile);
JSONArray jsonArray = JSON.parseArray(json);//alibaba

14.490 对象赋值(内部类)

final String userId=planConfigUser.getUserId();
		planConfigUser.setUser(new User(){
			{
				setId(userId);
			}
		});

14.500 升级为1.7后, 静态文件包含中文请求不到

https://blog.csdn.net/dongyan3595/article/details/118577368shiroopen in new window

14.510 java 对JSON 处理

net.sf.json.JSONException: Object is null 如果返回的字段中含有 null, 则会报错, 通过nvl(xx,'') 也不行 需要讲jar 换为 com.alibaba.fastjson

1. FastJson 对json中的KEY值的大小写转换方法open in new window

/**
     * json大写转小写
     *
     * @return JSONObject
     */
    public static JSONObject transToLowerObject(String json) {
        JSONObject JSONObject2 = new JSONObject();
        JSONObject JSONObject1 = JSON.parseObject(json);
        for (String key : JSONObject1.keySet()){
            Object object = JSONObject1.get(key);
            if (object.getClass().toString().endsWith("JSONObject")) {
                JSONObject2.put(key.toLowerCase(), transToLowerObject(object.toString()));
            } else if (object.getClass().toString().endsWith("JSONArray")) {
                JSONObject2.put(key.toLowerCase(), transToArray(JSONObject1.getJSONArray(key).toString()));
            }else{
                JSONObject2.put(key.toLowerCase(), object);
            }
        }
        return JSONObject2;
    }

    /**
     * jsonArray转jsonArray
     *
     * @return JSONArray
     */
    public static JSONArray transToArray(String jsonArray) {
        JSONArray jSONArray2 = new JSONArray();
        JSONArray jSONArray1 = JSON.parseArray(jsonArray);
        for (int i = 0; i < jSONArray1.size(); i++) {
            Object jArray = jSONArray1.getJSONObject(i);
            if (jArray.getClass().toString().endsWith("JSONObject")) {
                jSONArray2.add(transToLowerObject( jArray.toString()));
            } else if (jArray.getClass().toString().endsWith("JSONArray")) {
                jSONArray2.add(transToArray(jArray.toString()));
            }
        }
        return jSONArray2;
    }

这是递归方法,JSONArray用下面的方法,JSONObject用上面的方法

还有这种方案

/**
     * json的Key值转化为小写
     * @param json
     * @return
     */
    public static String transformLowerCase(String json){
        String regex = "[\\\"' ]*[^:\\\"' ]*[\\\"' ]*:";// (\{|\,)[a-zA-Z0-9_]+:

        Pattern pattern = Pattern.compile(regex);
        StringBuffer sb = new StringBuffer();
        // 方法二:正则替换
        Matcher m = pattern.matcher(json);
        while (m.find()) {
            m.appendReplacement(sb, m.group().toLowerCase());
        }
        m.appendTail(sb);
        return sb.toString();
    }

14.550 异常记录

1. 保存数据到

15.560 Java中List集合去重的几种方式

// 准备数据
List<String> list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
list.add("c");
list.add("b");
list.add("a");

1. 利用HashSet去重

// 利用list中的元素创建HashSet集合,此时set中进行了去重操作
HashSet set = new HashSet(list);
// 清空list集合
list.clear();
// 将去重后的元素重新添加到list中
list.addAll(set);

2. 通过List的contains()方法去重( 该方法 在平时用的最多)

// 创建一个新的list集合,用于存储去重后的元素
List listTemp = new ArrayList();
// 遍历list集合
for (int i = 0; i < list.size(); i++) {
	// 判断listTemp集合中是否包含list中的元素
	if (!listTemp.contains(list.get(i))) {
		// 将未包含的元素添加进listTemp集合中
		listTemp.add(list.get(i));
	}
}

3. 循环List进行去重

// 从list中索引为0开始往后遍历
for (int i = 0; i < list.size() - 1; i++) {
	// 从list中索引为 list.size()-1 开始往前遍历
	for (int j = list.size() - 1; j > i; j--) {
		// 进行比较
		if (list.get(j).equals(list.get(i))) {
			// 去重
			list.remove(j);
		}
	}
}

按理 对 方法2

场景:消息推送,需要考虑当前人员是否已经推送过,

public void send(){
    List<SysEmp> receiveList=new ArrayList<>();
    List userLististTemp = new ArrayList();// 用来去重
    for (SysPushModulesContent s1 : positiveContents) {
        // 业务处理
        List<SysPushSendRef> refList = sysPushSendRefPackageForEmp(id,receiveList,userLististTemp);
    }
}

/**
	组装 消息推送 sysPushSendRef 记录
	userLististTemp:用来控制是否已经推送过
*/
public List<SysPushSendRef> sysPushSendRefPackageForEmp(String objId,List<SysEmp> empList,List userLististTemp){
		if(empList==null || empList.size()==0){//表示没有对指定角色分配用户
			return null;
		}
		List<SysPushSendRef> refList= Lists.newArrayList();//作为最后  存放接收人的 参数
		for (SysEmp sysEmp : empList) {//定义 消息发送  人员信息(消息发送表)——————指定接收人信息
			if(sysEmp!=null&& sysEmp.getPushId()!=null){
				if (!userLististTemp.contains(sysEmp.getId())) {// 用于判断是否推送过
					SysPushSendRef ref =new SysPushSendRef();
					ref.setPushId(sysEmp.getPushId());
					ref.setReceive(sysEmp.getId());// 接收人id
					// 业务处理
					refList.add(ref);
					userLististTemp.add(sysEmp.getId());
				}
			}
		}
		if(refList==null || refList.size()==0){//表示没有对指定角色分配用户
			return null;
		}
		return refList;
	}


tomcat 下载

官网 : https://tomcat.apache.org/download-80.cgi#8.5.73open in new window

教程:https://www.cnblogs.com/li150dan/p/12535067.htmlopen in new window

15.570 spring的 InitializingBean 的 afterPropertiesSet 来初始化

https://www.cnblogs.com/feiyun126/p/7685312.htmlopen in new window

15.580 java 对16 进制的处理

16进制转10进制

String string = "000002af";
System.out.print(Long.valueOf(string,16));//--> 687

对于温度的处理,一般是 16进制补码转

/**
 * 16进制补码转10进制数
 * @param str
 * @return
 */
public static int complemwnt(String str) {
		int result = 0;
		String binaryString = Integer.toBinaryString(Integer.valueOf(str, 16));
        while(binaryString.length() < 16){
        	binaryString = "0"+binaryString;
        }
		String binary = binaryString.substring(0,1);//取第一位判断正负
		if ("0".equals(binary)) {
			result = Integer.valueOf(binaryString,2);
		}else {
			String[] split = binaryString.split("");
			StringBuilder builder = new StringBuilder();
			for (String s : split) {
			  if("0".equals(s)){
			    builder.append("1");
			  }else {
			      builder.append("0");
			  }
			}
			// 调用Integer.valueOf(value, 2) 将二进制转为十进制.
			result = Integer.valueOf(builder.toString(),2);
			// 先取负数在减1
			result = (0 - result) - 1;
		}
		return result;
		
	}

计算工具:https://www.23bei.com/tool-56.htmlopen in new window

列如:0000ff9b代表-101

image-20211229095505990
image-20211229095505990

运行jar 包项目

项目打包jar

## cmd 到目录下
java -jar test2-0.0.1-SNAPSHOT.jar  
java -jar test2-0.0.1-SNAPSHOT.jar  --server.port=8082 
image-20220223112321982
image-20220223112321982

17.590 java 对时间的处理

格式化时间

	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")

java list 循环覆盖

import org.springframework.beans.BeanUtils;

BeanUtils.copyProperties("转换前的类", "转换后的类");

for (PushMessage pushMessage : list) {
    // 从缓存中获取数据,个人最新消息 和 个人未读消息个数
    String key = "mess:latestNews:"+receiveId+":"+pushMessage.getPid();
    boolean exist = redisService.exist(key);
    if(exist){
   		 //pushMessage = getPushMessage(key);// 这样覆盖,不会对list产生影响,也即覆盖没有生效
    	BeanUtils.copyProperties(getPushMessage(key),pushMessage);// 通过拷贝复制对象,覆盖原对象
}

按照时间倒叙排列,没有时间放最后

	private void sortPushMessList(List<PushMessage> list) {
		Collections.sort(list, new Comparator<PushMessage>() {
			@Override
			public int compare(PushMessage pushMessage1, PushMessage pushMessage2) {
				if(pushMessage1.getSendTime()==null&&pushMessage2.getSendTime()!=null){
					return 1;
				}
				if(pushMessage1.getSendTime()==null&&pushMessage2.getSendTime()==null){
					return -1;
				}
				if(pushMessage1.getSendTime()!=null&&pushMessage2.getSendTime()==null){
					return -1;
				}
				int flag = pushMessage2.getSendTime().compareTo(pushMessage1.getSendTime());
				return flag;
			}
		});
	}

判断Object对象为空或空字符串

	/**
	 * 判断Object对象为空或空字符串
	 * @param obj
	 * @return
	 */
	public static Boolean isObjectNotEmpty(Object obj) {
		String str = ObjectUtils.toString(obj, "");
		Boolean flag = StringUtils.isNotBlank(str);
		return flag;
	}

JSONObject对象与JSON转换

参考:https://blog.csdn.net/qq_32138419/article/details/123796365open in new window

com.alibaba.fastjsonopen in new window.JSONObject之对象与JSON转换方法

输出:{name=大道,age=2,sex=m} -->

Student stu = new Student("大道", "m", 2);
//Java对象转换成JSON字符串
String stuString = JSONObject.toJSONString(stu);//{"age":2,"name":"大道","sex":"m"}

附件上传

同下面的 附属表重复添加 删除用户删除附件,找到新增的执行批量添加

    /**
     * 保存附件
     *  1. 需要找到用户删除的记录,执行删除
     *  2. 拿到新增的附件,执行批量添加
     * @param wlWorkOpRec
     */
    @Override
    public void saveFilesByRec(WlWorkOpRec wlWorkOpRec) {
        SysUser currUser = utils.getSysUser();
        // 1. 找到用户删除的记录,在数据库中删除
        if(wlWorkOpRec.getId()!=null){ // 只有更新时才会有 历史上传的附件记录,从而需要去执行删除
            List<Long> existFileIdList = wlWorkOpRec.getWlWorkOpFilesList().stream()
                    .filter(wlWorkOpFiles -> wlWorkOpFiles.getId() != null)
                    .map(WlWorkOpFiles::getId)
                    .collect(Collectors.toList()); // 获取之前上传的记录,主要是用于删除用户已经删除的记录
            // 删除附件,如果existFileIdList支持为空(全部删除)
            wlWorkOpFilesMapper.deleteOpFilesByOPidAndFileids(wlWorkOpRec.getId(),existFileIdList);
        }
        // 2. 拿到新增的数据,进行批量添加
        List<WlWorkOpFiles> addFiles = Lists.newArrayList();
//                wlWorkOpRec.getWlWorkOpFilesList().stream()
//                .filter(wlWorkOpFiles -> wlWorkOpFiles.getId() == null)
//                .collect(Collectors.toList());
        AtomicInteger index = new AtomicInteger();
        wlWorkOpRec.getWlWorkOpFilesList().forEach(wlWorkOpFiles->{
            if(wlWorkOpFiles.getId()==null){
                completeFile(wlWorkOpFiles,currUser,wlWorkOpRec.getId(), index.getAndIncrement());
                addFiles.add(wlWorkOpFiles);
            }
        });
        if(addFiles!=null && addFiles.size()>0){
            wlWorkOpFilesMapper.batchInsert(addFiles);
        }
    }
    public void completeFile(WlWorkOpFiles wlWorkOpFiles,SysUser currUser,Long opId,int index){
        wlWorkOpFiles.setOpId(opId);
        wlWorkOpFiles.setUploaderId(currUser.getUserId());
        wlWorkOpFiles.setUploaderName(currUser.getNickName());
        wlWorkOpFiles.setUploadTime(DateUtils.getNowDate());
        wlWorkOpFiles.setOrderNum(index);
        wlWorkOpFiles.setFileType(FileUtils.getFileType(wlWorkOpFiles.getFilePath()));
    }

执行删除,通过<if test="fileIdList != null and fileIdList.size()" > 可以支持全部删除

    <delete id="deleteOpFilesByOPidAndFileids" >
        delete from wl_work_op_files
        where OP_ID = #{opId}
        <if test="fileIdList != null and fileIdList.size()" >
            and id not in
            <foreach item="fileId" collection="fileIdList" open="(" separator="," close=")">
                #{fileId}
            </foreach>
        </if>
    </delete>

附属表重复添加

场景:附件表因为每次保存都会重复添加记录,又不能删除后重新添加,所以做法是,先删除数据库中已经给用户删除的记录,然后剔除数据库中存在的,再执行批量保存

  • 思路:
  <!-- 1. 删除多余的附件-->
  <delete id="deleteNotExistFilePath" >
    delete from base_file
    where 业务主表id=#{业务主表id}
    and file_path not in
    <foreach item="fileItem" collection="fileList" open="(" separator="," close=")">
      #{fileItem.filePath}
    </foreach>
  </delete>
 2. 去掉数据库现有的记录(拿到新增的)
	循环 取没有id的记录-即为新增
 
 3. 执行批量保存 新增的
		// 1. 保存前 删除多余的附件
		dsQuestionImgMapper.deleteNotExistFilePath(dsLocId,relativePathList);

		// 2. 剔除,数据库存在的记录(主要是找到 新增的记录)
		relativePathList.removeAll(dsQuestionImgMapper.getExistFilePath(dsLocId,relativePathList));


		if (relativePathList == null || relativePathList.size() == 0) {
			return;
		}
		// 3. 组装数据,批量保存
		List<DsQuestionImg> dsQuestionFilesList = Lists.newArrayList();
		//..... 业务处理
		dsQuestionImgMapper.batchInsert(dsQuestionFilesList, dsLocationRecord);// 批量插入 隐患上报文件
    <!-- 找到已经存在的 -->
    <select id="getExistFilePath" resultType="string">
        select file_path from ds_question_img
        where ds_loc_id=#{dsLocId}
        and file_path in
        <foreach item="relativePath" collection="relativePathList" open="(" separator="," close=")">
            #{relativePath}
        </foreach>
    </select>
    <!--删除多余的附件-->
    <delete id="deleteNotExistFilePath" >
        delete from ds_question_img
        where ds_loc_id=#{dsLocId}
        and file_path not in
        <foreach item="relativePath" collection="relativePathList" open="(" separator="," close=")">
            #{relativePath}
        </foreach>
    </delete>
    
   <insert id="batchInsert">
        insert into ds_question_img(
        ds_loc_id, file_type, upload_time, uploader_id, uploader_name, file_name, file_path, remark)
        values
        <foreach collection="dsQuestionImgList" separator="," item="item">
            (
            #{dsLocationRecord.id},#{item.fileType},#{item.uploadTime},#{item.uploaderId},#{item.uploaderName},
            #{item.fileName},#{item.filePath},null
            )
        </foreach>
    </insert>

Java 8 中的流式操作

要获取 id 为空的记录并重新组成一个新的 List

List<ZjjcTestPic> zjjcTestPicList = zjjcTestJz.getTestPicList().stream().filter(pic -> pic.getId() == null)
    .collect(Collectors.toList());

这个方法叫做 Java 8 中的流式操作(Stream API)中的 filter() 方法。filter() 方法用于根据指定的条件过滤流中的元素,并返回满足条件的元素组成的新流。

在给定的示例代码中,我们使用了 filter() 方法来过滤 List<ZjjcTestPic> 数组中的元素,根据条件筛选出 id 为空的记录。

流式操作是 Java 8 引入的一种函数式编程风格的处理集合数据的方式。通过使用流式操作,您可以以更简洁、更可读的方式对集合进行操作,例如过滤、映射、排序等。filter() 方法是 Stream API 中最常用的一个方法之一,它接受一个 Predicate 函数式接口作为参数,并返回一个新的流,其中包含符合条件的元素。

除了 filter() 方法外,Stream API 还提供了许多其他常用的方法,例如 map()collect()forEach() 等,可以根据需求进行链式调用,以实现复杂的集合操作。

java8流式操作

身份证规则

身份证验证
身份证验证

javaBean 复制

现有的对象 sysUser 是有数据的,因为保存用到的对象是SysUserVO ,所以通过 BeanUtils.copyProperties 进行复制

SysUserVO sysuservo=new SysUserVO();
BeanUtils.copyProperties(sysUser,sysuservo); // sysUser ==> sysuservo

前端发送 json数据,后端不用实体对象接受

@PostMapping("/getLineDeatil")
public AjaxResult getLineDeatil(@RequestBody String queryParam){
    try {
        JSONObject jsonObject = JSONObject.parseObject(queryParam);
        return AjaxResult.success(mapDetailService.getLineDeatil(jsonObject));
    } catch (Exception e) {
        e.printStackTrace();
        return AjaxResult.error(e.getMessage());
    }
}


//dao层
/**
	 * 线路途经的YJ站(包含起点终点)
	 * @param jsonObject
	 * @return
	 */
List<Map<String, Object>> wayPointForTeam(JSONObject jsonObject);

案例

选择线路+' '+ GT

@Override
public List<Map<String, Object>> getNearBasTowerList(BasTower basTower) {

    // 专门用于模糊匹配 线路+' '+ GT    拆字
    if(basTower.getInputSearch()!=null && !"".equals(basTower.getInputSearch())){
        String[] inputSearchArr = basTower.getInputSearch().split(" ");
        if(inputSearchArr!=null &&  inputSearchArr.length>1){
            String[] arr = getInputSearch(inputSearchArr);// 防止 空格过多
            basTower.setInputSearch(arr[0]);
            basTower.setInputSearch2(arr[1]);
        }
    }
    return basTowerMapper.getNearBasTowerList(basTower);
}
// 考虑 basTower.getInputSearch().split(" ") 之后 肯能出现 很多空格,要从所有空格中剔除
public String[] getInputSearch(String[] toEmailArray){
    //1.将内部类的array变成util的array
    List<String> toEmailList = new ArrayList( Arrays.asList(toEmailArray));
    Iterator<String> iterator = toEmailList.iterator();
    while (iterator.hasNext()) {
        String str = iterator.next();
        if  ("".equals(str)) {
            iterator.remove();   //2.注意这个地方
        }
    }
    //toEmailArray = toEmailList.toArray(new String[0]);	//将list转为string[]
    return toEmailList.toArray(new String[toEmailList.size()]);
}
<if test="inputSearch != null  and inputSearch != '' and (inputSearch2 == null  or inputSearch2 == '')">
    and(bt.line_name like concat('%', #{inputSearch}, '%') or bt.TOWER_NAME like concat('%', #{inputSearch}, '%') )
</if>
<if test="inputSearch != null  and inputSearch != '' and inputSearch2 != null  and inputSearch2 != ''">
    and(bt.line_name like concat('%', #{inputSearch}, '%') and bt.TOWER_NAME like concat('%', #{inputSearch2}, '%') )
</if>

java 反射

    /**
     * 以类的属性的get方法方法形式获取值
     * @param o:实体对象
     * @param field: 字段名称
     */
    private Object getValue(Object o, String field) throws Exception
    {
        if (StringUtils.isNotEmpty(name))
        {
            Class<?> clazz = o.getClass();
            String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
            Method method = clazz.getMethod(methodName);
            o = method.invoke(o);
        }
        return o;
    }
// getValue(basFsFhss,"bqArea")
	private String getProperty(ZjjcMaterialInfo item, String fieldName) {
		try {
			Method method = item.getClass().getMethod("get" + StringUtils.capitalize(fieldName));
			return (String) method.invoke(item);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

excel 导入 对时间的校验,赋值

1. 想实现的效果 item.setBdCreateTime(getDate(item.getBdCreateTimeStr(),"便桥修建日期",dataDescribe,sdf)); // dataDescribe 校验结果,但是 基础类型的字段无法传递,导致 dataDescribe 无法记录 校验结果

调用: dataDescribe += ExcelImportVerification.validationDate(item.getBqCreateTimeStr(),"setBqCreateTime",item,"便桥修建日期" ,sdf);

	/**
	 * 对时间校验,并且实现赋值
	 * @param datestr: 日期字符串
	 * @param field:需要赋值的日期字段名称
	 * @param obj: 实体对象
	 * @param tips:提示
	 * @param sdf:格式化日期 SimpleDateFormat 对象
	 * @return : 返回 校验结果
	 */
	public static String validationDate(String datestr,String field,Object obj,String tips, SimpleDateFormat sdf){
		if (StringUtils.isNotBlank(datestr)) {
			try {
				Method method = obj.getClass().getMethod(field, Date.class);
				method.invoke(obj , sdf.parse(datestr));
			} catch (Exception e) {
				e.printStackTrace();
				return tips+ "格式不正确;";
			}
		}
		return "";
	}

字符串长度

// 1.通过String的 length() 方法
String str = "ABCDEFGhijklmn";
System.out.println(str.length());//空格也算一个字符,,这样实际是不准确的,但是可以和前端的限定保持一致

// 2. 通过String的  getBytes,不建议使用
str.getBytes().length

严格计算 长度,中文2个字符

参考:https://www.ngui.cc/el/969201.htmlopen in new window

	/**
	 * 获取字符串的长度,如果有中文,则每个中文字符计为2位
	 * @param value
	 * 指定的字符串
	 * @return 字符串的长度
	 */
	public static int length(String value) {
		int valueLength = 0;
		String chinese = "[\u0391-\uFFE5]";
		/* 获取字段值的长度,如果含中文字符,则每个中文字符长度为2,否则为1 */
		for (int i = 0; i < value.length(); i++) {
			/* 获取一个字符 */
			String temp = value.substring(i, i + 1);
			/* 判断是否为中文字符 */
			if (temp.matches(chinese)) {
				/* 中文字符长度为2 */
				valueLength += 2;
			} else {
				/* 其他字符长度为1 */
				valueLength += 1;
			}
		}
		return valueLength;
	}

导入导出对数字的校验

/**
	 * 小数位数限定
	 *
	 * @param str         : 数值
	 * @param format:指定格式 小数或者整数  "8,4"|| "8"
	 * @return
	 */
	public static boolean checkDigit(String str, String format) {
		String[] formatArr = format.split(",");
		if (formatArr.length == 1) { // 说明当前校验的是整数
			if (str.length() > Integer.valueOf(formatArr[0])) {
				return false;
			} else {
				return true;
			}
		}
		String patternStr = "^(\\d){1," + formatArr[0] + "}(\\.(\\d){0," + formatArr[1] + "})?$";
		final Pattern pattern = Pattern.compile(patternStr);
		Matcher match = pattern.matcher(str);
		return match.matches();
	}


	/**
	 * 验证 小数/整数 长度 类型字段的长度
	 * @param integerField :需要校验的数值
	 * @param format:指定格式  小数或者整数  "8,4"|| "8"
	 * @param tips:提示信息
	 */
	public static String validationNumLength(Object integerField, String format, String tips) {
		if (integerField != null) {
			if (!checkDigit(integerField.toString(), format)) {
				return tips + "过长;";
			}
		}
		return "";
	}


// 调用  validationNumLength(item.getBdWidth(),"8,4", "便道宽度")

使用JSONObject作为实体对象

	@RequestMapping(value = "findLineData2", method = RequestMethod.POST)
	public AjaxResult findLineData2(@RequestBody String queryParam){
		try {
			JSONObject jsonObject = JSONObject.parseObject(queryParam);
			return AjaxResult.success(mapBasService.findLineData2(jsonObject));
		} catch (Exception e) {
			e.printStackTrace();
			return AjaxResult.error(e.getMessage());
		}
	}
<select id="findQxYhData" resultType="cn.semdo.ydxjdbapi.domain.MapPointVo">
        select * from (
            SELECT yh.id,'' as name, 'yh' as type,
                ifnull(if(length(trim(YH_LOC_JD_GB))>0,YH_LOC_JD_GB,null),longitude_GB) lng,
                ifnull(if(length(trim(YH_LOC_WD_GB))>0,YH_LOC_WD_GB,null),latitude_GB) lat,
                CONCAT('yh',yh.YH_LEVEL_MANAGE) subType
            FROM yh_op_record yh,bas_tower t
            WHERE yh.S_TOWER_ID=t.id
            <if test="opTeamId!=null and opTeamId != ''">
                AND yh.op_team_id = #{opTeamId}
            </if>
            <if test="opOrgId!=null and opOrgId != ''">
                AND yh.op_org_id = #{opOrgId}
            </if>
            union all
            select qx.id,'' as name, 'qx' as type,  longitude_gb lng, latitude_gb lat,
              CONCAT('qx',QX_LEVEL) as subType
            FROM qx_op_record qx,bas_tower t
            WHERE qx.TOWER_ID=t.id
            <if test="opTeamId!=null and opTeamId != ''">
                AND qx.op_team_id = #{opTeamId}
            </if>
            <if test="opOrgId!=null and opOrgId != ''">
                AND qx.op_org_id = #{opOrgId}
            </if>
        )aa
        order by type,subType
    </select>

64工作流

工作流驳回,获取上一个节点的操作人

/**
**  获取上个节点的 操作人 
**   @param objId :业务表id
**   @param procInsId:流程id
**   @param  taskDefKey:流程标识(上个流程节点的流程标识)
**   @param defaultUserId:如果没有找到 指定的人,返回该字段,防止返回空数据*
**   @return
**/
public String getActMesByTaskDefKey(String objId,String procInsId,String taskDefKey,String defaultUserId){
//   List<Map<String, Object>> list = histoicFlowList(objId,procInsId);
//   Map<String, Object> map = Maps.newHashMap();
//   for (Map<String, Object> histoicFlow:list ) {
//    if(histoicFlow.get("taskDefKey").equals(taskDefKey)){
//      map.put(taskDefKey, histoicFlow);
//    }
//   }
   String userid=defaultUserId;
   List<HistoricActivityInstance> list = historyService
      .createHistoricActivityInstanceQuery()
      .processInstanceId(procInsId).activityId(taskDefKey)
      .orderByHistoricActivityInstanceStartTime().asc()
      .orderByHistoricActivityInstanceEndTime().asc().list();
   if(list!=null && list.size()>0 ){
     HistoricActivityInstance historicActivityInstance = list.get(list.size() - 1);
     if(historicActivityInstance!=null && !"".equals(historicActivityInstance)){
      return historicActivityInstance.getAssignee();
     }
   }
   return userid;
  }

待整理 获取连线名称

删除任务

	/**
	 * 工作流删除任务
	 *
	 * @param procInsId:流程实例id
	 * @param describe:说明
	 */
	@Override
	public void deleteProcessInstance(String procInsId, String describe) {
		// 删除流程实例
		runtimeService.deleteProcessInstance(procInsId, "删除");
		// 删除历史记录
		historyService.deleteHistoricProcessInstance(procInsId);
	}

java 校验密码

CheckStrength.java

66 接口

百度接口 通过百度坐标获取当前位置详情

//通过坐标获取位置详情
http://api.map.baidu.com/geocoder/v2/?ak=rCTTTEXWsRNW3AlNvDbO2Bc5&location=31.940524127201048,117.27147584517168&output=json
//获取城市行政规划边界坐标点(用法查看 arcgis笔记中)
https://api.map.baidu.com/getscript  	

天气

http://apis.baidu.com/heweather/weather/free
https://lbs.amap.com/api/webservice/guide/api/weatherinfo/

http://restapi.amap.com/v3/weather/weatherInfo

100 其他

100.1 PDM 备注列不显示

设置样式

右击表,然后 获取格式,在对需要修改的表上,右击选中 应用格式,从而实现样式的复用

image-20240814112737971
image-20240814112737971

字段显示设置

空白区域右击选择 显示首选项 然后进入选择table,table列有两个tab,一个是设置显示的字段,一个是设置字体格式,这步操作完后,就可以通过上面的复制/应用格式 功能

  • 目前显示五列
    • 第一列:显示中文Name
    • 第二列:显示英文Code
    • 第三列:显示Data Type(字段类型和长度 例如:bigint(20))
    • 第四列:Key Indicator/Index Indicator
    • 第五列:Null Status
image-20240820102739182
image-20240820102739182

设置字体大小,外键和主键颜色

同上步,进入 table/format 设置

  • 先设置默认字体大小和颜色:微软雅黑8号黑色
  • 设置主键:红色
  • 设置外键:蓝色
image-20240820102829973
image-20240820102829973
  • 设置通用字段
image-20240820103158849
image-20240820103158849
  • 主键
image-20240820103349882
image-20240820103349882
  • 外键
image-20240820103409258
image-20240820103409258

100.2 svn

.idea classes target *iml *.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo __pycache__ *.rej *~ #*# .#* .*.swp .DS_Store [Tt]humbs.db

MyEclipse 代码提交时,让svn忽略classpath、target、.project //https://blog.csdn.net/xiaojin21cen/article/details/83054820open in new window

Svn 设置文件忽略

100.3 idea 启动报错

Error running 'SdbdApplication':

Command line is too long. Shorten command line for SdbdApplication or also for Spring Boot default configuration.

image-20210429144413728
image-20210429144413728

100.4 端口

## 查询所有进程
netstat -ano
## 查询指定端口是否被占
netstat -ano | findstr "8215" 
# 或者
netstat -ano | findstr :9205

## 查看进程号 3736 是哪个程序 占用在
tasklist | findstr "3736"
## 结束该进程 
taskkill /f /t /im java.exe

 taskkill /F /PID 31356
## 杀死 指定进程号 (linux)
kill -9 端口号
C:\Users\lishihuan>netstat -ano | findstr "8081"
  TCP    0.0.0.0:8081           0.0.0.0:0              LISTENING       1296
  TCP    [::]:8081              [::]:0                 LISTENING       1296

C:\Users\lishihuan>tasklist | findstr "1296"
java.exe                      1296 Console                   17  1,724,040 K