diff --git a/advisor/heuristic.go b/advisor/heuristic.go index f3dc88ff206138dce724aacb5e27118a21a15050..841441dcc86b9f2582441dd6ce9225d69934d3ab 100644 --- a/advisor/heuristic.go +++ b/advisor/heuristic.go @@ -1768,6 +1768,81 @@ func (q *Query4Audit) RuleSumNPE() Rule { return rule } +// RuleForbiddenTrigger FUN.007 +func (q *Query4Audit) RuleForbiddenTrigger() Rule { + var rule = q.RuleOK() + + // 由于vitess对某些语法的支持不完善,使得如创建临时表等语句无法通过语法检查 + // 所以这里使用正则对触发器、临时表、存储过程等进行匹配 + // 但是目前支持的也不是非常全面,有待完善匹配规则 + // TODO TiDB 目前还不支持触发器、存储过程、自定义函数、外键 + + forbidden := []*regexp.Regexp{ + regexp.MustCompile(`(?i)CREATE\s+TRIGGER\s+`), + } + + for _, reg := range forbidden { + if reg.MatchString(q.Query) { + rule = HeuristicRules["FUN.007"] + if position := reg.FindIndex([]byte(q.Query)); len(position) > 0 { + rule.Position = position[0] + } + break + } + } + return rule +} + +// RuleForbiddenProcedure FUN.008 +func (q *Query4Audit) RuleForbiddenProcedure() Rule { + var rule = q.RuleOK() + + // 由于vitess对某些语法的支持不完善,使得如创建临时表等语句无法通过语法检查 + // 所以这里使用正则对触发器、临时表、存储过程等进行匹配 + // 但是目前支持的也不是非常全面,有待完善匹配规则 + // TODO TiDB 目前还不支持触发器、存储过程、自定义函数、外键 + + forbidden := []*regexp.Regexp{ + regexp.MustCompile(`(?i)CREATE\s+PROCEDURE\s+`), + } + + for _, reg := range forbidden { + if reg.MatchString(q.Query) { + rule = HeuristicRules["FUN.008"] + if position := reg.FindIndex([]byte(q.Query)); len(position) > 0 { + rule.Position = position[0] + } + break + } + } + return rule +} + +// RuleForbiddenFunction FUN.009 +func (q *Query4Audit) RuleForbiddenFunction() Rule { + var rule = q.RuleOK() + + // 由于vitess对某些语法的支持不完善,使得如创建临时表等语句无法通过语法检查 + // 所以这里使用正则对触发器、临时表、存储过程等进行匹配 + // 但是目前支持的也不是非常全面,有待完善匹配规则 + // TODO TiDB 目前还不支持触发器、存储过程、自定义函数、外键 + + forbidden := []*regexp.Regexp{ + regexp.MustCompile(`(?i)CREATE\s+FUNCTION\s+`), + } + + for _, reg := range forbidden { + if reg.MatchString(q.Query) { + rule = HeuristicRules["FUN.009"] + if position := reg.FindIndex([]byte(q.Query)); len(position) > 0 { + rule.Position = position[0] + } + break + } + } + return rule +} + // RulePatternMatchingUsage ARG.007 func (q *Query4Audit) RulePatternMatchingUsage() Rule { var rule = q.RuleOK() @@ -1932,39 +2007,6 @@ func (idxAdv *IndexAdvisor) RuleUpdatePrimaryKey() Rule { return rule } -// RuleForbiddenSyntax CLA.017 -func (q *Query4Audit) RuleForbiddenSyntax() Rule { - var rule = q.RuleOK() - - // 由于vitess对某些语法的支持不完善,使得如创建临时表等语句无法通过语法检查 - // 所以这里使用正则对触发器、临时表、存储过程等进行匹配 - // 但是目前支持的也不是非常全面,有待完善匹配规则 - // TODO TiDB 目前还不支持触发器、存储过程、自定义函数、外键 - - forbidden := []*regexp.Regexp{ - regexp.MustCompile(`(?i)CREATE\s+TRIGGER\s+`), - - regexp.MustCompile(`(?i)CREATE\s+TEMPORARY\s+TABLE\s+`), - - regexp.MustCompile(`(?i)CREATE\s+VIEW\s+`), - regexp.MustCompile(`(?i)REPLACE\s+VIEW\s+`), - - regexp.MustCompile(`(?i)CREATE\s+PROCEDURE\s+`), - regexp.MustCompile(`(?i)CREATE\s+FUNCTION\s+`), - } - - for _, reg := range forbidden { - if reg.MatchString(q.Query) { - rule = HeuristicRules["CLA.017"] - if position := reg.FindIndex([]byte(q.Query)); len(position) > 0 { - rule.Position = position[0] - } - break - } - } - return rule -} - // RuleNestedSubQueries JOI.006 func (q *Query4Audit) RuleNestedSubQueries() Rule { var rule = q.RuleOK() @@ -2826,6 +2868,57 @@ func (q *Query4Audit) RuleTableCharsetCheck() Rule { return rule } +// RuleForbiddenView TBL.006 +func (q *Query4Audit) RuleForbiddenView() Rule { + var rule = q.RuleOK() + + // 由于vitess对某些语法的支持不完善,使得如创建临时表等语句无法通过语法检查 + // 所以这里使用正则对触发器、临时表、存储过程等进行匹配 + // 但是目前支持的也不是非常全面,有待完善匹配规则 + // TODO TiDB 目前还不支持触发器、存储过程、自定义函数、外键 + + forbidden := []*regexp.Regexp{ + regexp.MustCompile(`(?i)CREATE\s+VIEW\s+`), + regexp.MustCompile(`(?i)REPLACE\s+VIEW\s+`), + } + + for _, reg := range forbidden { + if reg.MatchString(q.Query) { + rule = HeuristicRules["TBL.006"] + if position := reg.FindIndex([]byte(q.Query)); len(position) > 0 { + rule.Position = position[0] + } + break + } + } + return rule +} + +// RuleForbiddenTempTable TBL.007 +func (q *Query4Audit) RuleForbiddenTempTable() Rule { + var rule = q.RuleOK() + + // 由于vitess对某些语法的支持不完善,使得如创建临时表等语句无法通过语法检查 + // 所以这里使用正则对触发器、临时表、存储过程等进行匹配 + // 但是目前支持的也不是非常全面,有待完善匹配规则 + // TODO TiDB 目前还不支持触发器、存储过程、自定义函数、外键 + + forbidden := []*regexp.Regexp{ + regexp.MustCompile(`(?i)CREATE\s+TEMPORARY\s+TABLE\s+`), + } + + for _, reg := range forbidden { + if reg.MatchString(q.Query) { + rule = HeuristicRules["TBL.007"] + if position := reg.FindIndex([]byte(q.Query)); len(position) > 0 { + rule.Position = position[0] + } + break + } + } + return rule +} + // RuleBlobDefaultValue COL.015 func (q *Query4Audit) RuleBlobDefaultValue() Rule { var rule = q.RuleOK() diff --git a/advisor/heuristic_test.go b/advisor/heuristic_test.go index b2d52affa5ca4660fb750c0b1b5669e5aa597875..de57328c13ee88ff90d84da51e5ff6739e96ec54 100644 --- a/advisor/heuristic_test.go +++ b/advisor/heuristic_test.go @@ -1637,20 +1637,86 @@ func TestRuleHavingClause(t *testing.T) { common.Log.Debug("Exiting function: %s", common.GetFunctionName()) } -// CLA.017 -func TestRuleForbiddenSyntax(t *testing.T) { +// FUN.007 +func TestRuleForbiddenTrigger(t *testing.T) { + common.Log.Debug("Entering function: %s", common.GetFunctionName()) + sqls := []string{ + `CREATE TRIGGER t1 AFTER INSERT ON work FOR EACH ROW INSERT INTO time VALUES(NOW());`, + } + for _, sql := range sqls { + q, _ := NewQuery4Audit(sql) + rule := q.RuleForbiddenTrigger() + if rule.Item != "FUN.007" { + t.Error("Rule not match:", rule.Item, "Expect : FUN.007") + } + + } + common.Log.Debug("Exiting function: %s", common.GetFunctionName()) +} + +// FUN.008 +func TestRuleForbiddenProcedure(t *testing.T) { + common.Log.Debug("Entering function: %s", common.GetFunctionName()) + sqls := []string{ + `CREATE PROCEDURE simpleproc (OUT param1 INT)`, + } + for _, sql := range sqls { + q, _ := NewQuery4Audit(sql) + rule := q.RuleForbiddenProcedure() + if rule.Item != "FUN.008" { + t.Error("Rule not match:", rule.Item, "Expect : FUN.008") + } + + } + common.Log.Debug("Exiting function: %s", common.GetFunctionName()) +} + +// FUN.009 +func TestRuleForbiddenFunction(t *testing.T) { + common.Log.Debug("Entering function: %s", common.GetFunctionName()) + sqls := []string{ + `CREATE FUNCTION hello (s CHAR(20));`, + } + for _, sql := range sqls { + q, _ := NewQuery4Audit(sql) + rule := q.RuleForbiddenFunction() + if rule.Item != "FUN.009" { + t.Error("Rule not match:", rule.Item, "Expect : FUN.009") + } + + } + common.Log.Debug("Exiting function: %s", common.GetFunctionName()) +} + +// TBL.006 +func TestRuleForbiddenView(t *testing.T) { common.Log.Debug("Entering function: %s", common.GetFunctionName()) sqls := []string{ `create view v_today (today) AS SELECT CURRENT_DATE;`, `CREATE VIEW v (col) AS SELECT 'abc';`, - `CREATE FUNCTION hello (s CHAR(20));`, - `CREATE PROCEDURE simpleproc (OUT param1 INT)`, } for _, sql := range sqls { q, _ := NewQuery4Audit(sql) - rule := q.RuleForbiddenSyntax() - if rule.Item != "CLA.017" { - t.Error("Rule not match:", rule.Item, "Expect : CLA.017") + rule := q.RuleForbiddenView() + if rule.Item != "TBL.006" { + t.Error("Rule not match:", rule.Item, "Expect : TBL.006") + } + + } + common.Log.Debug("Exiting function: %s", common.GetFunctionName()) +} + +// TBL.007 +func TestRuleForbiddenTempTable(t *testing.T) { + common.Log.Debug("Entering function: %s", common.GetFunctionName()) + sqls := []string{ + "CREATE TEMPORARY TABLE `work` (`time` time DEFAULT NULL) ENGINE=InnoDB;", + } + for _, sql := range sqls { + q, _ := NewQuery4Audit(sql) + rule := q.RuleForbiddenTempTable() + if rule.Item != "TBL.007" { + t.Error("Rule not match:", rule.Item, "Expect : TBL.007") } } diff --git a/advisor/rules.go b/advisor/rules.go index 37de8fe002467bf7217f44d42c15e07132a4035e..542230e35eab72516c693ed69ad7fcdb14ce06c6 100644 --- a/advisor/rules.go +++ b/advisor/rules.go @@ -423,14 +423,6 @@ func init() { Case: "update tbl set col=1", Func: (*Query4Audit).RuleOK, // 该建议在indexAdvisor中给 }, - "CLA.017": { - Item: "CLA.017", - Severity: "L2", - Summary: "不建议使用存储过程、视图、触发器、临时表等", - Content: `这些功能的使用在一定程度上会使得程序难以调试和拓展,更没有移植性,且会极大的增加出现 BUG 的概率。`, - Case: "CREATE VIEW v_today (today) AS SELECT CURRENT_DATE;", - Func: (*Query4Audit).RuleForbiddenSyntax, - }, "COL.001": { Item: "COL.001", Severity: "L1", @@ -634,6 +626,30 @@ func init() { Case: "SELECT SUM(COL) FROM tbl;", Func: (*Query4Audit).RuleSumNPE, }, + "FUN.007": { + Item: "FUN.007", + Severity: "L1", + Summary: "不建议使用触发器", + Content: `触发器的执行没有反馈和日志,隐藏了实际的执行步骤,当数据库出现问题是,不能通过慢日志分析触发器的具体执行情况,不易发现问题。在MySQL中,触发器不能临时关闭或打开,在数据迁移或数据恢复等场景下,需要临时drop触发器,可能影响到生产环境。`, + Case: "CREATE TRIGGER t1 AFTER INSERT ON work FOR EACH ROW INSERT INTO time VALUES(NOW());", + Func: (*Query4Audit).RuleForbiddenTrigger, + }, + "FUN.008": { + Item: "FUN.008", + Severity: "L1", + Summary: "不建议使用存储过程", + Content: `存储过程无版本控制,配合业务的存储过程升级很难做到业务无感知。存储过程在拓展和移植上也存在问题。`, + Case: "CREATE PROCEDURE simpleproc (OUT param1 INT);", + Func: (*Query4Audit).RuleForbiddenProcedure, + }, + "FUN.009": { + Item: "FUN.009", + Severity: "L1", + Summary: "不建议使用自定义函数", + Content: `不建议使用自定义函数`, + Case: "CREATE FUNCTION hello (s CHAR(20));", + Func: (*Query4Audit).RuleForbiddenFunction, + }, "GRP.001": { Item: "GRP.001", Severity: "L2", @@ -1072,6 +1088,22 @@ func init() { Case: "CREATE TABLE tbl (a int) DEFAULT CHARSET = latin1;", Func: (*Query4Audit).RuleTableCharsetCheck, }, + "TBL.006": { + Item: "TBL.006", + Severity: "L1", + Summary: "不建议使用视图", + Content: `不建议使用视图`, + Case: "create view v_today (today) AS SELECT CURRENT_DATE;", + Func: (*Query4Audit).RuleForbiddenView, + }, + "TBL.007": { + Item: "TBL.007", + Severity: "L1", + Summary: "不建议使用临时表", + Content: `不建议使用临时表`, + Case: "CREATE TEMPORARY TABLE `work` (`time` time DEFAULT NULL) ENGINE=InnoDB;", + Func: (*Query4Audit).RuleForbiddenTempTable, + }, } } diff --git a/advisor/testdata/TestListHeuristicRules.golden b/advisor/testdata/TestListHeuristicRules.golden index f6b37d819dbed5053da8dd76e9c707b5349ade33..a1607118cfbd143c5d0d6729230f4c4b7bba24cd 100644 --- a/advisor/testdata/TestListHeuristicRules.golden +++ b/advisor/testdata/TestListHeuristicRules.golden @@ -352,16 +352,6 @@ update tbl set col=1 ```sql update tbl set col=1 ``` -## 不建议使用存储过程、视图、触发器、临时表等 - -* **Item**:CLA.017 -* **Severity**:L2 -* **Content**:这些功能的使用在一定程度上会使得程序难以调试和拓展,更没有移植性,且会极大的增加出现 BUG 的概率。 -* **Case**: - -```sql -CREATE VIEW v_today (today) AS SELECT CURRENT_DATE; -``` ## 不建议使用 SELECT \* 类型查询 * **Item**:COL.001 @@ -612,6 +602,36 @@ SELECT COUNT(1) FROM tbl; ```sql SELECT SUM(COL) FROM tbl; ``` +## 不建议使用触发器 + +* **Item**:FUN.007 +* **Severity**:L1 +* **Content**:触发器的执行没有反馈和日志,隐藏了实际的执行步骤,当数据库出现问题是,不能通过慢日志分析触发器的具体执行情况,不易发现问题。在MySQL中,触发器不能临时关闭或打开,在数据迁移或数据恢复等场景下,需要临时drop触发器,可能影响到生产环境。 +* **Case**: + +```sql +CREATE TRIGGER t1 AFTER INSERT ON work FOR EACH ROW INSERT INTO time VALUES(NOW()); +``` +## 不建议使用存储过程 + +* **Item**:FUN.008 +* **Severity**:L1 +* **Content**:存储过程无版本控制,配合业务的存储过程升级很难做到业务无感知。存储过程在拓展和移植上也存在问题。 +* **Case**: + +```sql +CREATE PROCEDURE simpleproc (OUT param1 INT); +``` +## 不建议使用自定义函数 + +* **Item**:FUN.009 +* **Severity**:L1 +* **Content**:不建议使用自定义函数 +* **Case**: + +```sql +CREATE FUNCTION hello (s CHAR(20)); +``` ## 不建议对等值查询列使用 GROUP BY * **Item**:GRP.001 @@ -1152,3 +1172,23 @@ CREATE TABLE tbl (a int) AUTO_INCREMENT = 10; ```sql CREATE TABLE tbl (a int) DEFAULT CHARSET = latin1; ``` +## 不建议使用视图 + +* **Item**:TBL.006 +* **Severity**:L1 +* **Content**:不建议使用视图 +* **Case**: + +```sql +create view v_today (today) AS SELECT CURRENT_DATE; +``` +## 不建议使用临时表 + +* **Item**:TBL.007 +* **Severity**:L1 +* **Content**:不建议使用临时表 +* **Case**: + +```sql +CREATE TEMPORARY TABLE `work` (`time` time DEFAULT NULL) ENGINE=InnoDB; +``` diff --git a/advisor/testdata/TestMergeConflictHeuristicRules.golden b/advisor/testdata/TestMergeConflictHeuristicRules.golden index 219114a13a0d2f650293f39f9a226c63b5e0cf86..595b9a407c241424e0f9f9654e8bb2b356723346 100644 --- a/advisor/testdata/TestMergeConflictHeuristicRules.golden +++ b/advisor/testdata/TestMergeConflictHeuristicRules.golden @@ -32,7 +32,6 @@ advisor.Rule{Item:"CLA.013", Severity:"L3", Summary:"不建议使用 HAVING 子 advisor.Rule{Item:"CLA.014", Severity:"L2", Summary:"删除全表时建议使用 TRUNCATE 替代 DELETE", Content:"删除全表时建议使用 TRUNCATE 替代 DELETE", Case:"delete from tbl", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"CLA.015", Severity:"L4", Summary:"UPDATE 未指定 WHERE 条件", Content:"UPDATE 不指定 WHERE 条件一般是致命的,请您三思后行", Case:"update tbl set col=1", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"CLA.016", Severity:"L2", Summary:"不要 UPDATE 主键", Content:"主键是数据表中记录的唯一标识符,不建议频繁更新主键列,这将影响元数据统计信息进而影响正常的查询。", Case:"update tbl set col=1", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} -advisor.Rule{Item:"CLA.017", Severity:"L2", Summary:"不建议使用存储过程、视图、触发器、临时表等", Content:"这些功能的使用在一定程度上会使得程序难以调试和拓展,更没有移植性,且会极大的增加出现 BUG 的概率。", Case:"CREATE VIEW v_today (today) AS SELECT CURRENT_DATE;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"COL.001", Severity:"L1", Summary:"不建议使用 SELECT * 类型查询", Content:"当表结构变更时,使用 * 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。", Case:"select * from tbl where id=1", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"COL.002", Severity:"L2", Summary:"INSERT/REPLACE 未指定列名", Content:"当表结构发生变更,如果 INSERT 或 REPLACE 请求不明确指定列名,请求的结果将会与预想的不同; 建议使用 “INSERT INTO tbl(col1,col2)VALUES ...” 代替。", Case:"insert into tbl values(1,'name')", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"COL.003", Severity:"L2", Summary:"建议修改自增 ID 为无符号类型", Content:"建议修改自增 ID 为无符号类型", Case:"create table test(`id` int(11) NOT NULL AUTO_INCREMENT)", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} @@ -58,6 +57,9 @@ advisor.Rule{Item:"FUN.003", Severity:"L3", Summary:"使用了合并为可空列 advisor.Rule{Item:"FUN.004", Severity:"L4", Summary:"不建议使用 SYSDATE() 函数", Content:"SYSDATE() 函数可能导致主从数据不一致,请使用 NOW() 函数替代 SYSDATE()。", Case:"SELECT SYSDATE();", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"FUN.005", Severity:"L1", Summary:"不建议使用 COUNT(col) 或 COUNT(常量)", Content:"不要使用 COUNT(col) 或 COUNT(常量) 来替代 COUNT(*), COUNT(*) 是 SQL92 定义的标准统计行数的方法,跟数据无关,跟 NULL 和非 NULL 也无关。", Case:"SELECT COUNT(1) FROM tbl;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"FUN.006", Severity:"L1", Summary:"使用 SUM(COL) 时需注意 NPE 问题", Content:"当某一列的值全是 NULL 时,COUNT(COL) 的返回结果为0,但 SUM(COL) 的返回结果为 NULL,因此使用 SUM() 时需注意 NPE 问题。可以使用如下方式来避免 SUM 的 NPE 问题: SELECT IF(ISNULL(SUM(COL)), 0, SUM(COL)) FROM tbl", Case:"SELECT SUM(COL) FROM tbl;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} +advisor.Rule{Item:"FUN.007", Severity:"L1", Summary:"不建议使用触发器", Content:"触发器的执行没有反馈和日志,隐藏了实际的执行步骤,当数据库出现问题是,不能通过慢日志分析触发器的具体执行情况,不易发现问题。在MySQL中,触发器不能临时关闭或打开,在数据迁移或数据恢复等场景下,需要临时drop触发器,可能影响到生产环境。", Case:"CREATE TRIGGER t1 AFTER INSERT ON work FOR EACH ROW INSERT INTO time VALUES(NOW());", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} +advisor.Rule{Item:"FUN.008", Severity:"L1", Summary:"不建议使用存储过程", Content:"存储过程无版本控制,配合业务的存储过程升级很难做到业务无感知。存储过程在拓展和移植上也存在问题。", Case:"CREATE PROCEDURE simpleproc (OUT param1 INT);", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} +advisor.Rule{Item:"FUN.009", Severity:"L1", Summary:"不建议使用自定义函数", Content:"不建议使用自定义函数", Case:"CREATE FUNCTION hello (s CHAR(20));", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"GRP.001", Severity:"L2", Summary:"不建议对等值查询列使用 GROUP BY", Content:"GROUP BY 中的列在前面的 WHERE 条件中使用了等值查询,对这样的列进行 GROUP BY 意义不大。", Case:"select film_id, title from film where release_year='2006' group by release_year", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"JOI.001", Severity:"L2", Summary:"JOIN 语句混用逗号和 ANSI 模式", Content:"表连接的时候混用逗号和 ANSI JOIN 不便于人类理解,并且MySQL不同版本的表连接行为和优先级均有所不同,当 MySQL 版本变化后可能会引入错误。", Case:"select c1,c2,c3 from t1,t2 join t3 on t1.c1=t2.c1,t1.c3=t3,c1 where id>1000", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"JOI.002", Severity:"L4", Summary:"同一张表被连接两次", Content:"相同的表在 FROM 子句中至少出现两次,可以简化为对该表的单次访问。", Case:"select tb1.col from (tb1, tb2) join tb2 on tb1.id=tb.id where tb1.id=1", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} @@ -109,3 +111,5 @@ advisor.Rule{Item:"TBL.002", Severity:"L4", Summary:"请为表选择合适的存 advisor.Rule{Item:"TBL.003", Severity:"L8", Summary:"以DUAL命名的表在数据库中有特殊含义", Content:"DUAL表为虚拟表,不需要创建即可使用,也不建议服务以DUAL命名表。", Case:"create table dual(id int, primary key (id));", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"TBL.004", Severity:"L2", Summary:"表的初始AUTO_INCREMENT值不为0", Content:"AUTO_INCREMENT不为0会导致数据空洞。", Case:"CREATE TABLE tbl (a int) AUTO_INCREMENT = 10;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"TBL.005", Severity:"L4", Summary:"请使用推荐的字符集", Content:"表字符集只允许设置为utf8,utf8mb4", Case:"CREATE TABLE tbl (a int) DEFAULT CHARSET = latin1;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} +advisor.Rule{Item:"TBL.006", Severity:"L1", Summary:"不建议使用视图", Content:"不建议使用视图", Case:"create view v_today (today) AS SELECT CURRENT_DATE;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} +advisor.Rule{Item:"TBL.007", Severity:"L1", Summary:"不建议使用临时表", Content:"不建议使用临时表", Case:"CREATE TEMPORARY TABLE `work` (`time` time DEFAULT NULL) ENGINE=InnoDB;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} diff --git a/doc/heuristic.md b/doc/heuristic.md index f6b37d819dbed5053da8dd76e9c707b5349ade33..a1607118cfbd143c5d0d6729230f4c4b7bba24cd 100644 --- a/doc/heuristic.md +++ b/doc/heuristic.md @@ -352,16 +352,6 @@ update tbl set col=1 ```sql update tbl set col=1 ``` -## 不建议使用存储过程、视图、触发器、临时表等 - -* **Item**:CLA.017 -* **Severity**:L2 -* **Content**:这些功能的使用在一定程度上会使得程序难以调试和拓展,更没有移植性,且会极大的增加出现 BUG 的概率。 -* **Case**: - -```sql -CREATE VIEW v_today (today) AS SELECT CURRENT_DATE; -``` ## 不建议使用 SELECT \* 类型查询 * **Item**:COL.001 @@ -612,6 +602,36 @@ SELECT COUNT(1) FROM tbl; ```sql SELECT SUM(COL) FROM tbl; ``` +## 不建议使用触发器 + +* **Item**:FUN.007 +* **Severity**:L1 +* **Content**:触发器的执行没有反馈和日志,隐藏了实际的执行步骤,当数据库出现问题是,不能通过慢日志分析触发器的具体执行情况,不易发现问题。在MySQL中,触发器不能临时关闭或打开,在数据迁移或数据恢复等场景下,需要临时drop触发器,可能影响到生产环境。 +* **Case**: + +```sql +CREATE TRIGGER t1 AFTER INSERT ON work FOR EACH ROW INSERT INTO time VALUES(NOW()); +``` +## 不建议使用存储过程 + +* **Item**:FUN.008 +* **Severity**:L1 +* **Content**:存储过程无版本控制,配合业务的存储过程升级很难做到业务无感知。存储过程在拓展和移植上也存在问题。 +* **Case**: + +```sql +CREATE PROCEDURE simpleproc (OUT param1 INT); +``` +## 不建议使用自定义函数 + +* **Item**:FUN.009 +* **Severity**:L1 +* **Content**:不建议使用自定义函数 +* **Case**: + +```sql +CREATE FUNCTION hello (s CHAR(20)); +``` ## 不建议对等值查询列使用 GROUP BY * **Item**:GRP.001 @@ -1152,3 +1172,23 @@ CREATE TABLE tbl (a int) AUTO_INCREMENT = 10; ```sql CREATE TABLE tbl (a int) DEFAULT CHARSET = latin1; ``` +## 不建议使用视图 + +* **Item**:TBL.006 +* **Severity**:L1 +* **Content**:不建议使用视图 +* **Case**: + +```sql +create view v_today (today) AS SELECT CURRENT_DATE; +``` +## 不建议使用临时表 + +* **Item**:TBL.007 +* **Severity**:L1 +* **Content**:不建议使用临时表 +* **Case**: + +```sql +CREATE TEMPORARY TABLE `work` (`time` time DEFAULT NULL) ENGINE=InnoDB; +```