Fork me on GitHub

莫斯密码

摩斯密码

概念

摩尔斯电码(英语:Morse code)是一种时通时断的信号代码,通过不同的排列顺序来表达不同的英文字母数字标点符号。是由美国发明家萨缪尔·摩尔斯及其助手艾尔菲德·维尔在1836年发明。

摩尔斯电码是一种早期的数字化通信形式,但是它不同于现代只使用0和1两种状态的二进制代码,它的代码包括五种:

  1. ·):1
  2. -):111
  3. 字符内部的停顿(在点和划之间):0
  4. 字符之间的停顿:000
  5. 单词之间的停顿:0000000

—— 引自维基百科

一般来说,任何一种能把书面字符用可变长度的信号表示的编码方式都可以称为摩尔斯电码。但现在这一术语只用来特指两种表示英语字母和符号的摩尔斯电码:美式摩尔斯电码被使用在有线电报通信系统;今天还在使用的国际摩尔斯电码则只使用点和划。

电报公司根据要发的信的长度收费。商业代码精心设计了五个字符组成一组的代码,做为一个单词发送。比如:

  • BYOXO(Are you trying to crawl out of it? 准备好接收了吗?);

  • LIOUY(Why do you not answer my question? 为什么不回复?);

  • AYYLU(Not clearly coded, repeat more clearly. 编码不清楚,请清楚重复)。

    这些五个字符的简语可以用摩尔斯电码单独发送。有些简语后来在现代的网络上常用,如CUL(See you later. 再见)。

现代国际摩尔斯电码

现代国际摩尔斯电码是由Friedrich Clemens Gerke在1848年发明的,用在德国汉堡库克斯港之间的电报通信。1865年之后在少量修改之后由国际电报大会在巴黎标准化,后来由国际电信联盟统一定名为国际摩尔斯电码。

谐音记忆法

A:啊呀~

B:瞎~逼逼啥

C:草泥马

D:大~哥哥

E:额

F:服了you~了

G:咕

H:哈哈哈哈

I:哎哎

J:静悄的~

K:看一看

L:乐开~花了

M:妈

N:牛~呀

O:呕呕~

P:怪怕

Q:求你了~

R:日尼~玛

S:嘶嘶嘶

T:他~

U:天天有~

V:急转弯呀~

W:胖娃

X:吓死我啦

Y:一起玩吧~

Z:走停停

二叉树表示

时间控制和表示方法

有两种“符号”用来表示字符:点(·)和划(-),或叫“滴”(dit)和“答”(dah)。点的长度决定了发报的速度,并且被当作发报时间参考。下面是时间控制的图示:

1
2
-- --- ·-· ··· ·    /    -·-· --- -·· ·
M O R S E (空格) C O D E

这里,“-”表示划,“·”表示点。这是上面消息的准确发报时间(=表示有信号,.代表无信号,每个为一个点的长度):

1
2
3
4
===.===...===.===.===...=.===.=...=.=.=...=.......===.=.===.=...===.===.===...===.=.=...=
^ ^ ^ ^ ^
| 划 点 | 单词间隔
点划间隔 字符间隔

划一般是三个点的长度;点划之间的间隔是一个点的长度;字符之间的间隔是三个点的长度;单词之间的间隔是七个点的长度

初学者往往被教导发送点划间隔短小、短而快的字符,并且在符号和单词之间夸大间隔时间。比较起来,这种方式更加容易学会。

熟悉摩尔斯码的人之间经常像这样说话或拼写(其中,“长音 / Dah”是发“awe”的音):

1
2
 --     ---       ·-·      ···    · /    -·-·        ---      -··   ·
DahDah DahDahDah DiDahDit DiDiDit Dit, DahDiDahDit DahDahDah DahDiDi Di.

特殊符号(统一符号)

这是一些有特殊意义的点划组合。它们由二个或多个字母的摩尔斯电码连成一个使用,这样可以省去正常时把它们做为两个字母发送所必须的中间间隔时间。

符号 代码 意义
AAAAA ·-·-·-·-·- 调用信号,表示“我有消息发送”。
AAA(.) ·-·-·- 表示“本句完,接下一句”。
HH ········ 表示“有错,从上一字重新开始”。
AR(+) ·-·-· 表示“消息结束”。
AS(&) ·-··· 等待。
TTTTT ----- 表示“我正在接收你的消息”。
K -·- 表示“我已准备好,请开始发送消息”。
T - 表示“字收到了”。
IMI(?) ··--·· 表示“请重复你的电码,我不是很明白”。
R ·-· 表示“消息已收到”。
SK ···-·- 表示终止(联系结束)。
BT(=) -···- 分隔符。
SOS ···---··· 求救信号。

常用缩写

缩写和统一符号不同,缩写保留着字符中间的间隔,它们并没有被连成一个使用。

缩写 全写 注释
AA All after 某字以后
AB All before 字以前
ARRL American Radio Relay League 美国无线电中继联盟
ABT About 大约
ADS Address 地址
AGN Again 再一次
ANT Antenna 天线
BN All between ……之间
BUG Semiautomatic key 半自动关键
C Yes 是,好
CBA Callbook address 呼号手册
CFM Confirm 确认
CLG Calling 调用
CQ Calling any station 调用任意台站
CUL See you later 再见
CUZ Because 因为
CW Continuous wave 连续波
CX Conditions 状况
CY Copy 抄收
DE From 来自
DX Distance (sometimes refers to long distance contact) 距离(有时指长程通联)
ES And (和;且)
FB Fine business (Analogous to “OK”) 类似于“确定”
FCC Federal Communications Commission (美国)联邦通信委员会
FER For 为了
FREQ Frequency 频率
GA Good afternoon or Go ahead (depending on context) 午安;请发报(依上下文而定)
GE Good evening 晚安
GM Good morning 早安
GND Ground (ground potential) 地面(地电位)
GUD Good
HI Laughter
HR Here 这里
HV Have
LID Lid 覆盖
MILS Milliamperes 毫安培
NIL Nothing 无收信,空白
NR Number 编号,第……
OB Old boy 老大哥
OC Old chap 老伙计
OM Old man (any male amateur radio operator is an OM) 前辈,老手(男性)(任何男性业余无线电操作员都是OM)
OO Official Observer 官方观察员
OP Operator 操作员
OT Old timer 老前辈
OTC Old timers club 老手俱乐部
OOTC Old old timers club 资深老手俱乐部
PSE Please
PWR Power 功率
QCWA Quarter Century Wireless Association 四分之一世界无线电协会
R Received,Roger or decimal point (depending on context) 收到;小数点(依上下文而定)
RCVR Receiver 接收机
RPT Repeat or report (depending on context) 重复;报告(依上下文而定)
RST Signal report format (Readability-Signal Strength-Tone) 信号报告格式(可读性信号强度音)
RTTY Radioteletype 无线电传
RX Receive 接收
SAE Self addressed envelope 回邮信(即已填写自己地址以便对方回信的信封)
SASE Self addressed, stamped envelope 带邮票的回邮信封
SED Said
SEZ Says
SIG Signal 信号
SIGS Signals 信号
SKED Schedule 行程表
SN Soon 很快;不久的将来
SRI Sorry 抱歉
STN Station 台站
TEMP Temperature 温度
TMW Tomorrow 明天
TNX Thanks 谢谢
TU Thank you 谢谢你
TX Transmit 发射
U You
UR Your or you’re (depending on context) 你的;你是(依上下文而定)
URS Yours 你的
VY Very 非常;很
WDS Words
WKD Worked 工作
WL Will or Well 将会;好(依上下文而定)
WUD Would 将会
WX Weather 天气
XMTR Transmitter 发射机
XYL Wife 妻子
YL Young lady (used of any female) 女报务员(称呼任何女性报务员)
73 Best regards 致敬
88 Love and kisses 吻别
99 go way 走开(非友善)

