提交 e5a73668 编写于 作者: martianzhang's avatar martianzhang

daily update

  1. Makefile add `daily-quick` let test more fast
  2. Heuristic rule word lint
  3. FUN.001 add more logic, unstable now
上级 4768aa4d
......@@ -134,13 +134,13 @@ vendor: vitess tidb
.PHONY: tidb-parser
tidb-parser: tidb
@echo "\033[92mimporting tidb sql parser ...\033[0m"
@cd ${GOPATH}/src/github.com/pingcap/tidb && git checkout ec9672cea6612481b1da845dbab620b7a5581ca4 && make parser
@cd ${GOPATH}/src/github.com/pingcap/tidb && git checkout --quiet ec9672cea6612481b1da845dbab620b7a5581ca4 && make parser
# gometalinter
# 如果有不想改的lint问题可以使用metalinter.sh加黑名单
#@bash doc/example/metalinter.sh
.PHONY: lint
lint: build
lint: fast
@echo "\033[92mRun linter check ...\033[0m"
CGO_ENABLED=0 retool do gometalinter.v2 -j 1 --config doc/example/metalinter.json ./...
retool do revive -formatter friendly --exclude vendor/... -config doc/example/revive.toml ./...
......@@ -196,6 +196,11 @@ main_test: install
daily: | deps fmt vendor tidb-parser docker cover doc lint release install main_test clean logo
@echo "\033[92mdaily build finished\033[0m"
# vendor, tidb-parser, docker will cost long time, if all those are ready, daily-quick will much more fast.
.PHONY: daily-quick
daily-quick: | deps fmt cover doc lint logo
@echo "\033[92mdaily-quick build finished\033[0m"
.PHONY: logo
logo:
@echo "\033[93m"
......
......@@ -2181,9 +2181,40 @@ func (q *Query4Audit) RuleDataDrop() Rule {
func (q *Query4Audit) RuleCompareWithFunction() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
// Vitess 中有些函数进行了单独定义不在 FuncExpr 中,如: substring。所以不能直接用 FuncExpr 判断。
switch n := node.(type) {
case *sqlparser.ComparisonExpr:
if strings.HasSuffix(sqlparser.String(n.Left), ")") {
switch n.Left.(type) {
case *sqlparser.SQLVal, *sqlparser.ColName:
default:
rule = HeuristicRules["FUN.001"]
return false, nil
}
/*
// func always has bracket
if strings.HasSuffix(sqlparser.String(n.Left), ")") {
rule = HeuristicRules["FUN.001"]
return false, nil
}
*/
// func(a) between func(c) and func(d)
case *sqlparser.RangeCond:
switch n.Left.(type) {
case *sqlparser.SQLVal, *sqlparser.ColName:
default:
rule = HeuristicRules["FUN.001"]
return false, nil
}
switch n.From.(type) {
case *sqlparser.SQLVal, *sqlparser.ColName:
default:
rule = HeuristicRules["FUN.001"]
return false, nil
}
switch n.To.(type) {
case *sqlparser.SQLVal, *sqlparser.ColName:
default:
rule = HeuristicRules["FUN.001"]
return false, nil
}
......
......@@ -679,7 +679,7 @@ func TestRuleNoDeterministicGroupby(t *testing.T) {
func TestRuleNoDeterministicLimit(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
sqls := []string{
"select col1,col2 from tbl where name='zhangsan' limit 10",
"select col1,col2 from tbl where name='tony' limit 10",
}
for _, sql := range sqls {
q, err := NewQuery4Audit(sql)
......@@ -1194,7 +1194,7 @@ func TestRuleImpreciseDataType(t *testing.T) {
`select * from tb where col = 0.00001;`,
},
{
"REPLACE INTO `binks3` (`hostname`,`storagehost`, `filename`, `starttime`, `binlogstarttime`, `uploadname`, `binlogsize`, `filesize`, `md5`, `status`) VALUES (1, 1, 1, 1, 1, 1, ?, ?);",
"REPLACE INTO `storage` (`hostname`,`storagehost`, `filename`, `starttime`, `binlogstarttime`, `uploadname`, `binlogsize`, `filesize`, `md5`, `status`) VALUES (1, 1, 1, 1, 1, 1, ?, ?);",
},
}
for _, sql := range sqls[0] {
......@@ -1249,8 +1249,8 @@ func TestRuleValuesInDefinition(t *testing.T) {
func TestRuleIndexAttributeOrder(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
sqls := []string{
`create index idx1 on tabl(last_name,first_name);`,
`alter table tabl add index idx1 (last_name,first_name);`,
`create index idx1 on tab(last_name,first_name);`,
`alter table tab add index idx1 (last_name,first_name);`,
`CREATE TABLE test (id int,blob_col BLOB, INDEX(blob_col(10),id));`,
}
for _, sql := range sqls {
......@@ -1271,7 +1271,7 @@ func TestRuleIndexAttributeOrder(t *testing.T) {
func TestRuleNullUsage(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
sqls := []string{
`select c1,c2,c3 from tabl where c4 is null or c4 <> 1;`,
`select c1,c2,c3 from tab where c4 is null or c4 <> 1;`,
}
for _, sql := range sqls {
q, err := NewQuery4Audit(sql)
......@@ -1291,7 +1291,7 @@ func TestRuleNullUsage(t *testing.T) {
func TestRuleStringConcatenation(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
sqls := []string{
`select c1 || coalesce(' ' || c2 || ' ', ' ') || c3 as c from tabl;`,
`select c1 || coalesce(' ' || c2 || ' ', ' ') || c3 as c from tab;`,
}
for _, sql := range sqls {
q, err := NewQuery4Audit(sql)
......@@ -1609,7 +1609,7 @@ func TestRuleForbiddenSyntax(t *testing.T) {
func TestRuleNestedSubQueries(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
sqls := []string{
`SELECT s,p,d FROM tabl WHERE p.p_id = (SELECT s.p_id FROM tabl WHERE s.c_id = 100996 AND s.q = 1 );`,
`SELECT s,p,d FROM tab WHERE p.p_id = (SELECT s.p_id FROM tab WHERE s.c_id = 100996 AND s.q = 1 );`,
}
for _, sql := range sqls {
q, err := NewQuery4Audit(sql)
......@@ -1672,7 +1672,7 @@ func TestRuleMultiDBJoin(t *testing.T) {
func TestRuleORUsage(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
sqls := []string{
`SELECT c1,c2,c3 FROM tabl WHERE c1 = 14 OR c2 = 17;`,
`SELECT c1,c2,c3 FROM tab WHERE c1 = 14 OR c2 = 17;`,
}
for _, sql := range sqls {
q, err := NewQuery4Audit(sql)
......@@ -1969,9 +1969,15 @@ func TestRuleDataDrop(t *testing.T) {
func TestCompareWithFunction(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
sqls := [][]string{
{`select id from t where substring(name,1,3)='abc';`},
{
`select id from t where substring(name,1,3)='abc';`,
`SELECT * FROM tbl WHERE UNIX_TIMESTAMP(loginTime) BETWEEN UNIX_TIMESTAMP('2018-11-16 09:46:00 +0800 CST') AND UNIX_TIMESTAMP('2018-11-22 00:00:00 +0800 CST')`,
},
// TODO: 右侧使用函数比较
{`select id from t where 'abc'=substring(name,1,3);`},
{
`select id from t where 'abc'=substring(name,1,3);`,
`select id from t where col = (select 1)`,
},
}
for _, sql := range sqls[0] {
q, err := NewQuery4Audit(sql)
......@@ -2062,7 +2068,7 @@ func TestRuleIn(t *testing.T) {
}
// ARG.006
func TestRuleisNullIsNotNull(t *testing.T) {
func TestRuleIsNullIsNotNull(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
sqls := []string{
`select id from t where num is null;`,
......@@ -2249,10 +2255,10 @@ func TestRuleAlterDropKey(t *testing.T) {
func TestRuleCantBeNull(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
sqls := []string{
"CREATE TABLE `sbtest` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `c` longblob, PRIMARY KEY (`id`));",
"alter TABLE `sbtest` add column `c` longblob;",
"alter TABLE `sbtest` add column `c` text;",
"alter TABLE `sbtest` add column `c` blob;",
"CREATE TABLE `tbl` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `c` longblob, PRIMARY KEY (`id`));",
"alter TABLE `tbl` add column `c` longblob;",
"alter TABLE `tbl` add column `c` text;",
"alter TABLE `tbl` add column `c` blob;",
}
for _, sql := range sqls {
q, err := NewQuery4Audit(sql)
......
......@@ -212,7 +212,7 @@ func init() {
Item: "ARG.005",
Severity: "L1",
Summary: "IN要慎用,元素过多会导致全表扫描",
Content: ` 如:select id from t where num in(1,2,3)对于连续的数值,能用BETWEEN就不要用IN了:select id from t where num between 1 and 3。而当IN值过多时MySQL也可能会进入全表扫描导致性能急剧下降。`,
Content: ` 如:select id from t where num in(1,2,3)对于连续的数值,能用 BETWEEN 就不要用 IN 了:select id from t where num between 1 and 3。而当IN值过多时MySQL也可能会进入全表扫描导致性能急剧下降。`,
Case: "select id from t where num in(1,2,3)",
Func: (*Query4Audit).RuleIn,
},
......@@ -228,14 +228,14 @@ func init() {
Item: "ARG.007",
Severity: "L3",
Summary: "避免使用模式匹配",
Content: `性能问题是使用模式匹配操作符的最大缺点。使用LIKE或正则表达式进行模式匹配进行查询的另一个问题,是可能会返回意料之外的结果。最好的方案就是使用特殊的搜索引擎技术来替代SQL,比如Apache Lucene。另一个可选方案是将结果保存起来从而减少重复的搜索开销。如果一定要使用SQL,请考虑在MySQL中使用像FULLTEXT索引这样的第三方扩展。但更广泛地说,您不一定要使用SQL来解决所有问题。`,
Content: `性能问题是使用模式匹配操作符的最大缺点。使用LIKE或正则表达式进行模式匹配进行查询的另一个问题,是可能会返回意料之外的结果。最好的方案就是使用特殊的搜索引擎技术来替代SQL,比如 Apache Lucene。另一个可选方案是将结果保存起来从而减少重复的搜索开销。如果一定要使用SQL,请考虑在 MySQL 中使用像 FULLTEXT 索引这样的第三方扩展。但更广泛地说,您不一定要使用SQL来解决所有问题。`,
Case: "select c_id,c2,c3 from tbl where c2 like 'test%'",
Func: (*Query4Audit).RulePatternMatchingUsage,
},
"ARG.008": {
Item: "ARG.008",
Severity: "L1",
Summary: "OR查询索引列时请尽量使用IN谓词",
Summary: "OR 查询索引列时请尽量使用 IN 谓词",
Content: `IN-list谓词可以用于索引检索,并且优化器可以对IN-list进行排序,以匹配索引的排序序列,从而获得更有效的检索。请注意,IN-list必须只包含常量,或在查询块执行期间保持常量的值,例如外引用。`,
Case: "SELECT c1,c2,c3 FROM tbl WHERE c1 = 14 OR c1 = 17",
Func: (*Query4Audit).RuleORUsage,
......@@ -244,7 +244,7 @@ func init() {
Item: "ARG.009",
Severity: "L1",
Summary: "引号中的字符串开头或结尾包含空格",
Content: `如果VARCHAR列的前后存在空格将可能引起逻辑问题,如在MySQL 5.5中'a'和'a '可能会在查询中被认为是相同的值。`,
Content: `如果VARCHAR列的前后存在空格将可能引起逻辑问题,如在MySQL 5.5中 'a' 和 'a ' 可能会在查询中被认为是相同的值。`,
Case: "SELECT 'abc '",
Func: (*Query4Audit).RuleSpaceWithQuote,
},
......@@ -252,7 +252,7 @@ func init() {
Item: "ARG.010",
Severity: "L1",
Summary: "不要使用hint,如sql_no_cache, force index, ignore key, straight join等",
Content: `hint是用来强制SQL按照某个执行计划来执行,但随着数据量变化我们无法保证自己当初的预判是正确的。`,
Content: `hint 是用来强制 SQL 按照某个执行计划来执行,但随着数据量变化我们无法保证自己当初的预判是正确的。`,
Case: "SELECT * FROM t1 USE INDEX (i1) ORDER BY a;",
Func: (*Query4Audit).RuleHint,
},
......@@ -569,28 +569,28 @@ func init() {
Case: "SELECT COUNT(DISTINCT col, col2) FROM tbl;",
Func: (*Query4Audit).RuleCountDistinctMultiCol,
},
// DIS.003灵感来源于如下链接
// DIS.003 灵感来源于如下链接
// http://www.ijstr.org/final-print/oct2015/Query-Optimization-Techniques-Tips-For-Writing-Efficient-And-Faster-Sql-Queries.pdf
"DIS.003": {
Item: "DIS.003",
Severity: "L3",
Summary: "DISTINCT *对有主键的表没有意义",
Content: `当表已经有主键时,对所有列进行DISTINCT的输出结果与不进行DISTINCT操作的结果相同,请不要画蛇添足。`,
Content: `当表已经有主键时,对所有列进行 DISTINCT 的输出结果与不进行 DISTINCT 操作的结果相同,请不要画蛇添足。`,
Case: "SELECT DISTINCT * FROM film;",
Func: (*Query4Audit).RuleDistinctStar,
},
"FUN.001": {
Item: "FUN.001",
Severity: "L2",
Summary: "避免在WHERE条件中使用函数或其他运算符",
Content: `虽然在SQL中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧。`,
Summary: "避免在 WHERE 条件中使用函数或其他运算符",
Content: `虽然在 SQL 中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧。也不建议在查询比较条件两侧书写多余的括号,这会对阅读产生比较大的困扰。`,
Case: "select id from t where substring(name,1,3)='abc'",
Func: (*Query4Audit).RuleCompareWithFunction,
},
"FUN.002": {
Item: "FUN.002",
Severity: "L1",
Summary: "指定了WHERE条件或非MyISAM引擎时使用COUNT(*)操作性能不佳",
Summary: "指定了 WHERE 条件或非 MyISAM 引擎时使用 COUNT(*) 操作性能不佳",
Content: `COUNT(*)的作用是统计表行数,COUNT(COL)的作用是统计指定列非NULL的行数。MyISAM表对于COUNT(*)统计全表行数进行了特殊的优化,通常情况下非常快。但对于非MyISAM表或指定了某些WHERE条件,COUNT(*)操作需要扫描大量的行才能获取精确的结果,性能也因此不佳。有时候某些业务场景并不需要完全精确的COUNT值,此时可以用近似值来代替。EXPLAIN出来的优化器估算的行数就是一个不错的近似值,执行EXPLAIN并不需要真正去执行查询,所以成本很低。`,
Case: "SELECT c3, COUNT(*) AS accounts FROM tab where c2 < 10000 GROUP BY c3 ORDER BY num",
Func: (*Query4Audit).RuleCountStar,
......@@ -631,14 +631,14 @@ func init() {
Item: "GRP.001",
Severity: "L2",
Summary: "不建议对等值查询列使用GROUP BY",
Content: `GROUP BY中的列在前面的WHERE条件中使用了等值查询,对这样的列进行GROUP BY意义不大。`,
Content: `GROUP BY 中的列在前面的 WHERE 条件中使用了等值查询,对这样的列进行GROUP BY意义不大。`,
Case: "select film_id, title from film where release_year='2006' group by release_year",
Func: (*Query4Audit).RuleOK, // 该建议在indexAdvisor中给
},
"JOI.001": {
Item: "JOI.001",
Severity: "L2",
Summary: "JOIN语句混用逗号和ANSI模式",
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",
Func: (*Query4Audit).RuleCommaAnsiJoin,
......@@ -655,7 +655,7 @@ func init() {
Item: "JOI.003",
Severity: "L4",
Summary: "OUTER JOIN失效",
Content: `由于WHERE条件错误使得OUTER JOIN的外部表无数据返回,这会将查询隐式转换为 INNER JOIN 。如:select c from L left join R using(c) where L.a=5 and R.b=10。这种SQL逻辑上可能存在错误或程序员对OUTER JOIN如何工作存在误解,因为LEFT/RIGHT JOIN是LEFT/RIGHT OUTER JOIN的缩写。`,
Content: `由于 WHERE 条件错误使得 OUTER JOIN 的外部表无数据返回,这会将查询隐式转换为 INNER JOIN 。如:select c from L left join R using(c) where L.a=5 and R.b=10。这种SQL逻辑上可能存在错误或程序员对OUTER JOIN如何工作存在误解,因为LEFT/RIGHT JOIN是LEFT/RIGHT OUTER JOIN的缩写。`,
Case: "select c1,c2,c3 from t1 left outer join t2 using(c1) where t1.c2=2 and t2.c3=4",
Func: (*Query4Audit).RuleOK, // TODO
},
......@@ -663,7 +663,7 @@ func init() {
Item: "JOI.004",
Severity: "L4",
Summary: "不建议使用排它JOIN",
Content: `只在右侧表为NULL的带WHERE子句的LEFT OUTER JOIN语句,有可能是在WHERE子句中使用错误的列,如:“... FROM l LEFT OUTER JOIN r ON l.l = r.r WHERE r.z IS NULL”,这个查询正确的逻辑可能是 WHERE r.r IS NULL。`,
Content: `只在右侧表为 NULL 的带 WHERE 子句的LEFT OUTER JOIN语句,有可能是在WHERE子句中使用错误的列,如:“... FROM l LEFT OUTER JOIN r ON l.l = r.r WHERE r.z IS NULL”,这个查询正确的逻辑可能是 WHERE r.r IS NULL。`,
Case: "select c1,c2,c3 from t1 left outer join t2 on t1.c1=t2.c1 where t2.c2 is null",
Func: (*Query4Audit).RuleOK, // TODO
},
......@@ -679,7 +679,7 @@ func init() {
Item: "JOI.006",
Severity: "L4",
Summary: "将嵌套查询重写为JOIN通常会导致更高效的执行和更有效的优化",
Content: `一般来说,非嵌套子查询总是用于关联子查询,最多是来自FROM子句中的一个表,这些子查询用于ANY、ALL和EXISTS的谓词。如果可以根据查询语义决定子查询最多返回一个行,那么一个不相关的子查询或来自FROM子句中的多个表的子查询就被压平了。`,
Content: `一般来说,非嵌套子查询总是用于关联子查询,最多是来自FROM子句中的一个表,这些子查询用于 ANY, ALL 和 EXISTS 的谓词。如果可以根据查询语义决定子查询最多返回一个行,那么一个不相关的子查询或来自FROM子句中的多个表的子查询就被压平了。`,
Case: "SELECT s,p,d FROM tbl WHERE p.p_id = (SELECT s.p_id FROM tbl WHERE s.c_id = 100996 AND s.q = 1 )",
Func: (*Query4Audit).RuleNestedSubQueries,
},
......@@ -694,8 +694,8 @@ func init() {
"JOI.008": {
Item: "JOI.008",
Severity: "L4",
Summary: "不要使用跨DB的Join查询",
Content: `一般来说,跨DB的Join查询意味着查询语句跨越了两个不同的子系统,这可能意味着系统耦合度过高或库表结构设计不合理。`,
Summary: "不要使用跨数据库的 JOIN 查询",
Content: `一般来说,跨数据库的 JOIN 查询意味着查询语句跨越了两个不同的子系统,这可能意味着系统耦合度过高或库表结构设计不合理。`,
Case: "SELECT s,p,d FROM tbl WHERE p.p_id = (SELECT s.p_id FROM tbl WHERE s.c_id = 100996 AND s.q = 1 )",
Func: (*Query4Audit).RuleMultiDBJoin,
},
......@@ -752,8 +752,8 @@ func init() {
"KEY.007": {
Item: "KEY.007",
Severity: "L4",
Summary: "未指定主键或主键非int或bigint",
Content: `未指定主键或主键非int或bigint,建议将主键设置为int unsigned或bigint unsigned。`,
Summary: "未指定主键或主键非 int 或 bigint",
Content: `未指定主键或主键非 int 或 bigint,建议将主键设置为 int unsigned 或 bigint unsigned。`,
Case: "CREATE TABLE tbl (a int);",
Func: (*Query4Audit).RulePKNotInt,
},
......@@ -776,7 +776,7 @@ func init() {
"KWR.001": {
Item: "KWR.001",
Severity: "L2",
Summary: "SQL_CALC_FOUND_ROWS效率低下",
Summary: "SQL_CALC_FOUND_ROWS 效率低下",
Content: `因为SQL_CALC_FOUND_ROWS不能很好地扩展,所以可能导致性能问题; 建议业务使用其他策略来替代SQL_CALC_FOUND_ROWS提供的计数功能,比如:分页结果展示等。`,
Case: "select SQL_CALC_FOUND_ROWS col from tbl where id>1000",
Func: (*Query4Audit).RuleSQLCalcFoundRows,
......@@ -841,7 +841,7 @@ func init() {
Item: "LIT.004",
Severity: "L1",
Summary: "请使用分号或已设定的DELIMITER结尾",
Content: `USE database, SHOW DATABASES等命令也需要使用使用分号或已设定的DELIMITER结尾。`,
Content: `USE database, SHOW DATABASES 等命令也需要使用使用分号或已设定的 DELIMITER 结尾。`,
Case: "USE db",
Func: (*Query4Audit).RuleOK, // TODO: RuleAddDelimiter
},
......@@ -856,8 +856,8 @@ func init() {
"RES.002": {
Item: "RES.002",
Severity: "L4",
Summary: "未使用ORDER BY的LIMIT查询",
Content: `没有ORDER BY的LIMIT会导致非确定性的结果,这取决于查询执行计划。`,
Summary: "未使用 ORDER BY 的 LIMIT 查询",
Content: `没有 ORDER BY 的 LIMIT 会导致非确定性的结果,这取决于查询执行计划。`,
Case: "select col1,col2 from tbl where name=xx limit 10",
Func: (*Query4Audit).RuleNoDeterministicLimit,
},
......@@ -897,7 +897,7 @@ func init() {
Item: "RES.007",
Severity: "L4",
Summary: "永远为真的比较条件",
Content: "查询条件永远为真,这将导致WHERE条件失效进行全表查询。",
Content: "查询条件永远为真,可能导致 WHERE 条件失效进行全表查询。",
Case: "select * from tbl where 1 = 1;",
Func: (*Query4Audit).RuleMeaninglessWhere,
},
......@@ -905,7 +905,7 @@ func init() {
Item: "RES.008",
Severity: "L2",
Summary: "不建议使用LOAD DATA/SELECT ... INTO OUTFILE",
Content: "SELECT INTO OUTFILE需要授予FILE权限,这通过会引入安全问题。LOAD DATA虽然可以提高数据导入速度,但同时也可能导致从库同步延迟过大。",
Content: "SELECT INTO OUTFILE 需要授予 FILE 权限,这通过会引入安全问题。LOAD DATA 虽然可以提高数据导入速度,但同时也可能导致从库同步延迟过大。",
Case: "LOAD DATA INFILE 'data.txt' INTO TABLE db2.my_table;",
Func: (*Query4Audit).RuleLoadFile,
},
......@@ -945,7 +945,7 @@ func init() {
Item: "STA.002",
Severity: "L1",
Summary: "库名或表名点后建议不要加空格",
Content: `当使用db.table或table.column格式访问表或字段时,请不要在点号后面添加空格,虽然这样语法正确。`,
Content: `当使用 db.table 或 table.column 格式访问表或字段时,请不要在点号后面添加空格,虽然这样语法正确。`,
Case: "select col from sakila. film",
Func: (*Query4Audit).RuleSpaceAfterDot,
},
......@@ -976,7 +976,7 @@ func init() {
"SUB.002": {
Item: "SUB.002",
Severity: "L2",
Summary: "如果您不在乎重复的话,建议使用UNION ALL替代UNION",
Summary: "如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION",
Content: `与去除重复的UNION不同,UNION ALL允许重复元组。如果您不关心重复元组,那么使用UNION ALL将是一个更快的选项。`,
Case: "select teacher_id as id,people_name as name from t1,t2 where t1.teacher_id=t2.people_id union select student_id as id,people_name as name from t1,t2 where t1.student_id=t2.people_id",
Func: (*Query4Audit).RuleUNIONUsage,
......@@ -984,8 +984,8 @@ func init() {
"SUB.003": {
Item: "SUB.003",
Severity: "L3",
Summary: "考虑使用EXISTS而不是DISTINCT子查询",
Content: `DISTINCT关键字在对元组排序后删除重复。相反,考虑使用一个带有EXISTS关键字的子查询,您可以避免返回整个表。`,
Summary: "考虑使用 EXISTS 而不是 DISTINCT 子查询",
Content: `DISTINCT 关键字在对元组排序后删除重复。相反,考虑使用一个带有 EXISTS 关键字的子查询,您可以避免返回整个表。`,
Case: "SELECT DISTINCT c.c_id, c.c_name FROM c,e WHERE e.c_id = c.c_id",
Func: (*Query4Audit).RuleDistinctJoinUsage,
},
......@@ -1005,7 +1005,7 @@ func init() {
Item: "SUB.005",
Severity: "L8",
Summary: "子查询不支持LIMIT",
Content: `当前MySQL版本不支持在子查询中进行'LIMIT & IN/ALL/ANY/SOME'。`,
Content: `当前 MySQL 版本不支持在子查询中进行 'LIMIT & IN/ALL/ANY/SOME'。`,
Case: "SELECT * FROM staff WHERE name IN (SELECT NAME FROM customer ORDER BY name LIMIT 1)",
Func: (*Query4Audit).RuleSubQueryLimit,
},
......
......@@ -116,7 +116,7 @@ SELECT * FROM tb WHERE col IN (NULL);
* **Item**:ARG.005
* **Severity**:L1
* **Content**: 如:select id from t where num in(1,2,3)对于连续的数值,能用BETWEEN就不要用IN了:select id from t where num between 1 and 3。而当IN值过多时MySQL也可能会进入全表扫描导致性能急剧下降。
* **Content**: 如:select id from t where num in(1,2,3)对于连续的数值,能用 BETWEEN 就不要用 IN 了:select id from t where num between 1 and 3。而当IN值过多时MySQL也可能会进入全表扫描导致性能急剧下降。
* **Case**:
```sql
......@@ -136,13 +136,13 @@ select id from t where num is null
* **Item**:ARG.007
* **Severity**:L3
* **Content**:性能问题是使用模式匹配操作符的最大缺点。使用LIKE或正则表达式进行模式匹配进行查询的另一个问题,是可能会返回意料之外的结果。最好的方案就是使用特殊的搜索引擎技术来替代SQL,比如Apache Lucene。另一个可选方案是将结果保存起来从而减少重复的搜索开销。如果一定要使用SQL,请考虑在MySQL中使用像FULLTEXT索引这样的第三方扩展。但更广泛地说,您不一定要使用SQL来解决所有问题。
* **Content**:性能问题是使用模式匹配操作符的最大缺点。使用LIKE或正则表达式进行模式匹配进行查询的另一个问题,是可能会返回意料之外的结果。最好的方案就是使用特殊的搜索引擎技术来替代SQL,比如 Apache Lucene。另一个可选方案是将结果保存起来从而减少重复的搜索开销。如果一定要使用SQL,请考虑在 MySQL 中使用像 FULLTEXT 索引这样的第三方扩展。但更广泛地说,您不一定要使用SQL来解决所有问题。
* **Case**:
```sql
select c_id,c2,c3 from tbl where c2 like 'test%'
```
## OR查询索引列时请尽量使用IN谓词
## OR 查询索引列时请尽量使用 IN 谓词
* **Item**:ARG.008
* **Severity**:L1
......@@ -156,7 +156,7 @@ SELECT c1,c2,c3 FROM tbl WHERE c1 = 14 OR c1 = 17
* **Item**:ARG.009
* **Severity**:L1
* **Content**:如果VARCHAR列的前后存在空格将可能引起逻辑问题,如在MySQL 5.5中'a'和'a '可能会在查询中被认为是相同的值。
* **Content**:如果VARCHAR列的前后存在空格将可能引起逻辑问题,如在MySQL 5.5中 'a' 和 'a ' 可能会在查询中被认为是相同的值。
* **Case**:
```sql
......@@ -166,7 +166,7 @@ SELECT 'abc '
* **Item**:ARG.010
* **Severity**:L1
* **Content**:hint是用来强制SQL按照某个执行计划来执行,但随着数据量变化我们无法保证自己当初的预判是正确的。
* **Content**:hint 是用来强制 SQL 按照某个执行计划来执行,但随着数据量变化我们无法保证自己当初的预判是正确的。
* **Case**:
```sql
......@@ -536,23 +536,23 @@ SELECT COUNT(DISTINCT col, col2) FROM tbl;
* **Item**:DIS.003
* **Severity**:L3
* **Content**:当表已经有主键时,对所有列进行DISTINCT的输出结果与不进行DISTINCT操作的结果相同,请不要画蛇添足。
* **Content**:当表已经有主键时,对所有列进行 DISTINCT 的输出结果与不进行 DISTINCT 操作的结果相同,请不要画蛇添足。
* **Case**:
```sql
SELECT DISTINCT * FROM film;
```
## 避免在WHERE条件中使用函数或其他运算符
## 避免在 WHERE 条件中使用函数或其他运算符
* **Item**:FUN.001
* **Severity**:L2
* **Content**:虽然在SQL中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧
* **Content**:虽然在 SQL 中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧。也不建议在查询比较条件两侧书写多余的括号,这会对阅读产生比较大的困扰
* **Case**:
```sql
select id from t where substring(name,1,3)='abc'
```
## 指定了WHERE条件或非MyISAM引擎时使用COUNT(\*)操作性能不佳
## 指定了 WHERE 条件或非 MyISAM 引擎时使用 COUNT(\*) 操作性能不佳
* **Item**:FUN.002
* **Severity**:L1
......@@ -606,13 +606,13 @@ SELECT SUM(COL) FROM tbl;
* **Item**:GRP.001
* **Severity**:L2
* **Content**:GROUP BY中的列在前面的WHERE条件中使用了等值查询,对这样的列进行GROUP BY意义不大。
* **Content**:GROUP BY 中的列在前面的 WHERE 条件中使用了等值查询,对这样的列进行GROUP BY意义不大。
* **Case**:
```sql
select film_id, title from film where release_year='2006' group by release_year
```
## JOIN语句混用逗号和ANSI模式
## JOIN 语句混用逗号和 ANSI 模式
* **Item**:JOI.001
* **Severity**:L2
......@@ -636,7 +636,7 @@ select tb1.col from (tb1, tb2) join tb2 on tb1.id=tb.id where tb1.id=1
* **Item**:JOI.003
* **Severity**:L4
* **Content**:由于WHERE条件错误使得OUTER JOIN的外部表无数据返回,这会将查询隐式转换为 INNER JOIN 。如:select c from L left join R using(c) where L.a=5 and R.b=10。这种SQL逻辑上可能存在错误或程序员对OUTER JOIN如何工作存在误解,因为LEFT/RIGHT JOIN是LEFT/RIGHT OUTER JOIN的缩写。
* **Content**:由于 WHERE 条件错误使得 OUTER JOIN 的外部表无数据返回,这会将查询隐式转换为 INNER JOIN 。如:select c from L left join R using(c) where L.a=5 and R.b=10。这种SQL逻辑上可能存在错误或程序员对OUTER JOIN如何工作存在误解,因为LEFT/RIGHT JOIN是LEFT/RIGHT OUTER JOIN的缩写。
* **Case**:
```sql
......@@ -646,7 +646,7 @@ select c1,c2,c3 from t1 left outer join t2 using(c1) where t1.c2=2 and t2.c3=4
* **Item**:JOI.004
* **Severity**:L4
* **Content**:只在右侧表为NULL的带WHERE子句的LEFT OUTER JOIN语句,有可能是在WHERE子句中使用错误的列,如:“... FROM l LEFT OUTER JOIN r ON l.l = r.r WHERE r.z IS NULL”,这个查询正确的逻辑可能是 WHERE r.r IS NULL。
* **Content**:只在右侧表为 NULL 的带 WHERE 子句的LEFT OUTER JOIN语句,有可能是在WHERE子句中使用错误的列,如:“... FROM l LEFT OUTER JOIN r ON l.l = r.r WHERE r.z IS NULL”,这个查询正确的逻辑可能是 WHERE r.r IS NULL。
* **Case**:
```sql
......@@ -666,7 +666,7 @@ select bp1.p_id, b1.d_d as l, b1.b_id from b1 join bp1 on (b1.b_id = bp1.b_id) l
* **Item**:JOI.006
* **Severity**:L4
* **Content**:一般来说,非嵌套子查询总是用于关联子查询,最多是来自FROM子句中的一个表,这些子查询用于ANY、ALL和EXISTS的谓词。如果可以根据查询语义决定子查询最多返回一个行,那么一个不相关的子查询或来自FROM子句中的多个表的子查询就被压平了。
* **Content**:一般来说,非嵌套子查询总是用于关联子查询,最多是来自FROM子句中的一个表,这些子查询用于 ANY, ALL 和 EXISTS 的谓词。如果可以根据查询语义决定子查询最多返回一个行,那么一个不相关的子查询或来自FROM子句中的多个表的子查询就被压平了。
* **Case**:
```sql
......@@ -682,11 +682,11 @@ SELECT s,p,d FROM tbl WHERE p.p_id = (SELECT s.p_id FROM tbl WHERE s.c_id = 1009
```sql
UPDATE users u LEFT JOIN hobby h ON u.id = h.uid SET u.name = 'pianoboy' WHERE h.hobby = 'piano';
```
## 不要使用跨DB的Join查询
## 不要使用跨数据库的 JOIN 查询
* **Item**:JOI.008
* **Severity**:L4
* **Content**:一般来说,跨DB的Join查询意味着查询语句跨越了两个不同的子系统,这可能意味着系统耦合度过高或库表结构设计不合理。
* **Content**:一般来说,跨数据库的 JOIN 查询意味着查询语句跨越了两个不同的子系统,这可能意味着系统耦合度过高或库表结构设计不合理。
* **Case**:
```sql
......@@ -752,11 +752,11 @@ CREATE TABLE tbl ( a int, b int, c int, KEY idx_a (`a`),KEY idx_b(`b`),KEY idx_c
```sql
CREATE TABLE tbl ( a int, b int, c int, PRIMARY KEY(`a`,`b`,`c`));
```
## 未指定主键或主键非int或bigint
## 未指定主键或主键非 int 或 bigint
* **Item**:KEY.007
* **Severity**:L4
* **Content**:未指定主键或主键非int或bigint,建议将主键设置为int unsigned或bigint unsigned。
* **Content**:未指定主键或主键非 int 或 bigint,建议将主键设置为 int unsigned 或 bigint unsigned。
* **Case**:
```sql
......@@ -782,7 +782,7 @@ SELECT * FROM tbl ORDER BY a DESC, b ASC;
```sql
CREATE UNIQUE INDEX part_of_name ON customer (name(10));
```
## SQL\_CALC\_FOUND\_ROWS效率低下
## SQL\_CALC\_FOUND\_ROWS 效率低下
* **Item**:KWR.001
* **Severity**:L2
......@@ -866,7 +866,7 @@ select c1,c2,c3,c4 from tab1 where col_id REGEXP '[[:<:]]12[[:>:]]'
* **Item**:LIT.004
* **Severity**:L1
* **Content**:USE database, SHOW DATABASES等命令也需要使用使用分号或已设定的DELIMITER结尾。
* **Content**:USE database, SHOW DATABASES 等命令也需要使用使用分号或已设定的 DELIMITER 结尾。
* **Case**:
```sql
......@@ -882,11 +882,11 @@ USE db
```sql
select c1,c2,c3 from t1 where c2='foo' group by c2
```
## 未使用ORDER BY的LIMIT查询
## 未使用 ORDER BY 的 LIMIT 查询
* **Item**:RES.002
* **Severity**:L4
* **Content**:没有ORDER BY的LIMIT会导致非确定性的结果,这取决于查询执行计划。
* **Content**:没有 ORDER BY 的 LIMIT 会导致非确定性的结果,这取决于查询执行计划。
* **Case**:
```sql
......@@ -936,7 +936,7 @@ select * from tbl where 1 != 1;
* **Item**:RES.007
* **Severity**:L4
* **Content**:查询条件永远为真,这将导致WHERE条件失效进行全表查询。
* **Content**:查询条件永远为真,可能导致 WHERE 条件失效进行全表查询。
* **Case**:
```sql
......@@ -946,7 +946,7 @@ select * from tbl where 1 = 1;
* **Item**:RES.008
* **Severity**:L2
* **Content**:SELECT INTO OUTFILE需要授予FILE权限,这通过会引入安全问题。LOAD DATA虽然可以提高数据导入速度,但同时也可能导致从库同步延迟过大。
* **Content**:SELECT INTO OUTFILE 需要授予 FILE 权限,这通过会引入安全问题。LOAD DATA 虽然可以提高数据导入速度,但同时也可能导致从库同步延迟过大。
* **Case**:
```sql
......@@ -996,7 +996,7 @@ select col1,col2 from tbl where type!=0
* **Item**:STA.002
* **Severity**:L1
* **Content**:当使用db.table或table.column格式访问表或字段时,请不要在点号后面添加空格,虽然这样语法正确。
* **Content**:当使用 db.table 或 table.column 格式访问表或字段时,请不要在点号后面添加空格,虽然这样语法正确。
* **Case**:
```sql
......@@ -1032,7 +1032,7 @@ CREATE TABLE ` abc` (a int);
```sql
select col1,col2,col3 from table1 where col2 in(select col from table2)
```
## 如果您不在乎重复的话,建议使用UNION ALL替代UNION
## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
* **Item**:SUB.002
* **Severity**:L2
......@@ -1042,11 +1042,11 @@ select col1,col2,col3 from table1 where col2 in(select col from table2)
```sql
select teacher_id as id,people_name as name from t1,t2 where t1.teacher_id=t2.people_id union select student_id as id,people_name as name from t1,t2 where t1.student_id=t2.people_id
```
## 考虑使用EXISTS而不是DISTINCT子查询
## 考虑使用 EXISTS 而不是 DISTINCT 子查询
* **Item**:SUB.003
* **Severity**:L3
* **Content**:DISTINCT关键字在对元组排序后删除重复。相反,考虑使用一个带有EXISTS关键字的子查询,您可以避免返回整个表。
* **Content**:DISTINCT 关键字在对元组排序后删除重复。相反,考虑使用一个带有 EXISTS 关键字的子查询,您可以避免返回整个表。
* **Case**:
```sql
......@@ -1066,7 +1066,7 @@ SELECT * from tb where id in (select id from (select id from tb))
* **Item**:SUB.005
* **Severity**:L8
* **Content**:当前MySQL版本不支持在子查询中进行'LIMIT & IN/ALL/ANY/SOME'。
* **Content**:当前 MySQL 版本不支持在子查询中进行 'LIMIT & IN/ALL/ANY/SOME'。
* **Case**:
```sql
......
......@@ -116,7 +116,7 @@ SELECT * FROM tb WHERE col IN (NULL);
* **Item**:ARG.005
* **Severity**:L1
* **Content**: 如:select id from t where num in(1,2,3)对于连续的数值,能用BETWEEN就不要用IN了:select id from t where num between 1 and 3。而当IN值过多时MySQL也可能会进入全表扫描导致性能急剧下降。
* **Content**: 如:select id from t where num in(1,2,3)对于连续的数值,能用 BETWEEN 就不要用 IN 了:select id from t where num between 1 and 3。而当IN值过多时MySQL也可能会进入全表扫描导致性能急剧下降。
* **Case**:
```sql
......@@ -136,13 +136,13 @@ select id from t where num is null
* **Item**:ARG.007
* **Severity**:L3
* **Content**:性能问题是使用模式匹配操作符的最大缺点。使用LIKE或正则表达式进行模式匹配进行查询的另一个问题,是可能会返回意料之外的结果。最好的方案就是使用特殊的搜索引擎技术来替代SQL,比如Apache Lucene。另一个可选方案是将结果保存起来从而减少重复的搜索开销。如果一定要使用SQL,请考虑在MySQL中使用像FULLTEXT索引这样的第三方扩展。但更广泛地说,您不一定要使用SQL来解决所有问题。
* **Content**:性能问题是使用模式匹配操作符的最大缺点。使用LIKE或正则表达式进行模式匹配进行查询的另一个问题,是可能会返回意料之外的结果。最好的方案就是使用特殊的搜索引擎技术来替代SQL,比如 Apache Lucene。另一个可选方案是将结果保存起来从而减少重复的搜索开销。如果一定要使用SQL,请考虑在 MySQL 中使用像 FULLTEXT 索引这样的第三方扩展。但更广泛地说,您不一定要使用SQL来解决所有问题。
* **Case**:
```sql
select c_id,c2,c3 from tbl where c2 like 'test%'
```
## OR查询索引列时请尽量使用IN谓词
## OR 查询索引列时请尽量使用 IN 谓词
* **Item**:ARG.008
* **Severity**:L1
......@@ -156,7 +156,7 @@ SELECT c1,c2,c3 FROM tbl WHERE c1 = 14 OR c1 = 17
* **Item**:ARG.009
* **Severity**:L1
* **Content**:如果VARCHAR列的前后存在空格将可能引起逻辑问题,如在MySQL 5.5中'a'和'a '可能会在查询中被认为是相同的值。
* **Content**:如果VARCHAR列的前后存在空格将可能引起逻辑问题,如在MySQL 5.5中 'a' 和 'a ' 可能会在查询中被认为是相同的值。
* **Case**:
```sql
......@@ -166,7 +166,7 @@ SELECT 'abc '
* **Item**:ARG.010
* **Severity**:L1
* **Content**:hint是用来强制SQL按照某个执行计划来执行,但随着数据量变化我们无法保证自己当初的预判是正确的。
* **Content**:hint 是用来强制 SQL 按照某个执行计划来执行,但随着数据量变化我们无法保证自己当初的预判是正确的。
* **Case**:
```sql
......@@ -536,23 +536,23 @@ SELECT COUNT(DISTINCT col, col2) FROM tbl;
* **Item**:DIS.003
* **Severity**:L3
* **Content**:当表已经有主键时,对所有列进行DISTINCT的输出结果与不进行DISTINCT操作的结果相同,请不要画蛇添足。
* **Content**:当表已经有主键时,对所有列进行 DISTINCT 的输出结果与不进行 DISTINCT 操作的结果相同,请不要画蛇添足。
* **Case**:
```sql
SELECT DISTINCT * FROM film;
```
## 避免在WHERE条件中使用函数或其他运算符
## 避免在 WHERE 条件中使用函数或其他运算符
* **Item**:FUN.001
* **Severity**:L2
* **Content**:虽然在SQL中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧
* **Content**:虽然在 SQL 中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧。也不建议在查询比较条件两侧书写多余的括号,这会对阅读产生比较大的困扰
* **Case**:
```sql
select id from t where substring(name,1,3)='abc'
```
## 指定了WHERE条件或非MyISAM引擎时使用COUNT(\*)操作性能不佳
## 指定了 WHERE 条件或非 MyISAM 引擎时使用 COUNT(\*) 操作性能不佳
* **Item**:FUN.002
* **Severity**:L1
......@@ -606,13 +606,13 @@ SELECT SUM(COL) FROM tbl;
* **Item**:GRP.001
* **Severity**:L2
* **Content**:GROUP BY中的列在前面的WHERE条件中使用了等值查询,对这样的列进行GROUP BY意义不大。
* **Content**:GROUP BY 中的列在前面的 WHERE 条件中使用了等值查询,对这样的列进行GROUP BY意义不大。
* **Case**:
```sql
select film_id, title from film where release_year='2006' group by release_year
```
## JOIN语句混用逗号和ANSI模式
## JOIN 语句混用逗号和 ANSI 模式
* **Item**:JOI.001
* **Severity**:L2
......@@ -636,7 +636,7 @@ select tb1.col from (tb1, tb2) join tb2 on tb1.id=tb.id where tb1.id=1
* **Item**:JOI.003
* **Severity**:L4
* **Content**:由于WHERE条件错误使得OUTER JOIN的外部表无数据返回,这会将查询隐式转换为 INNER JOIN 。如:select c from L left join R using(c) where L.a=5 and R.b=10。这种SQL逻辑上可能存在错误或程序员对OUTER JOIN如何工作存在误解,因为LEFT/RIGHT JOIN是LEFT/RIGHT OUTER JOIN的缩写。
* **Content**:由于 WHERE 条件错误使得 OUTER JOIN 的外部表无数据返回,这会将查询隐式转换为 INNER JOIN 。如:select c from L left join R using(c) where L.a=5 and R.b=10。这种SQL逻辑上可能存在错误或程序员对OUTER JOIN如何工作存在误解,因为LEFT/RIGHT JOIN是LEFT/RIGHT OUTER JOIN的缩写。
* **Case**:
```sql
......@@ -646,7 +646,7 @@ select c1,c2,c3 from t1 left outer join t2 using(c1) where t1.c2=2 and t2.c3=4
* **Item**:JOI.004
* **Severity**:L4
* **Content**:只在右侧表为NULL的带WHERE子句的LEFT OUTER JOIN语句,有可能是在WHERE子句中使用错误的列,如:“... FROM l LEFT OUTER JOIN r ON l.l = r.r WHERE r.z IS NULL”,这个查询正确的逻辑可能是 WHERE r.r IS NULL。
* **Content**:只在右侧表为 NULL 的带 WHERE 子句的LEFT OUTER JOIN语句,有可能是在WHERE子句中使用错误的列,如:“... FROM l LEFT OUTER JOIN r ON l.l = r.r WHERE r.z IS NULL”,这个查询正确的逻辑可能是 WHERE r.r IS NULL。
* **Case**:
```sql
......@@ -666,7 +666,7 @@ select bp1.p_id, b1.d_d as l, b1.b_id from b1 join bp1 on (b1.b_id = bp1.b_id) l
* **Item**:JOI.006
* **Severity**:L4
* **Content**:一般来说,非嵌套子查询总是用于关联子查询,最多是来自FROM子句中的一个表,这些子查询用于ANY、ALL和EXISTS的谓词。如果可以根据查询语义决定子查询最多返回一个行,那么一个不相关的子查询或来自FROM子句中的多个表的子查询就被压平了。
* **Content**:一般来说,非嵌套子查询总是用于关联子查询,最多是来自FROM子句中的一个表,这些子查询用于 ANY, ALL 和 EXISTS 的谓词。如果可以根据查询语义决定子查询最多返回一个行,那么一个不相关的子查询或来自FROM子句中的多个表的子查询就被压平了。
* **Case**:
```sql
......@@ -682,11 +682,11 @@ SELECT s,p,d FROM tbl WHERE p.p_id = (SELECT s.p_id FROM tbl WHERE s.c_id = 1009
```sql
UPDATE users u LEFT JOIN hobby h ON u.id = h.uid SET u.name = 'pianoboy' WHERE h.hobby = 'piano';
```
## 不要使用跨DB的Join查询
## 不要使用跨数据库的 JOIN 查询
* **Item**:JOI.008
* **Severity**:L4
* **Content**:一般来说,跨DB的Join查询意味着查询语句跨越了两个不同的子系统,这可能意味着系统耦合度过高或库表结构设计不合理。
* **Content**:一般来说,跨数据库的 JOIN 查询意味着查询语句跨越了两个不同的子系统,这可能意味着系统耦合度过高或库表结构设计不合理。
* **Case**:
```sql
......@@ -752,11 +752,11 @@ CREATE TABLE tbl ( a int, b int, c int, KEY idx_a (`a`),KEY idx_b(`b`),KEY idx_c
```sql
CREATE TABLE tbl ( a int, b int, c int, PRIMARY KEY(`a`,`b`,`c`));
```
## 未指定主键或主键非int或bigint
## 未指定主键或主键非 int 或 bigint
* **Item**:KEY.007
* **Severity**:L4
* **Content**:未指定主键或主键非int或bigint,建议将主键设置为int unsigned或bigint unsigned。
* **Content**:未指定主键或主键非 int 或 bigint,建议将主键设置为 int unsigned 或 bigint unsigned。
* **Case**:
```sql
......@@ -782,7 +782,7 @@ SELECT * FROM tbl ORDER BY a DESC, b ASC;
```sql
CREATE UNIQUE INDEX part_of_name ON customer (name(10));
```
## SQL\_CALC\_FOUND\_ROWS效率低下
## SQL\_CALC\_FOUND\_ROWS 效率低下
* **Item**:KWR.001
* **Severity**:L2
......@@ -866,7 +866,7 @@ select c1,c2,c3,c4 from tab1 where col_id REGEXP '[[:<:]]12[[:>:]]'
* **Item**:LIT.004
* **Severity**:L1
* **Content**:USE database, SHOW DATABASES等命令也需要使用使用分号或已设定的DELIMITER结尾。
* **Content**:USE database, SHOW DATABASES 等命令也需要使用使用分号或已设定的 DELIMITER 结尾。
* **Case**:
```sql
......@@ -882,11 +882,11 @@ USE db
```sql
select c1,c2,c3 from t1 where c2='foo' group by c2
```
## 未使用ORDER BY的LIMIT查询
## 未使用 ORDER BY 的 LIMIT 查询
* **Item**:RES.002
* **Severity**:L4
* **Content**:没有ORDER BY的LIMIT会导致非确定性的结果,这取决于查询执行计划。
* **Content**:没有 ORDER BY 的 LIMIT 会导致非确定性的结果,这取决于查询执行计划。
* **Case**:
```sql
......@@ -936,7 +936,7 @@ select * from tbl where 1 != 1;
* **Item**:RES.007
* **Severity**:L4
* **Content**:查询条件永远为真,这将导致WHERE条件失效进行全表查询。
* **Content**:查询条件永远为真,可能导致 WHERE 条件失效进行全表查询。
* **Case**:
```sql
......@@ -946,7 +946,7 @@ select * from tbl where 1 = 1;
* **Item**:RES.008
* **Severity**:L2
* **Content**:SELECT INTO OUTFILE需要授予FILE权限,这通过会引入安全问题。LOAD DATA虽然可以提高数据导入速度,但同时也可能导致从库同步延迟过大。
* **Content**:SELECT INTO OUTFILE 需要授予 FILE 权限,这通过会引入安全问题。LOAD DATA 虽然可以提高数据导入速度,但同时也可能导致从库同步延迟过大。
* **Case**:
```sql
......@@ -996,7 +996,7 @@ select col1,col2 from tbl where type!=0
* **Item**:STA.002
* **Severity**:L1
* **Content**:当使用db.table或table.column格式访问表或字段时,请不要在点号后面添加空格,虽然这样语法正确。
* **Content**:当使用 db.table 或 table.column 格式访问表或字段时,请不要在点号后面添加空格,虽然这样语法正确。
* **Case**:
```sql
......@@ -1032,7 +1032,7 @@ CREATE TABLE ` abc` (a int);
```sql
select col1,col2,col3 from table1 where col2 in(select col from table2)
```
## 如果您不在乎重复的话,建议使用UNION ALL替代UNION
## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
* **Item**:SUB.002
* **Severity**:L2
......@@ -1042,11 +1042,11 @@ select col1,col2,col3 from table1 where col2 in(select col from table2)
```sql
select teacher_id as id,people_name as name from t1,t2 where t1.teacher_id=t2.people_id union select student_id as id,people_name as name from t1,t2 where t1.student_id=t2.people_id
```
## 考虑使用EXISTS而不是DISTINCT子查询
## 考虑使用 EXISTS 而不是 DISTINCT 子查询
* **Item**:SUB.003
* **Severity**:L3
* **Content**:DISTINCT关键字在对元组排序后删除重复。相反,考虑使用一个带有EXISTS关键字的子查询,您可以避免返回整个表。
* **Content**:DISTINCT 关键字在对元组排序后删除重复。相反,考虑使用一个带有 EXISTS 关键字的子查询,您可以避免返回整个表。
* **Case**:
```sql
......@@ -1066,7 +1066,7 @@ SELECT * from tb where id in (select id from (select id from tb))
* **Item**:SUB.005
* **Severity**:L8
* **Content**:当前MySQL版本不支持在子查询中进行'LIMIT & IN/ALL/ANY/SOME'。
* **Content**:当前 MySQL 版本不支持在子查询中进行 'LIMIT & IN/ALL/ANY/SOME'。
* **Case**:
```sql
......
......@@ -1016,68 +1016,68 @@
{
"checksumSHA1": "w8FCRjH70gM6QttB9QrEh9Y1x64=",
"path": "vitess.io/vitess",
"revision": "5b3aedd79bd1488060ae5c0551b31685892c6eed",
"revisionTime": "2018-11-14T21:08:06Z"
"revision": "088b121f3ef2d0f0631499fcdf77eed9b701b5fe",
"revisionTime": "2018-11-16T06:46:05Z"
},
{
"checksumSHA1": "aKn1oKcY74N8TRLm3Ayt7Q4bbI4=",
"path": "vitess.io/vitess/go/bytes2",
"revision": "5b3aedd79bd1488060ae5c0551b31685892c6eed",
"revisionTime": "2018-11-14T21:08:06Z"
"revision": "088b121f3ef2d0f0631499fcdf77eed9b701b5fe",
"revisionTime": "2018-11-16T06:46:05Z"
},
{
"checksumSHA1": "JVCEN4UGRmg3TofIBdzZMZ3G0Ww=",
"path": "vitess.io/vitess/go/hack",
"revision": "5b3aedd79bd1488060ae5c0551b31685892c6eed",
"revisionTime": "2018-11-14T21:08:06Z"
"revision": "088b121f3ef2d0f0631499fcdf77eed9b701b5fe",
"revisionTime": "2018-11-16T06:46:05Z"
},
{
"checksumSHA1": "e1WJ7vCnVrlQQQlc6n/FewCDMso=",
"path": "vitess.io/vitess/go/sqltypes",
"revision": "5b3aedd79bd1488060ae5c0551b31685892c6eed",
"revisionTime": "2018-11-14T21:08:06Z"
"revision": "088b121f3ef2d0f0631499fcdf77eed9b701b5fe",
"revisionTime": "2018-11-16T06:46:05Z"
},
{
"checksumSHA1": "ntFIQYkBS51G6y+FEkjFW40+HOU=",
"path": "vitess.io/vitess/go/vt/log",
"revision": "5b3aedd79bd1488060ae5c0551b31685892c6eed",
"revisionTime": "2018-11-14T21:08:06Z"
"revision": "088b121f3ef2d0f0631499fcdf77eed9b701b5fe",
"revisionTime": "2018-11-16T06:46:05Z"
},
{
"checksumSHA1": "XozR8bmeSR5KTe/nlUJkpJY2HKI=",
"path": "vitess.io/vitess/go/vt/proto/query",
"revision": "5b3aedd79bd1488060ae5c0551b31685892c6eed",
"revisionTime": "2018-11-14T21:08:06Z"
"revision": "088b121f3ef2d0f0631499fcdf77eed9b701b5fe",
"revisionTime": "2018-11-16T06:46:05Z"
},
{
"checksumSHA1": "OnWsUHLDKcO3spwH0jD55SvKD24=",
"path": "vitess.io/vitess/go/vt/proto/topodata",
"revision": "5b3aedd79bd1488060ae5c0551b31685892c6eed",
"revisionTime": "2018-11-14T21:08:06Z"
"revision": "088b121f3ef2d0f0631499fcdf77eed9b701b5fe",
"revisionTime": "2018-11-16T06:46:05Z"
},
{
"checksumSHA1": "sBAuZ/itMR8U8qbK4yLHxkP6Cpc=",
"path": "vitess.io/vitess/go/vt/proto/vtgate",
"revision": "5b3aedd79bd1488060ae5c0551b31685892c6eed",
"revisionTime": "2018-11-14T21:08:06Z"
"revision": "088b121f3ef2d0f0631499fcdf77eed9b701b5fe",
"revisionTime": "2018-11-16T06:46:05Z"
},
{
"checksumSHA1": "pLWM+SPGZs3k+IhjktE/cGUlpM0=",
"path": "vitess.io/vitess/go/vt/proto/vtrpc",
"revision": "5b3aedd79bd1488060ae5c0551b31685892c6eed",
"revisionTime": "2018-11-14T21:08:06Z"
"revision": "088b121f3ef2d0f0631499fcdf77eed9b701b5fe",
"revisionTime": "2018-11-16T06:46:05Z"
},
{
"checksumSHA1": "3ggEFYVEhMPxyhkKhRGw3x1eZ9M=",
"path": "vitess.io/vitess/go/vt/sqlparser",
"revision": "5b3aedd79bd1488060ae5c0551b31685892c6eed",
"revisionTime": "2018-11-14T21:08:06Z"
"revision": "088b121f3ef2d0f0631499fcdf77eed9b701b5fe",
"revisionTime": "2018-11-16T06:46:05Z"
},
{
"checksumSHA1": "oF4XzuOzwvj1iduX/lYqNSyY/HM=",
"path": "vitess.io/vitess/go/vt/vterrors",
"revision": "5b3aedd79bd1488060ae5c0551b31685892c6eed",
"revisionTime": "2018-11-14T21:08:06Z"
"revision": "088b121f3ef2d0f0631499fcdf77eed9b701b5fe",
"revisionTime": "2018-11-16T06:46:05Z"
}
],
"rootPath": "github.com/XiaoMi/soar"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册