命名技巧

命名

命名目的都是为了让代码和工程师进行对话,增强代码的可读性,可维护性。优秀的代码往往能够见名知意。

技巧

  • 结合上下文简化名称
  • 做有意义的区分
  • 名副其实,见名知意

类接口命名-名词或形容词

方法命名-动词

布尔命名

检查命名

回调命名

集合相关命名

数据相关命名

异步命名

成对动词

单词 意义
get获取 set 设置
add 增加 remove 删除
create 创建 destory 移除
start 启动 stop 停止
open 打开 close 关闭
read 读取 write 写入
load 载入 save 保存
create 创建 destroy 销毁
begin 开始 end 结束
backup 备份 restore 恢复
import 导入 export 导出
split 分割 merge 合并
inject 注入 extract 提取
attach 附着 detach 脱离
bind 绑定 separate 分离
view 查看 browse 浏览
edit 编辑 modify 修改
select 选取 mark 标记
copy 复制 paste 粘贴
undo 撤销 redo 重做
insert 插入 delete 移除
add 加入 append 添加
clean 清理 clear 清除
index 索引 sort 排序
find 查找 search 搜索
increase 增加 decrease 减少
play 播放 pause 暂停
launch 启动 run 运行
compile 编译 execute 执行
debug 调试 trace 跟踪
observe 观察 listen 监听
build 构建 publish 发布
input 输入 output 输出
encode 编码 decode 解码
encrypt 加密 decrypt 解密
compress 压缩 decompress 解压缩
pack 打包 unpack 解包
parse 解析 emit 生成
connect 连接 disconnect 断开
send 发送 receive 接收
download 下载 upload 上传
refresh 刷新 synchronize 同步
update 更新 revert 复原
lock 锁定 unlock 解锁
check out 签出 check in 签入
submit 提交 commit 交付
push 推 pull 拉
expand 展开 collapse 折叠
begin 起始 end 结束
backup 备份 restore 恢复
import 导入 export 导出
split 分割 merge 合并
inject 注入 extract 提取
attach 附着 detach 脱离
bind 绑定 separate 分离
view 查看 browse 浏览
edit 编辑 modify 修改
select 选取 mark 标记
copy 复制 paste 粘贴
undo 撤销 redo 重做
insert 插入 delete 移除
add 加入 append 添加
clean 清理 clear 清除
index 索引 sort 排序
find 查找 search 搜索
increase 增加 decrease 减少
play 播放 pause 暂停
launch 启动 run 运行
compile 编译 execute 执行
debug 调试 trace 跟踪
observe 观察 listen 监听
build 构建 publish 发布
input 输入 output 输出
encode 编码 decode 解码
encrypt 加密 decrypt 解密
compress 压缩 decompress 解压缩
pack 打包 unpack 解包
parse 解析 emit 生成
connect 连接 disconnect 断开
send 发送 receive 接收
download 下载 upload 上传
refresh 刷新 synchronize 同步
update 更新 revert 复原
lock 锁定 unlock 解锁
check out 签出 check in 签入
submit 提交 commit 交付
push 推 pull 拉
expand 展开 collapse 折叠
begin 起始 end 结束
start 开始 finish 完成
enter 进入 exit 退出
abort 放弃 quit 离开
obsolete 废弃 depreciate 废旧
collect 收集 aggregate 聚集

参考

优雅整洁的 Java 代码命名技巧,风之极·净化

事故

事故

201个别数据未反写,客户认为很严重

描述

2022年1月10日星期一 ,客户反应个别合同折扣业务功能异常。正常情况下,收款合同单据,如果进行总体折扣,在审核通过后,会对合同关联的产值进行单独折扣。但是,有一条合同单据,在进行总体折扣,审核通过的情况下,未能对产值进行折扣。功能存在异常

初步排查原因

代码、日志正常,个别数据有问题。貌似是数据库问题导致。

参考

更新丢失问题排查

sql触发器使用

触发器

概念

触发器是具有名称的数据库对象,它封装并定义一组要在响应对表执行的插入、更新或删除操作时执行的操作。

触发器在数据库里以独立的对象存储,它与存储过程和函数不同的是,存储过程与函数需要用户显示调用才执行,而触发器是由一个事件来启动运行。即触发器是当某个事件发生时自动地隐式运行。并且,触发器不能接收参数。所以运行触发器就叫触发或点火(firing)。

触发器的这种特性可以协助应用在数据库端确保数据的完整性 , 日志记录 , 数据校验等操作。

分类

行级触发器和语句级触发器

行级触发器对于触发事件所影响的每一行触发一次。例如,如果将删除定义为特定表的触发事件,并且单个 DELETE 语句从该表中删除五行,那么触发器会触发五次,即,对每一行触发一次。

语句级触发器仅对每个语句触发一次。在使用以上示例的情况下,如果将删除定义为特定表的触发事件,并且单个 DELETE 语句从该表中删除五行,那么触发器会触发一次。不能对 BEFORE 触发器或 INSTEAD OF 触发器指定语句级触发器粒度。

触发器变量

NEW 和 OLD 是特殊变量,您可以将其与 PL/SQL 触发器配合使用,而不必显式地定义这些变量。

NEW 是一个伪记录名,它引用行级触发器中的插入和更新操作的新表行。它的用法是 :NEW.column,其中 column 是表中对其定义此触发器的列的名称。
在行级前触发器中使用时,:NEW.column 的初始内容是所要插入的新行中的列值或者要替换旧行的行中的列值。
在行级后触发器中使用时,新列值已存储在表中。
当删除操作激活触发器时,该触发器中使用的 :NEW.column 为 null。
在触发器代码块中,可以像使用任何其他变量一样使用 :NEW.column。如果在行级前触发器的代码块中将值赋予 :NEW.column,那么所插入或更新的行将使用所赋予的值。

OLD 是一个伪记录名,它引用行级触发器中的更新和删除操作的旧表行。它的用法是 :OLD.column,其中 column 是表中对其定义此触发器的列的名称。
在行级前触发器中使用时,:OLD.column 的初始内容是所要删除的行中的列值或者要被新行替换的旧行中的列值。
在行级后触发器中使用时,旧列值已不再存储在表中。
当插入操作激活触发器时,该触发器中使用的 :OLD.column 为 null。
在触发器代码块中,可以像使用任何其他变量一样使用 :OLD.column。如果在行级前触发器的代码块中将值赋予 :OLD.column,那么所赋予的值不会影响触发器的操作。

特性 INSERT UPDATE DELETE
OLD NULL 实际值 实际值
NEW 实际值 实际值 NULL

触发器事件谓词

只能在触发器中使用触发器事件谓词 UPDATING、DELETING 和 INSERTING 来识别已激活该触发器的事件。

阅读语法图跳过直观语法图
DELETING
INSERTING
UPDATING
DELETING
如果删除操作已激活触发器,那么为 True。否则,为 False。
INSERTING
如果插入操作已激活触发器,那么为 True。否则,为 False。
UPDATING
如果更新操作已激活触发器,那么为 True。否则,为 False。
在 WHEN 子句或 PL/SQL 语句中,可将这些谓词指定为单个搜索条件,也可将它们指定为复合搜索条件内的布尔因子。

ORACLE 提供三个参数INSERTING, UPDATING,DELETING 用于判断触发了哪些操作。mysql无,一个触发器的trigger_time 只能是一种,所以不需要。

创建触发器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- mysql 触发器语法
-- delimiter空格//, 表示声明结束符为//
DELIMITER //
CREATE
[DEFINER = { user | CURRENT_USER }]
TRIGGER trigger_name
trigger_time trigger_event
ON tbl_name FOR EACH ROW
trigger_body
-- 重新声明结束符为分号
DELIMITER;

trigger_time: { BEFORE | AFTER }
trigger_event: { INSERT | UPDATE | DELETE }
  • DELIMITER //:MySQL 默认分隔符是; 但在触发器中,我们使用 // 表示触发器的开始与结束。但是像上面的语法中 ‘触发器的要执行的功能’ 的完整内容是begin开始到end结尾, 其中begin和end中间的内容是完整的sql语句,会涉及到分号.
    因为默认结束符是分号,如果不修改结束符,那么mysql一遇到分号,它就要自动执行,触发器创建语句就会执行不完整,从而报错.
    所以像这样的语句, 就需要事先把delimiter换成其它符号
  • FOR EACH ROW:这句表示只要满足触发器触发条件,触发器都会被执行,也就是说带上这个参数后,触发器将监测每一行对关联表操作的代码,一旦符合条件,触发器就会被触发。
  • trigger_body子句 包含要触发执行的SQL语句。可以是任何合法的语句,包括复合语句(需要使用BEGIN … END结构),流控制语句(if、case、while、loop、for、repeat、leave、iterate),变量声明(declare)以及指派(set),异常处理声明,允许条件声明,但是这里的语句受的限制和函数的一样。

注意

  • 创建触发器可能需要有CREATE TRIGGER权限.
1
2
grant create trigger on `database_naem`.`table_name` to `user_name`@`ip_address`;
revoke create trigger on `database_naem`.`table_name` from `user_name`@`ip_address`;
  • 不能对一个表创建具有相同的触发事件和触发时间的多个触发器(例如mysql)
  • OLD与NEW
    在触发器的SQL语句中,可以关联表中的任何列,通过使用OLD和NEW列名来标识,如OLD.col_name、NEW.col_name。OLD.col_name关联现有的行的一列在被更新或删除前的值。NEW.col_name关联一个新行的插入或更新现有的行的一列的值。
    对于INSERT语句,只有NEW是合法的。否则会报错:ERROR 1363 (HY000): There is no OLD row in on INSERT trigger
    对于DELETE语句,只有OLD是合法的。否则会报错:ERROR 1363 (HY000): There is no NEW row in on DELETE trigger
    对于UPDATE语句,NEW和OLD可以同时使用。
1
2
3
4
5
6
7
8
9
10
-- oracle 触发器语法
CREATE [OR REPLACE] TRIGGER trigger_name
INSTEAD OF
{INSERT | DELETE | UPDATE [OF column [, column …]]}
[OR {INSERT | DELETE | UPDATE [OF column [, column …]]}...]
ON [schema.] view_name --只能定义在视图上
[REFERENCING {OLD [AS] old | NEW [AS] new| PARENT as parent}]
[FOR EACH ROW ] --因为INSTEAD OF触发器只能在行级上触发,所以没有必要指定
[WHEN condition]
PL/SQL_block | CALL procedure_name;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
-- -- oracle 触发器
create or replace trigger DO_AUDIT_LHTS_OUTPUT
after update
on T_CASIC_LHTS_OUTPUT
for each ROW
WHEN (NEW .sjfy IS NOT NULL)
BEGIN
if :NEW.sjfy != :OLD.sjfy then
INSERT INTO audit_log values(AUDIT_LOG_SEQ.NEXTVAL,:OLD.id,:OLD.sjfy,:NEW.sjfy,NULL,sysdate);
END if;
END;

create or replace trigger DEPARTMENTS_BIU
before insert or update on T_CASIC_LHTS_OUTPUT
for each row
begin
if inserting and :new.sjfy is null then
:new.sjfy := 0;
end if;
end;

-- -- 使用触发器实现默认自增主键
create sequence simple_employees_seq;

create table SIMPLE_EMPLOYEES (
empno number primary key,
name varchar2(50) not null,
job varchar2(50)
);

create or replace trigger SIMPLE_EMPLOYEES_BIU_TRIG
before insert or update on SIMPLE_EMPLOYEES
for each row
begin
if inserting and :new.empno is null then
:new.empno := simple_employees_seq.nextval;
end if;
end;

删除触发器

drop trigger trigger-name

查看触发器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
mysql> SHOW TRIGGERS  like '%ttlsa%';  触发器名称匹配%ttlsa%
*************************** 1. row ***************************
Trigger: ttlsa_users_ai
Event: INSERT
Table: ttlsa_users
Statement: insert into ttlsa_users3 (uid,userinfo) values(NEW.uid,json_object(NEW.uid, NEW.username, NEW.password))
Timing: AFTER
Created: NULL
sql_mode: NO_ENGINE_SUBSTITUTION
Definer: root@127.0.0.1
character_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: latin1_swedish_ci
*************************** 2. row ***************************
Trigger: ttlsa_users_au
Event: UPDATE
Table: ttlsa_users
Statement: update ttlsa_users3 set userinfo=json_object(NEW.uid, NEW.username, NEW.password) where uid=OLD.uid
Timing: AFTER
Created: NULL
sql_mode: NO_ENGINE_SUBSTITUTION
Definer: root@127.0.0.1
character_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: latin1_swedish_ci
2 rows in set (0.00 sec)
mysql> SHOW TRIGGERS; 列出所有
mysql> SHOW TRIGGERS from database_name; 列出数据库的触发器
mysql> SHOW CREATE TRIGGER trigger_name; 查看创建触发器
mysql> SELECT * FROM information_schema.triggers WHERE trigger_name= '触发器名称';
*************************** 1. row ***************************
Trigger: ttlsa_users_ai
sql_mode: NO_ENGINE_SUBSTITUTION
SQL Original Statement: CREATE DEFINER=`root`@`127.0.0.1` trigger ttlsa_users_ai after insert on ttlsa_users for each row insert into ttlsa_users3 (uid,userinfo) values(NEW.uid,json_object(NEW.uid, NEW.username, NEW.password))
character_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: latin1_swedish_ci
1 row in set (0.01 sec)

触发器执行顺序

触发器建立的一般是InnoDB数据库,上面要使用的表也是要事务支持的。

  1. 如果BEFORE触发器执行失败,SQL无法正确执行。
  2. SQL执行失败时,AFTER型触发器不会触发。
  3. AFTER类型的触发器执行失败,SQL会回滚。

触发器异常情况

(1)如果BEFORE触发程序失败,不执行相应行上的操作。

(2)仅当BEFORE触发程序(如果有的话)和行操作均已成功执行,才执行AFTER触发程序。
(3) 如果在BEFORE或AFTER触发程序的执行过程中出现错误,将导致调用触发程序的整个语句的失败。
(4)对于事务性表,如果触发程序失败(以及由此导致的整个语句的失败),该语句所执行的所有更改将回滚。对于非事务性表,不能执行这类回滚,因而,即使语句失败,失败之前所作的任何更改依然有效

触发器示例

以下示例显示行级别的前触发器,对于每个隶属于部门 30 的新职员,在将该职员的记录插入到 EMP 表之前,此触发器会计算该职员的佣金。它还记录异常表中任何增幅超出 50% 的薪水增长情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
CREATE TABLE emp (
name VARCHAR2(10),
deptno NUMBER,
sal NUMBER,
comm NUMBER
)
/

CREATE TABLE exception (
name VARCHAR2(10),
old_sal NUMBER,
new_sal NUMBER
)
/

CREATE OR REPLACE TRIGGER emp_comm_trig
BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW
BEGIN
IF (:NEW.deptno = 30 and INSERTING) THEN
:NEW.comm := :NEW.sal * .4;
END IF;

IF (UPDATING and (:NEW.sal - :OLD.sal) > :OLD.sal * .5) THEN
INSERT INTO exception VALUES (:NEW.name, :OLD.sal, :NEW.sal);
END IF;
END /
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
-- 创建基本表结构-mysql
create table customers(
customer_id BIGINT PRIMARY KEY,
customer_name VARCHAR(50),
level VARCHAR(50)
) ENGINE=INNODB;


Insert into customers (customer_id, customer_name, level )values('1','Jack Ma','BASIC');
Insert into customers (customer_id, customer_name, level )values('2','Robin Li','BASIC');
Insert into customers (customer_id, customer_name, level )values('3','Pony Ma','VIP');
Select * from customers;

CREATE TABLE customer_status
(
customer_id BIGINT PRIMARY KEY,
status_notes VARCHAR(50)
)
ENGINE = INNODB;
CREATE TABLE sales
(
sales_id BIGINT PRIMARY KEY,
customer_id BIGINT,
sales_amount DOUBLE
)
ENGINE = INNODB;
CREATE TABLE audit_log
(
log_id BIGINT PRIMARY KEY AUTO_INCREMENT,
sales_id BIGINT,
previous_amount DOUBLE,
new_amount DOUBLE,
updated_by VARCHAR(50),
updated_on DATETIME
)
ENGINE = INNODB;

-- 触发器1
DELIMITER //
CREATE TRIGGER validate_sales_amount
BEFORE INSERT
ON sales
FOR EACH ROW
IF NEW.sales_amount>10000 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = "你输入的销售总额超过 10000 元。";
END IF//
DELIMITER ;

Insert into sales(sales_id, customer_id, sales_amount) values('1','1','12000');
select * from sales t;
-- 触发器2
DELIMITER //
CREATE TRIGGER customer_status_records
AFTER INSERT
ON customers
FOR EACH ROW
Insert into customer_status(customer_id, status_notes) VALUES(NEW.customer_id, '账户创建成功')//
DELIMITER ;


select * from customer_status cs ;
Insert into customers (customer_id, customer_name, level )values('4','Xing Wang','VIP');

-- 触发器3
DELIMITER //
CREATE TRIGGER validate_customer_level
BEFORE UPDATE
ON customers
FOR EACH ROW
IF OLD.level='VIP' THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'VIP 级别客户不能降级为普通级别客户';
END IF //
DELIMITER ;

select * from customers;
Update customers set level='BASIC' where customer_id='3';
Update customers set level='VIP' where customer_id='2';

-- -- 触发器4
DELIMITER //
CREATE TRIGGER log_sales_updates
AFTER UPDATE
ON sales
FOR EACH ROW
Insert into audit_log(sales_id, previous_amount, new_amount, updated_by, updated_on) VALUES (NEW.sales_id,OLD.sales_amount, NEW.sales_amount,(SELECT USER()), NOW() )//
DELIMITER ;

Insert into sales(sales_id, customer_id, sales_amount) values('5', '2','8000');

select * from sales;
select * from audit_log al ;
Update sales set sales_amount='9000' where sales_id='5';

SHOW TRIGGERS \G;

问题

  • oracle触发器编译错误PLS-00103

错误:PLS-00103: Encountered the symbol “” when expecting one of the following:

begin case declare exit for goto if loop mod null pragma
raise return select update while with <an identifier>
<a double-quoted delimited-identifier> <a bind variable> <<
close current delete fetch lock insert open rollback
savepoint set sql execute commit forall merge pipe

行:5
文本:  select TABLE1_SE.nextval into: NEW.id from dual;

这个错误很容易解决,删除语句中的空格,使用tab键进行分隔。

  • 在一个dml文件里面,写了三个存储过程。都是用declare声明的,执行报错PLS-00103。

发现两个declare语句之间有个换行。编写存储过程时,它会自动填充空格,空格会有问题的,所以就空格去除,或者在一个sql结束位置加上结束符 ‘/’ 也可。问题解决

  • Oracle最无奈错误PLS-00103

在其他语言中甚至SQL通用语言中判断语句大多数使用的都是elseifelse if此类关键词,但Oracle真的很特别,他的判断语句竟是elsif,所以这个错误只是elsif写错:laughing:

参考

oracle代码库

示例

oracle基本使用

oracle 基本使用

函数

函数语法

create_function::=

Description of the illustration create_function.gif

(*invoker_rights_clause\ ::=*, *parallel_enable_clause\::=*)

invoker_rights_clause ::=

Description of the illustration invoker_rights_clause.gif

parallel_enable_clause::=

Description of the illustration parallel_enable_clause.gif

streaming_clause::=

Description of the illustration streaming_clause.gif

call_spec::=

Description of the illustration call_spec.gif

Java_declaration::=

Description of the illustration Java_declaration.gif

C_declaration::=

Description of the illustration C_declaration.gif

函数示例

1
2
3
4
5
6
7
8
9
10
11
CREATE FUNCTION get_bal(acc_no IN NUMBER) 
RETURN NUMBER
IS acc_bal NUMBER(11,2);
BEGIN
SELECT order_total
INTO acc_bal
FROM orders
WHERE customer_id = acc_no;
RETURN(acc_bal);
END;
/

函数中声明临时变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
CREATE OR REPLACE FUNCTION CASICZS.GETREPORTROOMNAME2(OUTPUTNO IN VARCHAR2, PAYORGNAME IN VARCHAR2,OUTPUTTYPE IN VARCHAR2) RETURN VARCHAR2 IS 

tempSub11 varchar2(1) := SUBSTR(OUTPUTNO, 1, 1);
tempSub111 varchar2(1) := SUBSTR(OUTPUTNO, 11, 1);
tempSub41 varchar2(1) := SUBSTR(OUTPUTNO, 4, 1);
tempSub12 varchar2(2) := SUBSTR(OUTPUTNO, 1, 2);
retval varchar2(64);
BEGIN

IF tempSub11='1' THEN
retval :='一室';
ELSIF tempSub11='2' THEN
IF tempSub111='2' THEN
retval :='17所联合试验室';
ELSIF tempSub111='3' THEN
retval :='窦店联合试验室';
ELSIF tempSub111='4' THEN
retval :='成都719联合试验室';
ELSE retval :='环境与可靠性事业部';
END IF;
ELSIF tempSub11='3' THEN
retval :='理化分析与无损检测事业部(理化)';
ELSIF tempSub11='7' THEN
retval :='理化分析与无损检测事业部(无损)';
ELSIF tempSub11='4' THEN
retval :='四室';
ELSIF tempSub11='5' THEN
IF tempSub111='2' THEN
retval :='17所联合试验室';
ELSIF tempSub111='3' THEN
retval :='窦店联合试验室';
ELSIF tempSub111='4' THEN
retval :='成都719联合试验室';
ELSE retval :='五室';
END IF;
ELSIF tempSub11='8' THEN
retval :='八室';
ELSIF tempSub11='9' THEN
retval :='九室';
ELSIF tempSub11='T' THEN
retval :='元器件检测长沙分中心';
ELSIF tempSub11='C' THEN
retval :='元器件检测长沙分中心';
IF SUBSTR(OUTPUTNO,4, 1)='1' THEN
retval :='元器件检测成都分中心';
ELSIF tempSub41='2' THEN
retval :='一室';
ELSIF tempSub41='3' THEN
retval :='八室';
ELSIF tempSub41='4' THEN
retval :='元器件检测成都分中心';
END IF;
END IF;
IF PAYORGNAME='北京机电工程总体设计部' THEN
IF OUTPUTTYPE='environment' THEN--环境类产值
IF tempSub12!='2W' THEN
retval :='四部联合试验室';
END IF;
ELSIF OUTPUTTYPE='component' THEN
retval :='四部联合试验室';
END IF;
END IF;
RETURN retval;
END GETREPORTROOMNAME2;

增加工作日天数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE OR REPLACE FUNCTION ADD_BUS_DAYS(P_DATE IN DATE
, P_ADD_NUM IN INTEGER) RETURN DATE AS
V_CNT NUMBER;
V_BUS_DAY DATE := TRUNC(P_DATE);
BEGIN
SELECT MAX(RNUM)
INTO V_CNT
FROM (SELECT ROWNUM RNUM
FROM ALL_OBJECTS)
WHERE ROWNUM <= P_ADD_NUM
AND TO_CHAR(V_BUS_DAY + RNUM, 'DY') NOT IN ('SAT', 'SUN')
AND NOT EXISTS
(SELECT 1
FROM HOLIDAYS
WHERE HOLIDAY = V_BUS_DAY + RNUM);
V_BUS_DAY := V_BUS_DAY + V_CNT;
RETURN V_BUS_DAY;
END ADD_BUS_DAYS;

视图

物化视图

小提示:要想创建 “物化视图,至少具有 ‘CREATE MATERIALIZED VIEW’ 权限”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 权限查询,非 DBA 用户,则使用 user_sys_privs 即可
SELECT * FROM dba_sys_privs t WHERE t.privilege LIKE '%MATERIALIZED%';
grant create materialized view to scott; -- 授权
revoke create materialized view from scott; -- 回收


drop materialized view 物化视图名;

1. 查询物化视图,非 DBA 用户,请查询 all_mviews 或 user_mviews
SELECT *
FROM dba_mviews t
WHERE t.owner = 'SCOTT'
AND t.mview_name = 'MVW_PERSON_INFO';
2. 查询一般视图
SELECT * FROM dba_views;

语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
create materialized view 物化视图名        -- 1. 创建物化视图
build [immediate | deferred] -- 2. 创建方式,默认 immediate
refresh [force | fast | complete | never] -- 3. 物化视图刷新方式,默认 force
on [commit | demand] -- 4. 刷新触发方式
start with 开始时间 -- 5. 设置开始时间
next 间隔时间 -- 6. 设置间隔时间
with [primary key | rowid] -- 7. 类型,默认 primary key
[enable | disable] query rewrite -- 8. 是否启用查询重写
as -- 9. 关键字
查询语句; -- 10. select 语句


1. "创建 build" 的方式
(1) 'immediate':立即生效,默认。
(2) 'deferred' : 延迟至第一次 refresh 时才生效
2. "刷新 refresh" 的方式
(1) force :默认。如果可以 '快速刷新' 就 '快速刷新',否则执行 '完全刷新'
(2) fast :'快速刷新'。只刷新 '增量' 部分(前提:创建 '物化日志')
(3) complete: '完全刷新'。刷新时更新全部数据,包括视图中已经生成的原有数据
(4) never : 从不刷新
3. "触发" (请注意,on demand 中,才需要设置 '开始时间' 和 '间隔时间') -- 冲突
(1) on commit:基表有 commit 动作时,刷新刷图("不能跨库执行"
(2) on demand:在需要时刷新
[1] 根据后面设定的 '开始时间''结束时间' 进行刷新
[2] 手动调用 dbms_mview 包中的过程进行刷新
4. 基于基表的 primary keyrowid 创建
(1) 如果是基于 rowid,则不能对基表执行 '分组函数''多表连接' 等需要把
多个 rowid 合成一行的操作(理由很简单:到底以哪个 rowid 为准呢?)
5. enable query rewrite 启用查询重写(请注意, '开始时间''间隔时间' 不支持)-- 冲突
(1) 不支持的理由也很简单。
所谓的 '重写',就是讲对基表的查询定位到物化视图上,
'开始时间''间隔时间' 会造成物化视图上部分数据延迟,所以,不能重写
(2) 参数: query_rewrite_enabled (可通过 v$parameter 视图查询)

示例

创建物化视图:每 3 分钟刷新一次
1
2
3
4
5
6
7
8
9
10
11
CREATE MATERIALIZED VIEW mvw_person_info 
BUILD IMMEDIATE
REFRESH FORCE
ON DEMAND
START WITH SYSDATE
NEXT SYSDATE + 3/1440
AS
SELECT pi.person_no,
pi.name,
pi.create_date
FROM person_info pi;
手动刷新物化视图
1
2
3
4
5
BEGIN
dbms_mview.refresh(list => '视图名',
method => 'fast', -- 增量刷新
refresh_after_errors => TRUE);
END;

技巧

联表更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- -- 方法1
UPDATE T2
SET T2.C =
(SELECT B FROM T1 WHERE T1.A = T2.A)
WHERE EXISTS (SELECT 1 FROM T1 WHERE T1.A = T2.A)
-- -- 方法2
update student A
set (A.name,a.dq) =
(select B.bname,b.bdq
from newstudent B
where B.Bid = A.id
and A.dq = 10
)
where exists (select 1
from newstudent B
where B.Bid = A.id
and A.dq = 10
);
-- -- 方法3
MERGE INTO T2
USING T1
ON (T2.A = T1.A)
WHEN MATCHED THEN
UPDATE SET T2.C = T1.B

字符串处理

拆分字符串

1
2
SUBSTR(FI.TARGETID,0,INSTR(FI.TARGETID,'$') -1) TABLE,
SUBSTR(FI.TARGETID,INSTR(FI.TARGETID,'$') + 1 ) BIZID

SUBSTR(string,start_position,[length]) 求子字符串,返回字符串

解释:string 元字符串

​ start_position 开始位置(从0开始)

​ length 可选项,子字符串的个数

INSTR(string,subString,position,ocurrence)查找字符串位置

解释:string:源字符串

subString:要查找的子字符串

position:查找的开始位置

ocurrence:源字符串中第几次出现的子字符串

参考

mysql基本使用

mysql

安装

测试

客户端登录测试是否可以连接

1
mysql -h 127.0.0.1 -u root -p

补充: 命令行神器-mycli

1
2
pip install mycli
mycli -u 用户名 -h 主机名 -p 密码 --database 数据库

使用

创建数据库、用户

1
2
3
4
5
create database nextcloud default charset utf8 collate utf8_general_ci;
grant all privileges on nextcloud.* to 'mass'@'%' identified by 'mass123';
flush privileges;

CREATE USER 'mass'@'%' IDENTIFIED BY 'mass123';

技巧

查看MySql库中所有表的大小和记录数

1
2
3
4
5
6
7
8
9
SELECT TABLE_NAME,
DATA_LENGTH,
INDEX_LENGTH,
(DATA_LENGTH + INDEX_LENGTH) AS LENGTH,
TABLE_ROWS,
CONCAT(ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024,3),'MB') AS total_size
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'activiti6ui'
ORDER BY TABLE_ROWS desc;

mysql执行.sql文件

1
source /path/to/s.sql 或者 ./path/to/s.sql

mysql联表更新

在MySQL中,可以在 UPDATE语句 中使用JOIN子句执行跨表更新。MySQL UPDATE JOIN的语法如下:

1
2
3
4
5
UPDATE T1, T2,
[INNER JOIN | LEFT JOIN] T1 ON T1.C1 = T2. C1
SET T1.C2 = T2.C2,
T2.C3 = expr
WHERE condition

更详细地看看MySQL UPDATE JOIN语法:

首先,在UPDATE子句之后,指定主表(T1)和希望主表连接表(T2)。

第二,指定一种要使用的连接,即INNER JOIN或LEFT JOIN和连接条件。JOIN子句必须出现在UPDATE子句之后。

第三,要为要更新的T1和/或T2表中的列分配新值。

第四,WHERE子句中的条件用于指定要更新的行。

示例

使用INNER JOIN子句的MySQL UPDATE JOIN示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
mysql> select * from employees; -- 更新之前的数据
+--------+---------------+-------------+--------+
| emp_id | emp_name | performance | salary |
+--------+---------------+-------------+--------+
| 1 | Mary Doe | 1 | 50000 |
| 2 | Cindy Minsu | 3 | 65000 |
| 3 | Sue Greenspan | 4 | 75000 |
| 4 | Grace Dell | 5 | 125000 |
| 5 | Nancy Johnson | 3 | 85000 |
| 6 | John Doe | 2 | 45000 |
| 7 | Lily Bush | 3 | 55000 |
+--------+---------------+-------------+--------+
7 rows in set

mysql> UPDATE employees
INNER JOIN
merits ON employees.performance = merits.performance
SET
salary = salary + salary * percentage; -- 执行连接更新
Query OK, 6 rows affected
Rows matched: 7 Changed: 6 Warnings: 0

mysql> select * from employees; -- 更新之后的数据
+--------+---------------+-------------+--------+
| emp_id | emp_name | performance | salary |
+--------+---------------+-------------+--------+
| 1 | Mary Doe | 1 | 50000 |
| 2 | Cindy Minsu | 3 | 66950 |
| 3 | Sue Greenspan | 4 | 78750 |
| 4 | Grace Dell | 5 | 135000 |
| 5 | Nancy Johnson | 3 | 87550 |
| 6 | John Doe | 2 | 45450 |
| 7 | Lily Bush | 3 | 56650 |
+--------+---------------+-------------+--------+
7 rows in set

因为省略了 UPDATE 语句中的 WHERE 子句,所以 employees表中的所有记录都被更新。如果需要 performance 等级大于1的员工才更新薪资,那么 sql 可以这样写:

1
2
3
4
5
6
UPDATE employees
INNER JOIN
merits ON employees.performance = merits.performance
SET
salary = salary + salary * percentage
WHERE employees.performance > 1;

使用LEFT JOIN的MySQL UPDATE JOIN示例

要计算新员工的工资,不能使用 UPDATE INNER JOIN 语句(为什么不能,可参考sql之left join、right join、inner join的区别),因为它们的绩效数据在 merits表中不可用。这就是为什么要使用 UPDATE LEFT JOIN 来实现了。
UPDATE LEFT JOIN 语句在另一个表中没有相应行时,就会更新表中的一行。
例如,可以使用以下语句将新雇员的工资增加1.5%:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
-- 新加了两名员工,没有绩效考核信息
mysql> SELECT * FROM employees;
+--------+---------------+-------------+--------+
| emp_id | emp_name | performance | salary |
+--------+---------------+-------------+--------+
| 1 | Mary Doe | 1 | 50000 |
| 2 | Cindy Minsu | 3 | 66950 |
| 3 | Sue Greenspan | 4 | 78750 |
| 4 | Grace Dell | 5 | 135000 |
| 5 | Nancy Johnson | 3 | 87550 |
| 6 | John Doe | 2 | 45450 |
| 7 | Lily Bush | 3 | 56650 |
| 8 | Jack William | NULL | 43000 |
| 9 | Ricky Bond | NULL | 52000 |
+--------+---------------+-------------+--------+
9 rows in set

mysql> UPDATE employees
LEFT JOIN
merits ON employees.performance = merits.performance
SET
salary = salary + salary * 0.015
WHERE
merits.percentage IS NULL;
Query OK, 2 rows affected
Rows matched: 2 Changed: 2 Warnings: 0

mysql> select * from employees;
+--------+---------------+-------------+--------+
| emp_id | emp_name | performance | salary |
+--------+---------------+-------------+--------+
| 1 | Mary Doe | 1 | 50000 |
| 2 | Cindy Minsu | 3 | 66950 |
| 3 | Sue Greenspan | 4 | 78750 |
| 4 | Grace Dell | 5 | 135000 |
| 5 | Nancy Johnson | 3 | 87550 |
| 6 | John Doe | 2 | 45450 |
| 7 | Lily Bush | 3 | 56650 |
| 8 | Jack William | NULL | 43645 |
| 9 | Ricky Bond | NULL | 52780 |
+--------+---------------+-------------+--------+
9 rows in set

附带建表语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
CREATE DATABASE IF NOT EXISTS empdb;

USE empdb;
-- create tables
CREATE TABLE merits (
performance INT(11) NOT NULL,
percentage FLOAT NOT NULL,
PRIMARY KEY (performance)
);

CREATE TABLE employees (
emp_id INT(11) NOT NULL AUTO_INCREMENT,
emp_name VARCHAR(255) NOT NULL,
performance INT(11) DEFAULT NULL,
salary FLOAT DEFAULT NULL,
PRIMARY KEY (emp_id),
CONSTRAINT fk_performance FOREIGN KEY (performance)
REFERENCES merits (performance)
);

-- insert data for merits table
INSERT INTO merits(performance,percentage)
VALUES(1,0),
(2,0.01),
(3,0.03),
(4,0.05),
(5,0.08);

-- insert data for employees table
INSERT INTO employees(emp_name,performance,salary)
VALUES('Mary Doe', 1, 50000),
('Cindy Minsu', 3, 65000),
('Sue Greenspan', 4, 75000),
('Grace Dell', 5, 125000),
('Nancy Johnson', 3, 85000),
('John Doe', 2, 45000),
('Lily Bush', 3, 55000);

-- --新增两名员工
INSERT INTO employees(emp_name,performance,salary)
VALUES('Jack William',NULL,43000),
('Ricky Bond',NULL,52000);

树形查询

使用自定义存储过程实现,通过父编码查询所有子节点数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
DELIMITER // 
CREATE FUNCTION `findChildren`(rootId VARCHAR(400)) RETURNS varchar(4000) CHARSET utf8mb4 DETERMINISTIC
BEGIN
DECLARE sTemp VARCHAR(4000);
DECLARE sTempChd VARCHAR(4000);
SET sTemp = '$';
SET sTempChd = rootId;
WHILE sTempChd is not null DO
SET sTemp = CONCAT(sTemp,',',sTempChd);
SELECT GROUP_CONCAT(id) INTO sTempChd FROM t_core_org
WHERE FIND_IN_SET(parentid,sTempChd)>0;
END WHILE;
RETURN sTemp;
END
//
DELIMITER ;

增加工作日天数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT t.CREATEDTIME,
DATE_ADD(t.CREATEDTIME, INTERVAL 16 DAY) as simple,
WEEKDAY(DATE_ADD(t.CREATEDTIME, INTERVAL 16 DAY)) as week,
DATE_ADD(
t.CREATEDTIME,
INTERVAL (16 +
IF(
(WEEK(t.CREATEDTIME) <> WEEK(DATE_ADD(t.CREATEDTIME, INTERVAL 16 DAY)))
OR (WEEKDAY(DATE_ADD(t.CREATEDTIME, INTERVAL 16 DAY)) IN (5, 6)),
2,
0) + 16 div 5 * 2)
DAY
) AS FinalDate
from t_core_org t;

函数

函数语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
CREATE
[DEFINER = user]
PROCEDURE [IF NOT EXISTS] sp_name ([proc_parameter[,...]])
[characteristic ...] routine_body

CREATE
[DEFINER = user]
FUNCTION [IF NOT EXISTS] sp_name ([func_parameter[,...]])
RETURNS type
[characteristic ...] routine_body

proc_parameter:
[ IN | OUT | INOUT ] param_name type

func_parameter:
param_name type

type:
Any valid MySQL data type

characteristic: {
COMMENT 'string'
| LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
}

routine_body:
Valid SQL routine statement

函数调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
mysql> delimiter //

mysql> CREATE PROCEDURE citycount (IN country CHAR(3), OUT cities INT)
BEGIN
SELECT COUNT(*) INTO cities FROM world.city
WHERE CountryCode = country;
END//
Query OK, 0 rows affected (0.01 sec)

mysql> delimiter ;

mysql> CALL citycount('JPN', @cities); -- cities in Japan
Query OK, 1 row affected (0.00 sec)

mysql> SELECT @cities;
+---------+
| @cities |
+---------+
| 248 |
+---------+
1 row in set (0.00 sec)

mysql> CALL citycount('FRA', @cities); -- cities in France
Query OK, 1 row affected (0.00 sec)

mysql> SELECT @cities;
+---------+
| @cities |
+---------+
| 40 |
+---------+
1 row in set (0.00 sec)


mysql> CREATE FUNCTION hello (s CHAR(20))
mysql> RETURNS CHAR(50) DETERMINISTIC
RETURN CONCAT('Hello, ',s,'!');
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT hello('world');
+----------------+
| hello('world') |
+----------------+
| Hello, world! |
+----------------+
1 row in set (0.00 sec)

函数示例

查找子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE DEFINER=`root`@`localhost` FUNCTION `empdb`.`findChildren`(rootId VARCHAR(100)) RETURNS varchar(4000) CHARSET utf8mb4
DETERMINISTIC
BEGIN
DECLARE sTemp VARCHAR(4000);
DECLARE sTempChd VARCHAR(4000);
SET sTemp = '$';
SET sTempChd = rootId;
WHILE sTempChd is not null DO
SET sTemp = CONCAT(sTemp,',',sTempChd);
SELECT GROUP_CONCAT(id) INTO sTempChd FROM t_core_org
WHERE FIND_IN_SET(parentid,sTempChd)>0;
END WHILE;
RETURN sTemp;
END;

获取当前系统时间

1
2
3
select now() as Systime;
select current_date as Systime;
select sysdate() as Systime;

问题

  • ERROR 1045 (28000): Access denied for user ‘root’@’localhost’ (using password: NO/YES)

修改my.in/my.cnf配置文件

    进入mysql安装目录

    编辑my.ini

    在[mysqld]下添加skip-grant-tables,保存即可。

  使用管理员身份打开命令行

  ①重启mysql:

    systemctl restart mysqld

  ②进入mysql,登录
    mysql -u root -p
    不用输入密码,直接回车(出现Enter Password 也一样直接回车,即可登陆成功)

  ③输入use mysql,修改root的密码:
    update user set authentication_string=password(‘新密码’) where user=’root’;
    flush privileges;

  ④退出:

    quit;

  ⑤再次重启mysql:

    systemctl restart mysqld

  ⑥测试是否成功就是是否登陆成功咯。
    mysql -u root -p

    Enter Password>’新密码’

  • MySQL8服务无法启动,服务没有报告任何错误

免安装版mysql-8.0.15-winx64.zip

按照教程来安装,解压,增加my.ini文件,修改文件内部的地址信息,配置环境变量path,新建data文件夹(注意这里,就是这个位置出错了),以管理员身份运行cmd初始化,mysqld -install,net start mysql,启动mysql。

结果报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
D:\develop\mysql\mysql-8.0.15-winx64\bin>net start mysql
mysql 服务正在启动 .
mysql 服务无法启动。

服务没有报告任何错误。

请键入 NET HELPMSG 3534 以获得更多的帮助。

# 删除 data文件夹 ,重新打开管理员身份的cmd
D:\develop\mysql\mysql-8.0.15-winx64\bin>mysqld --initialize --user=mysql --console
2022-01-05T11:53:49.564904Z 0 [System] [MY-013169] [Server] D:\develop\mysql\mysql-8.0.15-winx64\bin\mysqld.exe (mysqld 8.0.15) initializing of server in progress as process 29036
2022-01-05T11:53:52.732704Z 5 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: =7_8DGtv&gaJ
2022-01-05T11:53:53.906223Z 0 [System] [MY-013170] [Server] D:\develop\mysql\mysql-8.0.15-winx64\bin\mysqld.exe (mysqld 8.0.15) initializing of server has completed

D:\develop\mysql\mysql-8.0.15-winx64\bin>mysqld -install
The service already exists!
The current server installed: D:\develop\mysql\mysql-8.0.15-winx64\bin\mysqld.exe --defaults-file=../my.ini mysql

net start mysql

# 配置文件使用全路径!!!
D:\develop\mysql\mysql-8.0.15-winx64\bin>mysqld --remove
Service successfully removed.

D:\develop\mysql\mysql-8.0.15-winx64\bin>mysqld.exe --install mysql --defaults-file="D:\develop\mysql\mysql-8.0.15-winx64\my.ini"
Service successfully installed.
D:\develop\mysql\mysql-8.0.15-winx64\bin>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。


D:\develop\mysql\mysql-8.0.15-winx64\bin>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。
  • SQL 错误 [1064] [42000]: 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 ‘’ at line 3
    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 ‘’ at line 3
    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 ‘’ at line 3

琢磨了半天,原来Mysql解释器一遇到;号时就结束,回车以后就执行了。但是现在并不希望Mysql这么做,因为存储过程中可能 包含很多分号的语句,所以怎么办了,很简单Mysql给我们提供了delimiter关键字,delimiter作用就是把;分号替换成指定的符号,比如//或$$。当再出现//或$$时,Mysql解释器才会执行命令。

  • 单引号与反引号区别

在上述的sql语句中,列名称使用的是单引号而不是反引号,所以会就报了这个错误出来。修改后

再次运行就不会报错了,但是有一点需要注意,后面列的注释不能用反引号,因为这只是一个普通字符串,不是MySQL的关键字。

添加delimiter

  • 语法错误

列名称使用的是单引号而不是反引号,所以会就报了这个错误出来。

参考

mysql8 官方文档

数据分析-埋点

埋点

概念

所谓埋点是数据领域的专业术语,也是互联网应用里的一个俗称。它的学名应该叫做事件追踪,对应的英文是Event Tracking。

针对特定用户行为或事件进行捕获、处理和发送的相关技术及其实施过程。

目的

埋点是为了满足快捷、高效、丰富的数据应用而做的用户行为过程及结果记录。数据埋点是一种常用的数据采集的方法。埋点是数据的来源,采集的数据可以分析网站/APP的使用情况,用户行为习惯等,是建立用户画像、用户行为路径等数据产品的基础。

对于产品来说,用户在你的产品里做了什么、停留了多久、有什么异样,都是可以通过数据埋点来实现监控的。

  1. 提高渠道转化:通过用户的操作序列,找到用户流失的节点
  2. 改善产品:通过用户行为分析产品是否有问题,例如用户有没有因为设计按钮过多导致用户行为无效等问题,以此发现功能设计缺陷等。
  3. 精准客户运营:对客户进行分组(例如有的喜欢打折购买,有的喜欢直接购买等),实现精准营销,发放优惠券等
  4. 完善客户画像:基本属性(性别、年龄、地区等),行为属性(设备操作习惯等)
  5. 数据分析:埋点作为原料放在数据仓库中。提供渠道转化、个性推荐等

分类

前后端埋点

参考

埋点

java8 stream基本使用

背景

概念

使用

技巧

java8 小技巧保证分组groupingBy后排序不变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// 一个参数
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList());
}
// 两个参数
public static <T, K, A, D>
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream) {
return groupingBy(classifier, HashMap::new, downstream);
}
// 三个参数
public static <T, K, D, A, M extends Map<K, D>>
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream) {
Supplier<A> downstreamSupplier = downstream.supplier();
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {
K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");
A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());
downstreamAccumulator.accept(container, t);
};
BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner());
@SuppressWarnings("unchecked")
Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory;

if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID);
}
else {
@SuppressWarnings("unchecked")
Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();
Function<Map<K, A>, M> finisher = intermediate -> {
intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
@SuppressWarnings("unchecked")
M castResult = (M) intermediate;
return castResult;
};
return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);
}
}

//创建数据
private static List<Person> getPersionList() {
List<Person> persons = new ArrayList<>();
for (int i = 1; i <= 40; i++) {
Random r = new Random();
Person person = new Person();
person.setName("abel-" + i);
person.setSex((int) (Math.random() * 2));
person.setGroup(String.valueOf(i%2));
person.setAge(25 + r.nextInt(50));
persons.add(person);
}
return persons;

}


/**
* 有序分组
*/
private static void groupByTest() {
List<Person> persons = getPersionList();
//将list 排序,并按照排序后的结果进行有序分组
LinkedHashMap<Integer, List<Person>> ageMap = personsSort.stream().sorted(Comparator.comparingInt(Person::getAge)).collect(Collectors.groupingBy(Person::getAge, LinkedHashMap::new, Collectors.toList()));
}

参考

随园食单

随园食单

须知单

学问之道,先知而后行,饮食亦然。作《须知单》。

先天须知

凡物各有先天,如人各有资禀。人性下愚,虽孔、孟教之,无益也;物性不良,虽易牙烹之,亦无味也。指其大略:猪宜皮薄,不可腥臊;鸡宜骟嫩,不可老稚;鲫鱼以扁身白肚为佳,乌背者,必崛强[插图]于盘中;鳗鱼以湖溪游泳为贵,江生者,必槎丫[插图]其骨节;谷喂之鸭,其膘肥而白色;壅土之笋,其节少而甘鲜。同一火腿也,而好丑判若天渊;同一台鲞也,而美恶分为冰炭。其他杂物,可以类推。大抵一席佳肴,司厨之功居其六,买办之功居其四。

作料须知

厨者之作料,如妇人之衣服首饰也。虽有天姿,虽善涂抹,而敝衣蓝缕,西子亦难以为容。善烹调者,酱用伏酱,先尝甘否;油用香油,须审生熟;酒用酒娘[插图],应去糟粕;醋用米醋,须求清冽。且酱有清浓之分,油有荤素之别,酒有酸甜之异,醋有陈新之殊,不可丝毫错误。其他葱、椒、姜、桂、糖、盐,虽用之不多,而俱宜选择上品。苏州店卖秋油,有上、中、下三等。镇江醋颜色虽佳,味不甚酸,失醋之本旨矣。以板浦醋为第一,浦口醋次之。

调剂须知

调剂之法,相物而施。有酒、水兼用者,有专用酒不用水者,有专用水不用酒者;有盐、酱并用者,有专用清酱不用盐者,有用盐不用酱者;有物太腻,要用油先炙者;有气太腥,要用醋先喷者;有取鲜必用冰糖者;有以干燥为贵者,使其味入于内,煎炒之物是也;有以汤多为贵者,使其味溢于外,清浮之物是也。

搭配须知

谚曰:“相女配夫。”《记》曰:“拟人必于其伦。”烹调之法,何以异焉?凡一物烹成,必需辅佐。要使清者配清,浓者配浓,柔者配柔,刚者配刚,方有和合之妙。其中可荤可素者,蘑菇、鲜笋、冬瓜是也;可荤不可素者,葱、韭、茴香、新蒜是也;可素不可荤者,芹菜、百合、刀豆是也。常见人置蟹粉于燕窝之中,放百合于鸡、猪之肉,毋乃唐尧与苏峻对坐,不太悖乎?亦有交互见功者,炒荤菜用素油,炒素菜用荤油是也。

独用须知

味太浓重者,只宜独用,不可搭配。如李赞皇、张江陵一流,须专用之,方尽其才。食物中,鳗也,鳖也,蟹也,鲥鱼也,牛羊也,皆宜独食,不可加搭配。何也?此数物者,味甚厚,力量甚大,而流弊亦甚多,用五味调和,全力治之,方能取其长而去其弊。何暇舍其本题,别生枝节哉?金陵人好以海参配甲鱼,鱼翅配蟹粉,我见辄攒眉。觉甲鱼、蟹粉之味,海参、鱼翅分之而不足;海参、鱼翅之弊,甲鱼、蟹粉染之而有余。

  • © 2015-2024 DXShelley
  • Powered by Hexo Theme Ayer

请我喝杯咖啡吧~~~

支付宝
微信