diff --git a/Makefile b/Makefile
index 72c2d34b8aade195b334d1c7c7e6126f5633d6f9..35b09b06a22640ebf44e66d5fbe85041af1de51a 100644
--- a/Makefile
+++ b/Makefile
@@ -45,8 +45,9 @@ deps:
@echo "\033[92mDependency check\033[0m"
@bash ./deps.sh
# The retool tools.json is setup from retool-install.sh
+ # some packages download need more open internet access
retool sync
- retool do gometalinter.v2 intall
+ #retool do gometalinter.v2 --install
# Code format
.PHONY: fmt
@@ -151,7 +152,7 @@ lint: build
@echo "gometalinter check your code is pretty good"
.PHONY: release
-release: deps build
+release: build
@echo "\033[92mCross platform building for release ...\033[0m"
@mkdir -p release
@for GOOS in darwin linux windows; do \
@@ -176,7 +177,7 @@ docker:
-v `pwd`/doc/example/sakila.sql.gz:/docker-entrypoint-initdb.d/sakila.sql.gz \
$(MYSQL_RELEASE):$(MYSQL_VERSION)
- @echo -n "waiting for sakila database initializing "
+ @echo "waiting for sakila database initializing "
@while ! mysql -h 127.0.0.1 -u root sakila -p1tIsB1g3rt -NBe "do 1;" 2>/dev/null; do \
printf '.' ; \
sleep 1 ; \
@@ -201,7 +202,7 @@ daily: | deps fmt vendor docker cover doc lint release install main_test clean l
# vendor, 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
+daily-quick: | deps fmt cover main_test doc lint logo
@echo "\033[92mdaily-quick build finished\033[0m"
.PHONY: logo
diff --git a/advisor/heuristic.go b/advisor/heuristic.go
index 8945f0368598bbd9c8b60780558fe197b3fb418a..83f274754fbfc20097e3626feb6f2ff3d2a942e0 100644
--- a/advisor/heuristic.go
+++ b/advisor/heuristic.go
@@ -1629,6 +1629,9 @@ func (q *Query4Audit) RuleImpreciseDataType() Rule {
case *tidb.CreateTableStmt:
// Create table statement
for _, col := range node.Cols {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeFloat, mysql.TypeDouble, mysql.TypeDecimal, mysql.TypeNewDecimal:
rule = HeuristicRules["COL.009"]
@@ -1641,6 +1644,9 @@ func (q *Query4Audit) RuleImpreciseDataType() Rule {
switch spec.Tp {
case tidb.AlterTableAddColumns, tidb.AlterTableChangeColumn, tidb.AlterTableModifyColumn:
for _, col := range spec.NewColumns {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeFloat, mysql.TypeDouble,
mysql.TypeDecimal, mysql.TypeNewDecimal:
@@ -1686,6 +1692,9 @@ func (q *Query4Audit) RuleValuesInDefinition() Rule {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeSet, mysql.TypeEnum, mysql.TypeBit:
rule = HeuristicRules["COL.010"]
@@ -1696,6 +1705,9 @@ func (q *Query4Audit) RuleValuesInDefinition() Rule {
switch spec.Tp {
case tidb.AlterTableAddColumns, tidb.AlterTableChangeColumn, tidb.AlterTableModifyColumn:
for _, col := range spec.NewColumns {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeSet, mysql.TypeEnum, mysql.TypeBit:
rule = HeuristicRules["COL.010"]
@@ -2238,6 +2250,9 @@ func (q *Query4Audit) RuleReadablePasswords() Rule {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeString, mysql.TypeVarchar, mysql.TypeVarString,
mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob:
@@ -2253,6 +2268,9 @@ func (q *Query4Audit) RuleReadablePasswords() Rule {
switch spec.Tp {
case tidb.AlterTableModifyColumn, tidb.AlterTableChangeColumn, tidb.AlterTableAddColumns:
for _, col := range spec.NewColumns {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeString, mysql.TypeVarchar, mysql.TypeVarString,
mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob:
@@ -2426,6 +2444,9 @@ func (q *Query4Audit) RuleVarcharVSChar() Rule {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
// 在 TiDB 的 AST 中,char 和 binary 的 type 都是 mysql.TypeString
// 只是 binary 数据类型的 character 和 collate 是 binary
@@ -2439,6 +2460,9 @@ func (q *Query4Audit) RuleVarcharVSChar() Rule {
switch spec.Tp {
case tidb.AlterTableAddColumns, tidb.AlterTableChangeColumn, tidb.AlterTableModifyColumn:
for _, col := range spec.NewColumns {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeString:
rule = HeuristicRules["COL.008"]
@@ -2553,6 +2577,9 @@ func (q *Query4Audit) RuleBLOBNotNull() Rule {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob:
for _, opt := range col.Options {
@@ -2573,6 +2600,9 @@ func (q *Query4Audit) RuleBLOBNotNull() Rule {
switch spec.Tp {
case tidb.AlterTableAddColumns, tidb.AlterTableModifyColumn, tidb.AlterTableChangeColumn:
for _, col := range spec.NewColumns {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob:
for _, opt := range col.Options {
@@ -2808,6 +2838,9 @@ func (q *Query4Audit) RuleTimestampDefault() Rule {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
+ if col.Tp == nil {
+ continue
+ }
if col.Tp.Tp == mysql.TypeTimestamp {
hasDefault := false
for _, option := range col.Options {
@@ -2829,6 +2862,9 @@ func (q *Query4Audit) RuleTimestampDefault() Rule {
tidb.AlterTableChangeColumn,
tidb.AlterTableAlterColumn:
for _, col := range spec.NewColumns {
+ if col.Tp == nil {
+ continue
+ }
if col.Tp.Tp == mysql.TypeTimestamp {
hasDefault := false
for _, option := range col.Options {
@@ -2879,6 +2915,9 @@ func (q *Query4Audit) RuleColumnWithCharset() Rule {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
+ if col.Tp == nil {
+ continue
+ }
if col.Tp.Charset != "" || col.Tp.Collate != "" {
rule = HeuristicRules["COL.014"]
break
@@ -2890,6 +2929,9 @@ func (q *Query4Audit) RuleColumnWithCharset() Rule {
case tidb.AlterTableAlterColumn, tidb.AlterTableChangeColumn,
tidb.AlterTableModifyColumn, tidb.AlterTableAddColumns:
for _, col := range spec.NewColumns {
+ if col.Tp == nil {
+ continue
+ }
if col.Tp.Charset != "" || col.Tp.Collate != "" {
rule = HeuristicRules["COL.014"]
break
@@ -3091,6 +3133,9 @@ func (q *Query4Audit) RuleBlobDefaultValue() Rule {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeMediumBlob, mysql.TypeTinyBlob, mysql.TypeLongBlob:
for _, opt := range col.Options {
@@ -3107,6 +3152,9 @@ func (q *Query4Audit) RuleBlobDefaultValue() Rule {
case tidb.AlterTableModifyColumn, tidb.AlterTableAlterColumn,
tidb.AlterTableChangeColumn, tidb.AlterTableAddColumns:
for _, col := range spec.NewColumns {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeMediumBlob, mysql.TypeTinyBlob, mysql.TypeLongBlob:
for _, opt := range col.Options {
@@ -3134,6 +3182,9 @@ func (q *Query4Audit) RuleIntPrecision() Rule {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeLong:
if (col.Tp.Flen < 10 || col.Tp.Flen > 11) && col.Tp.Flen > 0 {
@@ -3154,6 +3205,9 @@ func (q *Query4Audit) RuleIntPrecision() Rule {
case tidb.AlterTableAddColumns, tidb.AlterTableChangeColumn,
tidb.AlterTableAlterColumn, tidb.AlterTableModifyColumn:
for _, col := range spec.NewColumns {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeLong:
if (col.Tp.Flen < 10 || col.Tp.Flen > 11) && col.Tp.Flen > 0 {
@@ -3185,6 +3239,9 @@ func (q *Query4Audit) RuleVarcharLength() Rule {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeVarchar, mysql.TypeVarString:
if col.Tp.Flen > common.Config.MaxVarcharLength {
@@ -3199,6 +3256,9 @@ func (q *Query4Audit) RuleVarcharLength() Rule {
case tidb.AlterTableAddColumns, tidb.AlterTableChangeColumn,
tidb.AlterTableAlterColumn, tidb.AlterTableModifyColumn:
for _, col := range spec.NewColumns {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeVarchar, mysql.TypeVarString:
if col.Tp.Flen > common.Config.MaxVarcharLength {
@@ -3247,6 +3307,51 @@ func (q *Query4Audit) RuleColumnNotAllowType() Rule {
return rule
}
+// RuleTimePrecision COL.019
+func (q *Query4Audit) RuleTimePrecision() Rule {
+ var rule = q.RuleOK()
+
+ switch q.Stmt.(type) {
+ case *sqlparser.DDL:
+ for _, tiStmt := range q.TiStmt {
+ switch node := tiStmt.(type) {
+ case *tidb.CreateTableStmt:
+ for _, col := range node.Cols {
+ if col.Tp == nil {
+ continue
+ }
+ switch col.Tp.Tp {
+ case mysql.TypeDatetime, mysql.TypeTimestamp, mysql.TypeDuration:
+ if col.Tp.Decimal > 0 {
+ rule = HeuristicRules["COL.019"]
+ }
+ }
+ }
+ case *tidb.AlterTableStmt:
+ for _, spec := range node.Specs {
+ switch spec.Tp {
+ case tidb.AlterTableChangeColumn, tidb.AlterTableAlterColumn,
+ tidb.AlterTableModifyColumn, tidb.AlterTableAddColumns:
+ for _, col := range spec.NewColumns {
+ if col.Tp == nil {
+ continue
+ }
+ switch col.Tp.Tp {
+ case mysql.TypeDatetime, mysql.TypeTimestamp, mysql.TypeDuration:
+ if col.Tp.Decimal > 0 {
+ rule = HeuristicRules["COL.019"]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return rule
+}
+
// RuleNoOSCKey KEY.002
func (q *Query4Audit) RuleNoOSCKey() Rule {
var rule = q.RuleOK()
@@ -3292,6 +3397,9 @@ func (q *Query4Audit) RuleMaxTextColsCount() Rule {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
+ if col.Tp == nil {
+ continue
+ }
switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeLongBlob, mysql.TypeMediumBlob, mysql.TypeTinyBlob:
textColsCount++
@@ -3452,6 +3560,9 @@ func (q *Query4Audit) RuleAutoIncUnsigned() Rule {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
+ if col.Tp == nil {
+ continue
+ }
for _, opt := range col.Options {
if opt.Tp == tidb.ColumnOptionAutoIncrement {
if !mysql.HasUnsignedFlag(col.Tp.Flag) {
@@ -3471,6 +3582,9 @@ func (q *Query4Audit) RuleAutoIncUnsigned() Rule {
case tidb.AlterTableChangeColumn, tidb.AlterTableAlterColumn,
tidb.AlterTableModifyColumn, tidb.AlterTableAddColumns:
for _, col := range spec.NewColumns {
+ if col.Tp == nil {
+ continue
+ }
for _, opt := range col.Options {
if opt.Tp == tidb.ColumnOptionAutoIncrement {
if !mysql.HasUnsignedFlag(col.Tp.Flag) {
diff --git a/advisor/heuristic_test.go b/advisor/heuristic_test.go
index a729e01d4265cc9f99be07c71a62577f76301885..889aa5e759497bc1b85e61e4af78e8727312f417 100644
--- a/advisor/heuristic_test.go
+++ b/advisor/heuristic_test.go
@@ -3073,6 +3073,47 @@ func TestRuleColumnNotAllowType(t *testing.T) {
common.Log.Debug("Exiting function: %s", common.GetFunctionName())
}
+// COL.019
+func TestRuleTimePrecision(t *testing.T) {
+ common.Log.Debug("Entering function: %s", common.GetFunctionName())
+ sqls := [][]string{
+ // 正面的例子
+ {
+ "CREATE TABLE t1 (t TIME(3), dt DATETIME(6));",
+ "ALTER TABLE t1 add t TIME(3);",
+ },
+ // 反面的例子
+ {
+ "CREATE TABLE t1 (t TIME, dt DATETIME);",
+ "ALTER TABLE t1 add t TIME;",
+ },
+ }
+ for _, sql := range sqls[0] {
+ q, err := NewQuery4Audit(sql)
+ if err == nil {
+ rule := q.RuleTimePrecision()
+ if rule.Item != "COL.019" {
+ t.Error("Rule not match:", rule.Item, "Expect : COL.019")
+ }
+ } else {
+ t.Error("sqlparser.Parse Error:", err)
+ }
+ }
+
+ for _, sql := range sqls[1] {
+ q, err := NewQuery4Audit(sql)
+ if err == nil {
+ rule := q.RuleTimePrecision()
+ if rule.Item != "OK" {
+ t.Error("Rule not match:", rule.Item, "Expect : OK")
+ }
+ } else {
+ t.Error("sqlparser.Parse Error:", err)
+ }
+ }
+ common.Log.Debug("Exiting function: %s", common.GetFunctionName())
+}
+
// KEY.002
func TestRuleNoOSCKey(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
diff --git a/advisor/index.go b/advisor/index.go
index ace191c8f73ed69bf3ad7585c740a3655a287578..7d3dd333b658e837611efa4e3f10cb5409870928 100644
--- a/advisor/index.go
+++ b/advisor/index.go
@@ -628,7 +628,6 @@ func (idxAdv *IndexAdvisor) buildJoinIndex(meta common.Meta) []IndexInfo {
indexColsList := make(map[string]map[string][]*common.Column)
for _, col := range IndexCols {
mergeIndex(indexColsList, col)
-
}
if common.Config.TestDSN.Disable || common.Config.OnlineDSN.Disable {
@@ -723,6 +722,11 @@ func (idxAdv *IndexAdvisor) buildIndexWithNoEnv(indexList map[string]map[string]
// mergeIndex 将索引用到的列去重后合并到一起
func mergeIndex(idxList map[string]map[string][]*common.Column, column *common.Column) {
+ // 散粒度低于阈值将不会添加索引
+ if common.Config.MinCardinality/100 > column.Cardinality {
+ return
+ }
+
db := column.DB
tb := column.Table
if idxList[db] == nil {
diff --git a/advisor/index_test.go b/advisor/index_test.go
index b667b084f57b2138cc0a6a82ab747e799027603a..f9f7087e73d92b417a090cb1b2b4d72623e37f04 100644
--- a/advisor/index_test.go
+++ b/advisor/index_test.go
@@ -357,6 +357,8 @@ func TestRuleUpdatePrimaryKey(t *testing.T) {
func TestIndexAdvise(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
+ minCardinalityBak := common.Config.MinCardinality
+ common.Config.MinCardinality = 20
vEnv, rEnv := env.BuildEnv()
defer vEnv.CleanUp()
@@ -377,12 +379,13 @@ func TestIndexAdvise(t *testing.T) {
if idxAdvisor != nil {
rule := idxAdvisor.IndexAdvise().Format()
if len(rule) > 0 {
- pretty.Println(rule)
+ _, _ = pretty.Println(rule)
}
}
}
}
common.Log.Debug("Exiting function: %s", common.GetFunctionName())
+ common.Config.MinCardinality = minCardinalityBak
}
func TestIndexAdviseNoEnv(t *testing.T) {
diff --git a/advisor/rules.go b/advisor/rules.go
index ff56c85ec305223f9f05faacd6a5aee209a4e103..10008d8a568618ed373afc26fa8dad879263f41a 100644
--- a/advisor/rules.go
+++ b/advisor/rules.go
@@ -569,6 +569,14 @@ func init() {
Case: "CREATE TABLE tab (a BOOLEAN);",
Func: (*Query4Audit).RuleColumnNotAllowType,
},
+ "COL.019": {
+ Item: "COL.019",
+ Severity: "L1",
+ Summary: "不建议使用精度在秒级以下的时间数据类型",
+ Content: "使用高精度的时间数据类型带来的存储空间消耗相对较大;MySQL 在5.6.4以上才可以支持精确到微秒的时间数据类型,使用时需要考虑版本兼容问题。",
+ Case: "CREATE TABLE t1 (t TIME(3), dt DATETIME(6));",
+ Func: (*Query4Audit).RuleTimePrecision,
+ },
"DIS.001": {
Item: "DIS.001",
Severity: "L1",
diff --git a/advisor/rules_test.go b/advisor/rules_test.go
index 938a54601813c974ca9607aadd04ba141153e436..1b5044d4cf13ae548a49e708224fcd81508e7b01 100644
--- a/advisor/rules_test.go
+++ b/advisor/rules_test.go
@@ -18,6 +18,7 @@ package advisor
import (
"flag"
+ "strings"
"testing"
"github.com/XiaoMi/soar/common"
@@ -26,6 +27,11 @@ import (
var update = flag.Bool("update", false, "update .golden files")
func TestListTestSQLs(t *testing.T) {
+ for _, sql := range common.TestSQLs {
+ if !strings.HasSuffix(sql, ";") {
+ t.Errorf("%s should end with ';'", sql)
+ }
+ }
err := common.GoldenDiff(func() { ListTestSQLs() }, t.Name(), update)
if nil != err {
t.Fatal(err)
diff --git a/advisor/testdata/TestListHeuristicRules.golden b/advisor/testdata/TestListHeuristicRules.golden
index 1e135d7e686f725269a92573265cf263ed54b6e5..5ca0ffb9f0bdfeeb0bce6939392d3ab27efbe44f 100644
--- a/advisor/testdata/TestListHeuristicRules.golden
+++ b/advisor/testdata/TestListHeuristicRules.golden
@@ -532,6 +532,16 @@ CREATE TABLE tab (a varchar(3500));
```sql
CREATE TABLE tab (a BOOLEAN);
```
+## 不建议使用精度在秒级以下的时间数据类型
+
+* **Item**:COL.019
+* **Severity**:L1
+* **Content**:使用高精度的时间数据类型带来的存储空间消耗相对较大;MySQL 在5.6.4以上才可以支持精确到微秒的时间数据类型,使用时需要考虑版本兼容问题。
+* **Case**:
+
+```sql
+CREATE TABLE t1 (t TIME(3), dt DATETIME(6));
+```
## 消除不必要的 DISTINCT 条件
* **Item**:DIS.001
diff --git a/advisor/testdata/TestListTestSQLs.golden b/advisor/testdata/TestListTestSQLs.golden
index 2f4a8bdd6705d4c81ecedfbd7a879b19c32e886b..e3c442999594329dd73b21fbc53d8763090824b1 100644
--- a/advisor/testdata/TestListTestSQLs.golden
+++ b/advisor/testdata/TestListTestSQLs.golden
@@ -79,6 +79,7 @@ SELECT description FROM film WHERE description IN('NEWS','asd') GROUP BY descrip
alter table address add index idx_city_id(city_id);
alter table inventory add index `idx_store_film` (`store_id`,`film_id`);
alter table inventory add index `idx_store_film` (`store_id`,`film_id`),add index `idx_store_film` (`store_id`,`film_id`),add index `idx_store_film` (`store_id`,`film_id`);
-SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d')
+SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d');
create table hello.t (id int unsigned);
-select * from tb where data >= ''
+select * from tb where data >= '';
+alter table tb alter column id drop default;
diff --git a/advisor/testdata/TestMergeConflictHeuristicRules.golden b/advisor/testdata/TestMergeConflictHeuristicRules.golden
index c17fc2c98036cdf7919df5e20b43530c2523d1b4..2859b6d0da6e4acd502ec7aed0bb447dcad12c2d 100644
--- a/advisor/testdata/TestMergeConflictHeuristicRules.golden
+++ b/advisor/testdata/TestMergeConflictHeuristicRules.golden
@@ -50,6 +50,7 @@ advisor.Rule{Item:"COL.015", Severity:"L4", Summary:"TEXT 和 BLOB 类型的字
advisor.Rule{Item:"COL.016", Severity:"L1", Summary:"整型定义建议采用 INT(10) 或 BIGINT(20)", Content:"INT(M) 在 integer 数据类型中,M 表示最大显示宽度。 在 INT(M) 中,M 的值跟 INT(M) 所占多少存储空间并无任何关系。 INT(3)、INT(4)、INT(8) 在磁盘上都是占用 4 bytes 的存储空间。", Case:"CREATE TABLE tab (a INT(1));", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"COL.017", Severity:"L2", Summary:"VARCHAR 定义长度过长", Content:"varchar 是可变长字符串,不预先分配存储空间,长度不要超过1024,如果存储长度过长 MySQL 将定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。", Case:"CREATE TABLE tab (a varchar(3500));", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"COL.018", Severity:"L1", Summary:"建表语句中使用了不推荐的字段类型", Content:"以下字段类型不被推荐使用:boolean", Case:"CREATE TABLE tab (a BOOLEAN);", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
+advisor.Rule{Item:"COL.019", Severity:"L1", Summary:"不建议使用精度在秒级以下的时间数据类型", Content:"使用高精度的时间数据类型带来的存储空间消耗相对较大;MySQL 在5.6.4以上才可以支持精确到微秒的时间数据类型,使用时需要考虑版本兼容问题。", Case:"CREATE TABLE t1 (t TIME(3), dt DATETIME(6));", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"DIS.001", Severity:"L1", Summary:"消除不必要的 DISTINCT 条件", Content:"太多DISTINCT条件是复杂的裹脚布式查询的症状。考虑将复杂查询分解成许多简单的查询,并减少DISTINCT条件的数量。如果主键列是列的结果集的一部分,则DISTINCT条件可能没有影响。", Case:"SELECT DISTINCT c.c_id,count(DISTINCT c.c_name),count(DISTINCT c.c_e),count(DISTINCT c.c_n),count(DISTINCT c.c_me),c.c_d FROM (select distinct id, name from B) as e WHERE e.country_id = c.country_id", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"DIS.002", Severity:"L3", Summary:"COUNT(DISTINCT) 多列时结果可能和你预想的不同", Content:"COUNT(DISTINCT col) 计算该列除NULL之外的不重复行数,注意 COUNT(DISTINCT col, col2) 如果其中一列全为 NULL 那么即使另一列有不同的值,也返回0。", Case:"SELECT COUNT(DISTINCT col, col2) FROM tbl;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"DIS.003", Severity:"L3", Summary:"DISTINCT * 对有主键的表没有意义", Content:"当表已经有主键时,对所有列进行 DISTINCT 的输出结果与不进行 DISTINCT 操作的结果相同,请不要画蛇添足。", Case:"SELECT DISTINCT * FROM film;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
diff --git a/ast/testdata/TestCompress.golden b/ast/testdata/TestCompress.golden
index 203ba195de92542534e7776990bffd9062e59c3f..70d4d31f3818aaf588d434d64b8f1b1abd3087df 100644
--- a/ast/testdata/TestCompress.golden
+++ b/ast/testdata/TestCompress.golden
@@ -160,9 +160,11 @@ alter table inventory add index `idx_store_film` (`store_id`,`film_id`);
alter table inventory add index `idx_store_film` (`store_id`,`film_id`);
alter table inventory add index `idx_store_film` (`store_id`,`film_id`),add index `idx_store_film` (`store_id`,`film_id`),add index `idx_store_film` (`store_id`,`film_id`);
alter table inventory add index `idx_store_film` (`store_id`,`film_id`),add index `idx_store_film` (`store_id`,`film_id`),add index `idx_store_film` (`store_id`,`film_id`);
-SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d')
-SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d')
+SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d');
+SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d');
create table hello.t (id int unsigned);
create table hello.t (id int unsigned);
-select * from tb where data >= ''
-select * from tb where data >= ''
+select * from tb where data >= '';
+select * from tb where data >= '';
+alter table tb alter column id drop default;
+alter table tb alter column id drop default;
diff --git a/ast/testdata/TestFormat.golden b/ast/testdata/TestFormat.golden
index 4236be095ee94438abf15642d2268a753a8c01ee..a1e1474eef36cf57a89bf2dbb8186c8fababd7aa 100644
--- a/ast/testdata/TestFormat.golden
+++ b/ast/testdata/TestFormat.golden
@@ -846,7 +846,7 @@ ADD
ADD
index `idx_store_film` (
`store_id`, `film_id`);
-SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d')
+SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d');
SELECT
DATE_FORMAT( t. atm, '%Y-%m-%d'
@@ -864,14 +864,20 @@ COUNT( DISTINCT (
)
ORDER BY
DATE_FORMAT( t. atm, '%Y-%m-%d'
-)
+);
create table hello.t (id int unsigned);
create table hello. t (id int unsigned);
-select * from tb where data >= ''
+select * from tb where data >= '';
SELECT
*
FROM
tb
WHERE
- data >= ''
+ data >= '';
+alter table tb alter column id drop default;
+
+ALTER TABLE
+ tb alter column id
+DROP
+ DEFAULT;
diff --git a/ast/testdata/TestPretty.golden b/ast/testdata/TestPretty.golden
index 2f6c85add72ffc32dbbccc8156cd612fdd165018..e1267eeb22771a01efbc23f665b078e9f5e8a14d 100644
--- a/ast/testdata/TestPretty.golden
+++ b/ast/testdata/TestPretty.golden
@@ -1490,7 +1490,7 @@ ADD
ADD
index `idx_store_film` (
`store_id`, `film_id`);
-SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d')
+SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d');
SELECT
DATE_FORMAT( t. atm, '%Y-%m-%d'
@@ -1508,14 +1508,20 @@ COUNT( DISTINCT (
)
ORDER BY
DATE_FORMAT( t. atm, '%Y-%m-%d'
-)
+);
create table hello.t (id int unsigned);
create table hello. t (id int unsigned);
-select * from tb where data >= ''
+select * from tb where data >= '';
SELECT
*
FROM
tb
WHERE
- data >= ''
+ data >= '';
+alter table tb alter column id drop default;
+
+ALTER TABLE
+ tb alter column id
+DROP
+ DEFAULT;
diff --git a/ast/testdata/TestTokenize.golden b/ast/testdata/TestTokenize.golden
index b8abe22ea9a0b16bfe914fe826d2a2fbdc75b014..52d2c10834491c7f86b32eb9814da8eb542fe4e5 100644
--- a/ast/testdata/TestTokenize.golden
+++ b/ast/testdata/TestTokenize.golden
@@ -160,9 +160,11 @@ alter table inventory add index `idx_store_film` (`store_id`,`film_id`);
[{5 ALTER TABLE 0} {1 inventory 0} {5 ADD 0} {1 index 0} {3 `idx_store_film` 0} {0 0} {7 ( 0} {3 `store_id` 0} {7 , 0} {3 `film_id` 0} {7 ) 0} {7 ; 0}]
alter table inventory add index `idx_store_film` (`store_id`,`film_id`),add index `idx_store_film` (`store_id`,`film_id`),add index `idx_store_film` (`store_id`,`film_id`);
[{5 ALTER TABLE 0} {1 inventory 0} {5 ADD 0} {1 index 0} {3 `idx_store_film` 0} {0 0} {7 ( 0} {3 `store_id` 0} {7 , 0} {3 `film_id` 0} {7 ) 0} {7 , 0} {5 ADD 0} {1 index 0} {3 `idx_store_film` 0} {0 0} {7 ( 0} {3 `store_id` 0} {7 , 0} {3 `film_id` 0} {7 ) 0} {7 , 0} {5 ADD 0} {1 index 0} {3 `idx_store_film` 0} {0 0} {7 ( 0} {3 `store_id` 0} {7 , 0} {3 `film_id` 0} {7 ) 0} {7 ; 0}]
-SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d')
-[{5 SELECT 0} {4 DATE_FORMAT( 0} {1 t. 0} {1 atm, 0} {0 0} {2 '%Y-%m-%d' 0} {7 ) 0} {7 , 0} {0 0} {4 COUNT( 0} {1 DISTINCT 0} {7 ( 0} {1 t. 0} {1 usr) 0} {7 ) 0} {0 0} {5 FROM 0} {1 usr_terminal 0} {1 t 0} {5 WHERE 0} {1 t. 0} {1 atm 0} {7 > 0} {0 0} {2 '2018-10-22 00:00:00' 0} {0 0} {6 AND 0} {1 t. 0} {1 agent 0} {1 LIKE 0} {2 '%Chrome%' 0} {0 0} {6 AND 0} {1 t. 0} {1 system 0} {7 = 0} {0 0} {2 'eip' 0} {0 0} {5 GROUP BY 0} {4 DATE_FORMAT( 0} {1 t. 0} {1 atm, 0} {0 0} {2 '%Y-%m-%d' 0} {7 ) 0} {0 0} {5 ORDER BY 0} {4 DATE_FORMAT( 0} {1 t. 0} {1 atm, 0} {0 0} {2 '%Y-%m-%d' 0} {7 ) 0}]
+SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d');
+[{5 SELECT 0} {4 DATE_FORMAT( 0} {1 t. 0} {1 atm, 0} {0 0} {2 '%Y-%m-%d' 0} {7 ) 0} {7 , 0} {0 0} {4 COUNT( 0} {1 DISTINCT 0} {7 ( 0} {1 t. 0} {1 usr) 0} {7 ) 0} {0 0} {5 FROM 0} {1 usr_terminal 0} {1 t 0} {5 WHERE 0} {1 t. 0} {1 atm 0} {7 > 0} {0 0} {2 '2018-10-22 00:00:00' 0} {0 0} {6 AND 0} {1 t. 0} {1 agent 0} {1 LIKE 0} {2 '%Chrome%' 0} {0 0} {6 AND 0} {1 t. 0} {1 system 0} {7 = 0} {0 0} {2 'eip' 0} {0 0} {5 GROUP BY 0} {4 DATE_FORMAT( 0} {1 t. 0} {1 atm, 0} {0 0} {2 '%Y-%m-%d' 0} {7 ) 0} {0 0} {5 ORDER BY 0} {4 DATE_FORMAT( 0} {1 t. 0} {1 atm, 0} {0 0} {2 '%Y-%m-%d' 0} {7 ) 0} {7 ; 0}]
create table hello.t (id int unsigned);
[{1 create 0} {1 table 0} {1 hello. 0} {1 t 0} {7 ( 0} {1 id 0} {1 int 0} {1 unsigned) 0} {7 ; 0}]
-select * from tb where data >= ''
-[{5 SELECT 0} {7 * 0} {0 0} {5 FROM 0} {1 tb 0} {5 WHERE 0} {1 data 0} {7 >= 0} {0 0} {2 '' 0}]
+select * from tb where data >= '';
+[{5 SELECT 0} {7 * 0} {0 0} {5 FROM 0} {1 tb 0} {5 WHERE 0} {1 data 0} {7 >= 0} {0 0} {2 '' 0} {7 ; 0}]
+alter table tb alter column id drop default;
+[{5 ALTER TABLE 0} {1 tb 0} {1 alter 0} {1 column 0} {1 id 0} {5 DROP 0} {4 DEFAULT; 0}]
diff --git a/cmd/soar/soar.go b/cmd/soar/soar.go
index b393ce3c2f4c0f2b442bd1d09716a7556fbfa6aa..3d26a72809dc80d784962e4b9ff3e618f2bd6708 100644
--- a/cmd/soar/soar.go
+++ b/cmd/soar/soar.go
@@ -187,8 +187,6 @@ func main() {
common.Log.Warning(errContent)
if common.Config.OnlySyntaxCheck {
fmt.Println(errContent)
- }
- if !common.Config.DryRun {
os.Exit(1)
}
// tidb parser 语法检查给出的建议 ERR.000
diff --git a/common/cases.go b/common/cases.go
index 79cda1384a82deee20d7d6ea0f5d957392d731ca..47e513b1de5cd9e59d2440d076ecbc9778be2d98 100644
--- a/common/cases.go
+++ b/common/cases.go
@@ -196,11 +196,14 @@ func init() {
"alter table inventory add index `idx_store_film` (`store_id`,`film_id`),add index `idx_store_film` (`store_id`,`film_id`),add index `idx_store_film` (`store_id`,`film_id`);",
// https://github.com/XiaoMi/soar/issues/47
- `SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d')`,
+ `SELECT DATE_FORMAT(t.atm, '%Y-%m-%d'), COUNT(DISTINCT (t.usr)) FROM usr_terminal t WHERE t.atm > '2018-10-22 00:00:00' AND t.agent LIKE '%Chrome%' AND t.system = 'eip' GROUP BY DATE_FORMAT(t.atm, '%Y-%m-%d') ORDER BY DATE_FORMAT(t.atm, '%Y-%m-%d');`,
// https://github.com/XiaoMi/soar/issues/17
"create table hello.t (id int unsigned);",
// https://github.com/XiaoMi/soar/issues/146
- "select * from tb where data >= ''",
+ "select * from tb where data >= '';",
+
+ // https://github.com/XiaoMi/soar/issues/163
+ "alter table tb alter column id drop default;",
}
}
diff --git a/common/config.go b/common/config.go
index 090d3b60a427d53109e5098beed3b7a898c13f9e..32e83cdfe0032e5dd3109bcf8fae0f912451d6b9 100644
--- a/common/config.go
+++ b/common/config.go
@@ -110,6 +110,7 @@ type Configuration struct {
MaxSubqueryDepth int `yaml:"max-subquery-depth"` // 子查询最大尝试
MaxVarcharLength int `yaml:"max-varchar-length"` // varchar最大长度
ColumnNotAllowType []string `yaml:"column-not-allow-type"` // 字段不允许使用的数据类型
+ MinCardinality float64 `yaml:"min-cardinality"` // 添加索引散粒度阈值,范围 0~100
// ++++++++++++++EXPLAIN检查项+++++++++++++
ExplainSQLReportType string `yaml:"explain-sql-report-type"` // EXPLAIN markdown 格式输出 SQL 样式,支持 sample, fingerprint, pretty 等
@@ -163,6 +164,7 @@ var Config = &Configuration{
ConnTimeOut: 3,
QueryTimeOut: 30,
Delimiter: ";",
+ MinCardinality: 0,
MaxJoinTableCount: 5,
MaxGroupByColsCount: 5,
@@ -512,6 +514,7 @@ func readCmdFlags() error {
connTimeOut := flag.Int("conn-time-out", Config.ConnTimeOut, "ConnTimeOut, 数据库连接超时时间,单位秒")
queryTimeOut := flag.Int("query-time-out", Config.QueryTimeOut, "QueryTimeOut, 数据库SQL执行超时时间,单位秒")
delimiter := flag.String("delimiter", Config.Delimiter, "Delimiter, SQL分隔符")
+ minCardinality := flag.Float64("min-cardinality", Config.MinCardinality, "MinCardinality,索引列散粒度最低阈值,散粒度低于该值的列不添加索引,建议范围0.0 ~ 100.0")
// +++++++++++++++日志相关+++++++++++++++++
logLevel := flag.Int("log-level", Config.LogLevel, "LogLevel, 日志级别, [0:Emergency, 1:Alert, 2:Critical, 3:Error, 4:Warning, 5:Notice, 6:Informational, 7:Debug]")
logOutput := flag.String("log-output", Config.LogOutput, "LogOutput, 日志输出位置")
@@ -612,6 +615,7 @@ func readCmdFlags() error {
Config.IgnoreRules = strings.Split(*ignoreRules, ",")
Config.RewriteRules = strings.Split(*rewriteRules, ",")
*blackList = strings.TrimSpace(*blackList)
+ Config.MinCardinality = *minCardinality
if filepath.IsAbs(*blackList) || *blackList == "" {
Config.BlackList = *blackList
diff --git a/common/testdata/TestPrintConfiguration.golden b/common/testdata/TestPrintConfiguration.golden
index 759c35371bc26829f74f1f115d877594465ce5a1..71891c1f85115cf0566c6fcc04d7d9af184fe1e5 100644
--- a/common/testdata/TestPrintConfiguration.golden
+++ b/common/testdata/TestPrintConfiguration.golden
@@ -71,6 +71,7 @@ max-subquery-depth: 5
max-varchar-length: 1024
column-not-allow-type:
- boolean
+min-cardinality: 0
explain-sql-report-type: pretty
explain-type: extended
explain-format: traditional
diff --git a/deps.sh b/deps.sh
index afeb3e08d6aff7e50d5f05428b93d0852015a828..d1a66c29692905c3fe8ccefa635f87c11d46879a 100755
--- a/deps.sh
+++ b/deps.sh
@@ -10,3 +10,22 @@ for cmd in ${NEEDED_COMMANDS} ; do
echo "${cmd} found"
fi
done
+
+# MySQL client
+## Mac OS: brew install mysql
+## Ubuntu: apt-get install mysql-client
+
+# Docker
+## https://www.docker.com
+
+# Git
+## https://git-scm.com/
+
+# Go
+## https://golang.org/
+
+# Govendor
+## go get github.com/kardianos/govendor
+
+# retool
+## go get github.com/twitchtv/retool
diff --git a/doc/example/main_test.md b/doc/example/main_test.md
index 149da347c968b5e1e594b563d5e3fbc35d0cb032..1ff04158d22a5d6454361b7ce782af0cdd9685a9 100644
--- a/doc/example/main_test.md
+++ b/doc/example/main_test.md
@@ -1,6 +1,6 @@
# Query: C3FAEDA6AD6D762B
-★ ★ ★ ★ ☆ 85分
+★ ★ ★ ★ ☆ 95分
```sql
@@ -12,52 +12,17 @@ WHERE
LENGTH = 86
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 10.00% | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: E969B9297DA79BA6
-★ ★ ★ ★ ☆ 80分
+★ ★ ★ ★ ☆ 90分
```sql
@@ -69,56 +34,21 @@ WHERE
LENGTH IS NULL
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 10.00% | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
-
-
-
-## 应尽量避免在WHERE子句中对字段进行NULL值判断
+## 应尽量避免在 WHERE 子句中对字段进行 NULL 值判断
* **Item:** ARG.006
* **Severity:** L1
-* **Content:** 使用IS NULL或IS NOT NULL将可能导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null;可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0;
+* **Content:** 使用 IS NULL 或 IS NOT NULL 将可能导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null;可以在num上设置默认值0,确保表中 num 列没有 NULL 值,然后这样查询: select id from t where num=0;
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: 8A106444D14B9880
@@ -134,52 +64,33 @@ HAVING
title = 'abc'
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## 不建议使用HAVING子句
+## 不建议使用 HAVING 子句
* **Item:** CLA.013
* **Severity:** L3
-* **Content:** 将查询的HAVING子句改写为WHERE中的查询条件,可以在查询处理期间使用索引。
+* **Content:** 将查询的 HAVING 子句改写为 WHERE 中的查询条件,可以在查询处理期间使用索引。
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: A0C5E62C724A121A
-★ ★ ★ ★ ☆ 85分
+★ ★ ★ ★ ☆ 95分
```sql
@@ -191,52 +102,17 @@ WHERE
LENGTH >= 60
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 33.33% | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: 868317D1973FD1B0
-★ ★ ★ ★ ☆ 85分
+★ ★ ★ ★ ☆ 95分
```sql
@@ -249,48 +125,13 @@ WHERE
AND 84
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 11.11% | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: 707FE669669FA075
@@ -306,38 +147,13 @@ WHERE
title LIKE 'AIR%'
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | range | idx\_title | idx\_title | 767 | | 2 | ☠️ **100.00%** | ☠️ **O(n)** | Using index condition |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* **range**: 只检索给定范围的行, 使用一个索引来选择行. key列显示使用了哪个索引. key_len包含所使用索引的最长关键元素.
-
-#### Extra信息解读
-
-* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
-
-* **Using index condition**: 在5.6版本后加入的新特性(Index Condition Pushdown)。Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行。
-
-
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: DF916439ABD07664
@@ -353,44 +169,21 @@ WHERE
title IS NOT NULL
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | idx\_title | NULL | | | 1000 | 90.00% | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 应尽量避免在WHERE子句中对字段进行NULL值判断
+## 应尽量避免在 WHERE 子句中对字段进行 NULL 值判断
* **Item:** ARG.006
* **Severity:** L1
-* **Content:** 使用IS NULL或IS NOT NULL将可能导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null;可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0;
+* **Content:** 使用 IS NULL 或 IS NOT NULL 将可能导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null;可以在num上设置默认值0,确保表中 num 列没有 NULL 值,然后这样查询: select id from t where num=0;
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: B9336971FF3D3792
@@ -407,48 +200,21 @@ WHERE
AND title = 'ALABAMA DEVIL'
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ref | idx\_title | idx\_title | 767 | const | 1 | 10.00% | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
+## 不建议使用 SELECT * 类型查询
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-#### Extra信息解读
+* **Item:** COL.001
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+* **Severity:** L1
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 为sakila库的film表添加索引
+## 不建议使用连续判断
-* **Item:** IDX.001
+* **Item:** RES.009
* **Severity:** L2
-* **Content:** 为列title添加索引,散粒度为: 100.00%; 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_title\_length\` (\`title\`,\`length\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
# Query: 68E48001ECD53152
@@ -465,48 +231,21 @@ WHERE
AND title = 'ALABAMA DEVIL'
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ref | idx\_title | idx\_title | 767 | const | 1 | 33.33% | ☠️ **O(n)** | Using where |
-
+## 不建议使用 SELECT * 类型查询
+* **Item:** COL.001
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+* **Severity:** L1
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 为sakila库的film表添加索引
+## 不建议使用连续判断
-* **Item:** IDX.001
+* **Item:** RES.009
* **Severity:** L2
-* **Content:** 为列title添加索引,散粒度为: 100.00%; 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_title\_length\` (\`title\`,\`length\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
# Query: 12FF1DAA3D425FA9
@@ -524,48 +263,21 @@ WHERE
AND title = 'xyz'
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ref | idx\_title,
idx\_fk\_language\_id | idx\_title | 767 | const | 1 | 33.33% | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
+## 不建议使用 SELECT * 类型查询
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-#### Extra信息解读
+* **Item:** COL.001
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+* **Severity:** L1
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 为sakila库的film表添加索引
+## 不建议使用连续判断
-* **Item:** IDX.001
+* **Item:** RES.009
* **Severity:** L2
-* **Content:** 为列title添加索引,散粒度为: 100.00%; 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_title\_length\` (\`title\`,\`length\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
# Query: E84CBAAC2E12BDEA
@@ -582,52 +294,25 @@ WHERE
AND language_id < 10
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | idx\_fk\_language\_id | NULL | | | 1000 | 33.33% | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
+## 不建议使用 SELECT * 类型查询
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
+* **Item:** COL.001
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+* **Severity:** L1
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 为sakila库的film表添加索引
+## 不建议使用连续判断
-* **Item:** IDX.001
+* **Item:** RES.009
* **Severity:** L2
-* **Content:** 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
# Query: 6A0F035BD4E01018
-★ ★ ★ ★ ☆ 80分
+★ ★ ★ ☆ ☆ 75分
```sql
@@ -642,54 +327,33 @@ GROUP BY
release_year
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | idx\_fk\_language\_id | NULL | | | 1000 | 10.00% | ☠️ **O(n)** | Using where; Using temporary |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
+## 请为 GROUP BY 显示添加 ORDER BY 条件
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
+* **Item:** CLA.008
* **Severity:** L2
-* **Content:** 为列length添加索引,散粒度为: 14.00%; 为列language\_id添加索引,散粒度为: 0.10%; 为列release\_year添加索引,散粒度为: 0.10%;
+* **Content:** 默认 MySQL 会对 'GROUP BY col1, col2, ...' 请求按如下顺序排序 'ORDER BY col1, col2, ...'。如果 GROUP BY 语句不指定 ORDER BY 条件会导致无谓的排序产生,如果不需要排序建议添加 'ORDER BY NULL'。
+
+## 使用 SUM(COL) 时需注意 NPE 问题
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_language\_id\_release\_year\` (\`length\`,\`language\_id\`,\`release\_year\`) ;
+* **Item:** FUN.006
+* **Severity:** L1
+* **Content:** 当某一列的值全是 NULL 时,COUNT(COL) 的返回结果为0,但 SUM(COL) 的返回结果为 NULL,因此使用 SUM() 时需注意 NPE 问题。可以使用如下方式来避免 SUM 的 NPE 问题: SELECT IF(ISNULL(SUM(COL)), 0, SUM(COL)) FROM tbl
-## 请为GROUP BY显示添加ORDER BY条件
+## 不建议使用连续判断
-* **Item:** CLA.008
+* **Item:** RES.009
* **Severity:** L2
-* **Content:** 默认MySQL会对'GROUP BY col1, col2, ...'请求按如下顺序排序'ORDER BY col1, col2, ...'。如果GROUP BY语句不指定ORDER BY条件会导致无谓的排序产生,如果不需要排序建议添加'ORDER BY NULL'。
+* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
# Query: 23D176AEA2947002
-★ ★ ★ ★ ☆ 80分
+★ ★ ★ ★ ☆ 85分
```sql
@@ -703,188 +367,103 @@ GROUP BY
release_year
```
-## Explain信息
+## 请为 GROUP BY 显示添加 ORDER BY 条件
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 33.33% | ☠️ **O(n)** | Using where; Using temporary |
+* **Item:** CLA.008
+* **Severity:** L2
+* **Content:** 默认 MySQL 会对 'GROUP BY col1, col2, ...' 请求按如下顺序排序 'ORDER BY col1, col2, ...'。如果 GROUP BY 语句不指定 ORDER BY 条件会导致无谓的排序产生,如果不需要排序建议添加 'ORDER BY NULL'。
-### Explain信息解读
+## 使用 SUM(COL) 时需注意 NPE 问题
-#### SelectType信息解读
+* **Item:** FUN.006
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+* **Severity:** L1
-#### Type信息解读
+* **Content:** 当某一列的值全是 NULL 时,COUNT(COL) 的返回结果为0,但 SUM(COL) 的返回结果为 NULL,因此使用 SUM() 时需注意 NPE 问题。可以使用如下方式来避免 SUM 的 NPE 问题: SELECT IF(ISNULL(SUM(COL)), 0, SUM(COL)) FROM tbl
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+# Query: 73DDF6E6D9E40384
-#### Extra信息解读
+★ ★ ★ ☆ ☆ 65分
-* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
+```sql
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+SELECT
+ release_year, language_id, SUM( LENGTH)
+FROM
+ film
+GROUP BY
+ release_year, language_id
+```
+## 最外层 SELECT 未指定 WHERE 条件
-## 为sakila库的film表添加索引
+* **Item:** CLA.001
-* **Item:** IDX.001
+* **Severity:** L4
-* **Severity:** L2
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-* **Content:** 为列length添加索引,散粒度为: 14.00%; 为列release\_year添加索引,散粒度为: 0.10%;
+## 请为 GROUP BY 显示添加 ORDER BY 条件
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_release\_year\` (\`length\`,\`release\_year\`) ;
+* **Item:** CLA.008
+* **Severity:** L2
+* **Content:** 默认 MySQL 会对 'GROUP BY col1, col2, ...' 请求按如下顺序排序 'ORDER BY col1, col2, ...'。如果 GROUP BY 语句不指定 ORDER BY 条件会导致无谓的排序产生,如果不需要排序建议添加 'ORDER BY NULL'。
-## 请为GROUP BY显示添加ORDER BY条件
+## 使用 SUM(COL) 时需注意 NPE 问题
-* **Item:** CLA.008
+* **Item:** FUN.006
-* **Severity:** L2
+* **Severity:** L1
-* **Content:** 默认MySQL会对'GROUP BY col1, col2, ...'请求按如下顺序排序'ORDER BY col1, col2, ...'。如果GROUP BY语句不指定ORDER BY条件会导致无谓的排序产生,如果不需要排序建议添加'ORDER BY NULL'。
+* **Content:** 当某一列的值全是 NULL 时,COUNT(COL) 的返回结果为0,但 SUM(COL) 的返回结果为 NULL,因此使用 SUM() 时需注意 NPE 问题。可以使用如下方式来避免 SUM 的 NPE 问题: SELECT IF(ISNULL(SUM(COL)), 0, SUM(COL)) FROM tbl
-# Query: 73DDF6E6D9E40384
+# Query: B3C502B4AA344196
-★ ★ ★ ☆ ☆ 60分
+★ ★ ★ ☆ ☆ 75分
```sql
SELECT
- release_year, language_id, SUM( LENGTH)
+ release_year, SUM( LENGTH)
FROM
film
+WHERE
+ LENGTH = 123
GROUP BY
- release_year, language_id
+ release_year, (LENGTH+ language_id)
```
-## Explain信息
+## 请为 GROUP BY 显示添加 ORDER BY 条件
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using temporary |
+* **Item:** CLA.008
+* **Severity:** L2
+* **Content:** 默认 MySQL 会对 'GROUP BY col1, col2, ...' 请求按如下顺序排序 'ORDER BY col1, col2, ...'。如果 GROUP BY 语句不指定 ORDER BY 条件会导致无谓的排序产生,如果不需要排序建议添加 'ORDER BY NULL'。
-### Explain信息解读
+## GROUP BY 的条件为表达式
-#### SelectType信息解读
+* **Item:** CLA.010
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+* **Severity:** L2
-#### Type信息解读
+* **Content:** 当 GROUP BY 条件为表达式或函数时会使用到临时表,如果在未指定 WHERE 或 WHERE 条件返回的结果集较大时性能会很差。
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+## 使用 SUM(COL) 时需注意 NPE 问题
-#### Extra信息解读
+* **Item:** FUN.006
-* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
+* **Severity:** L1
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列release\_year添加索引,散粒度为: 0.10%; 为列language\_id添加索引,散粒度为: 0.10%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_release\_year\_language\_id\` (\`release\_year\`,\`language\_id\`) ;
-
-
-
-## SELECT未指定WHERE条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
-
-## 请为GROUP BY显示添加ORDER BY条件
-
-* **Item:** CLA.008
-
-* **Severity:** L2
-
-* **Content:** 默认MySQL会对'GROUP BY col1, col2, ...'请求按如下顺序排序'ORDER BY col1, col2, ...'。如果GROUP BY语句不指定ORDER BY条件会导致无谓的排序产生,如果不需要排序建议添加'ORDER BY NULL'。
-
-# Query: B3C502B4AA344196
-
-★ ★ ★ ☆ ☆ 70分
-
-```sql
-
-SELECT
- release_year, SUM( LENGTH)
-FROM
- film
-WHERE
- LENGTH = 123
-GROUP BY
- release_year, (LENGTH+ language_id)
-```
-
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 10.00% | ☠️ **O(n)** | Using where; Using temporary |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
-
-
-
-## 请为GROUP BY显示添加ORDER BY条件
-
-* **Item:** CLA.008
-
-* **Severity:** L2
-
-* **Content:** 默认MySQL会对'GROUP BY col1, col2, ...'请求按如下顺序排序'ORDER BY col1, col2, ...'。如果GROUP BY语句不指定ORDER BY条件会导致无谓的排序产生,如果不需要排序建议添加'ORDER BY NULL'。
-
-## GROUP BY的条件为表达式
-
-* **Item:** CLA.010
-
-* **Severity:** L2
-
-* **Content:** 当GROUP BY条件为表达式或函数时会使用到临时表,如果在未指定WHERE或WHERE条件返回的结果集较大时性能会很差。
+* **Content:** 当某一列的值全是 NULL 时,COUNT(COL) 的返回结果为0,但 SUM(COL) 的返回结果为 NULL,因此使用 SUM() 时需注意 NPE 问题。可以使用如下方式来避免 SUM 的 NPE 问题: SELECT IF(ISNULL(SUM(COL)), 0, SUM(COL)) FROM tbl
# Query: 47044E1FE1A965A5
-★ ★ ★ ☆ ☆ 60分
+★ ★ ★ ☆ ☆ 70分
```sql
@@ -896,60 +475,25 @@ GROUP BY
release_year
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using temporary |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列release\_year添加索引,散粒度为: 0.10%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_release\_year\` (\`release\_year\`) ;
-
-
-
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## 请为GROUP BY显示添加ORDER BY条件
+## 请为 GROUP BY 显示添加 ORDER BY 条件
* **Item:** CLA.008
* **Severity:** L2
-* **Content:** 默认MySQL会对'GROUP BY col1, col2, ...'请求按如下顺序排序'ORDER BY col1, col2, ...'。如果GROUP BY语句不指定ORDER BY条件会导致无谓的排序产生,如果不需要排序建议添加'ORDER BY NULL'。
+* **Content:** 默认 MySQL 会对 'GROUP BY col1, col2, ...' 请求按如下顺序排序 'ORDER BY col1, col2, ...'。如果 GROUP BY 语句不指定 ORDER BY 条件会导致无谓的排序产生,如果不需要排序建议添加 'ORDER BY NULL'。
# Query: 2BA1217F6C8CF0AB
-☆ ☆ ☆ ☆ ☆ 0分
+★ ★ ☆ ☆ ☆ 45分
```sql
@@ -961,53 +505,37 @@ GROUP BY
address, district
```
-## MySQL返回信息
-
-Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'optimizer_RSq3xBEF0TXgZsHj.address.address_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
-
-## 为sakila库的address表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列address添加索引,散粒度为: 100.00%; 为列district添加索引,散粒度为: 100.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`address\` add index \`idx\_address\_district\` (\`address\`,\`district\`) ;
-
-
-
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## 请为GROUP BY显示添加ORDER BY条件
+## 请为 GROUP BY 显示添加 ORDER BY 条件
* **Item:** CLA.008
* **Severity:** L2
-* **Content:** 默认MySQL会对'GROUP BY col1, col2, ...'请求按如下顺序排序'ORDER BY col1, col2, ...'。如果GROUP BY语句不指定ORDER BY条件会导致无谓的排序产生,如果不需要排序建议添加'ORDER BY NULL'。
+* **Content:** 默认 MySQL 会对 'GROUP BY col1, col2, ...' 请求按如下顺序排序 'ORDER BY col1, col2, ...'。如果 GROUP BY 语句不指定 ORDER BY 条件会导致无谓的排序产生,如果不需要排序建议添加 'ORDER BY NULL'。
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 非确定性的GROUP BY
+## 非确定性的 GROUP BY
* **Item:** RES.001
* **Severity:** L4
-* **Content:** SQL返回的列既不在聚合函数中也不是GROUP BY表达式的列中,因此这些值的结果将是非确定性的。如:select a, b, c from tbl where foo="bar" group by a,该SQL返回的结果就是不确定的。
+* **Content:** SQL返回的列既不在聚合函数中也不是 GROUP BY 表达式的列中,因此这些值的结果将是非确定性的。如:select a, b, c from tbl where foo="bar" group by a,该 SQL 返回的结果就是不确定的。
# Query: 863A85207E4F410D
@@ -1025,48 +553,25 @@ GROUP BY
title
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | index | idx\_title | idx\_title | 767 | | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* **index**: 全表扫描, 只是扫描表的时候按照索引次序进行而不是行. 主要优点就是避免了排序, 但是开销仍然非常大.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 请为GROUP BY显示添加ORDER BY条件
+## 请为 GROUP BY 显示添加 ORDER BY 条件
* **Item:** CLA.008
* **Severity:** L2
-* **Content:** 默认MySQL会对'GROUP BY col1, col2, ...'请求按如下顺序排序'ORDER BY col1, col2, ...'。如果GROUP BY语句不指定ORDER BY条件会导致无谓的排序产生,如果不需要排序建议添加'ORDER BY NULL'。
+* **Content:** 默认 MySQL 会对 'GROUP BY col1, col2, ...' 请求按如下顺序排序 'ORDER BY col1, col2, ...'。如果 GROUP BY 语句不指定 ORDER BY 条件会导致无谓的排序产生,如果不需要排序建议添加 'ORDER BY NULL'。
-## 避免在WHERE条件中使用函数或其他运算符
+## 避免在 WHERE 条件中使用函数或其他运算符
* **Item:** FUN.001
* **Severity:** L2
-* **Content:** 虽然在SQL中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧。
+* **Content:** 虽然在 SQL 中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧。也不建议在查询比较条件两侧书写多余的括号,这会对阅读产生比较大的困扰。
# Query: DF59FD602E4AA368
-☆ ☆ ☆ ☆ ☆ 0分
+★ ★ ★ ★ ☆ 80分
```sql
@@ -1082,33 +587,17 @@ ORDER BY
language_id
```
-## MySQL返回信息
-
-Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'optimizer_RSq3xBEF0TXgZsHj.film.language_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%; 为列release\_year添加索引,散粒度为: 0.10%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_release\_year\` (\`length\`,\`release\_year\`) ;
-
-
-
-## 非确定性的GROUP BY
+## 非确定性的 GROUP BY
* **Item:** RES.001
* **Severity:** L4
-* **Content:** SQL返回的列既不在聚合函数中也不是GROUP BY表达式的列中,因此这些值的结果将是非确定性的。如:select a, b, c from tbl where foo="bar" group by a,该SQL返回的结果就是不确定的。
+* **Content:** SQL返回的列既不在聚合函数中也不是 GROUP BY 表达式的列中,因此这些值的结果将是非确定性的。如:select a, b, c from tbl where foo="bar" group by a,该 SQL 返回的结果就是不确定的。
# Query: F6DBEAA606D800FC
-★ ★ ★ ★ ☆ 90分
+★ ★ ★ ★ ★ 100分
```sql
@@ -1124,48 +613,11 @@ ORDER BY
release_year
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 10.00% | ☠️ **O(n)** | Using where; Using temporary; Using filesort |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
-
-* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%; 为列release\_year添加索引,散粒度为: 0.10%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_release\_year\` (\`length\`,\`release\_year\`) ;
-
-
+## OK
# Query: 6E9B96CA3F0E6BDA
-★ ★ ☆ ☆ ☆ 55分
+★ ★ ★ ☆ ☆ 65分
```sql
@@ -1179,70 +631,33 @@ ORDER BY
release_year ASC, language_id DESC
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 10.00% | ☠️ **O(n)** | Using where; Using filesort |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
-
-
-
-## ORDER BY语句对多个不同条件使用不同方向的排序无法使用索引
+## ORDER BY 语句对多个不同条件使用不同方向的排序无法使用索引
* **Item:** CLA.007
* **Severity:** L2
-* **Content:** ORDER BY子句中的所有表达式必须按统一的ASC或DESC方向排序,以便利用索引。
+* **Content:** ORDER BY 子句中的所有表达式必须按统一的 ASC 或 DESC 方向排序,以便利用索引。
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## ORDER BY多个列但排序方向不同时可能无法使用索引
+## ORDER BY 多个列但排序方向不同时可能无法使用索引
* **Item:** KEY.008
* **Severity:** L4
-* **Content:** 在MySQL 8.0之前当ORDER BY多个列指定的排序方向不同时将无法使用已经建立的索引。
+* **Content:** 在 MySQL 8.0之前当 ORDER BY 多个列指定的排序方向不同时将无法使用已经建立的索引。
# Query: 2EAACFD7030EA528
-★ ★ ★ ★ ☆ 90分
+★ ★ ★ ★ ★ 100分
```sql
@@ -1260,48 +675,11 @@ LIMIT
10
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 10.00% | ☠️ **O(n)** | Using where; Using temporary; Using filesort |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
-
-* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%; 为列release\_year添加索引,散粒度为: 0.10%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_release\_year\` (\`length\`,\`release\_year\`) ;
-
-
+## OK
# Query: 5CE2F187DBF2A710
-★ ★ ★ ★ ☆ 85分
+★ ★ ★ ★ ☆ 95分
```sql
@@ -1317,54 +695,17 @@ LIMIT
10
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 10.00% | ☠️ **O(n)** | Using where; Using filesort |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%; 为列release\_year添加索引,散粒度为: 0.10%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_release\_year\` (\`length\`,\`release\_year\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: E75234155B5E2E14
-★ ★ ★ ☆ ☆ 65分
+★ ★ ★ ☆ ☆ 75分
```sql
@@ -1378,60 +719,49 @@ LIMIT
10
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using filesort |
+## 最外层 SELECT 未指定 WHERE 条件
+* **Item:** CLA.001
+* **Severity:** L4
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+## 不建议使用 SELECT * 类型查询
-## 为sakila库的film表添加索引
+* **Item:** COL.001
-* **Item:** IDX.001
+* **Severity:** L1
-* **Severity:** L2
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-* **Content:** 为列release\_year添加索引,散粒度为: 0.10%;
+# Query: AFEEBF10A8D74E32
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_release\_year\` (\`release\_year\`) ;
+★ ★ ★ ★ ☆ 80分
+```sql
+SELECT
+ film_id
+FROM
+ film
+ORDER BY
+ release_year
+LIMIT
+ 10
+```
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
-
-## 不建议使用SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
# Query: 965D5AC955824512
-★ ★ ★ ★ ☆ 85分
+★ ★ ★ ★ ☆ 95分
```sql
@@ -1447,54 +777,17 @@ LIMIT
10
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 33.33% | ☠️ **O(n)** | Using where; Using filesort |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: 1E2CF4145EE706A5
-★ ★ ★ ★ ☆ 85分
+★ ★ ★ ★ ☆ 95分
```sql
@@ -1510,54 +803,17 @@ LIMIT
10
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 33.33% | ☠️ **O(n)** | Using where; Using filesort |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: A314542EEE8571EE
-★ ★ ★ ★ ☆ 85分
+★ ★ ★ ★ ☆ 95分
```sql
@@ -1571,52 +827,13 @@ ORDER BY
last_name
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *customer* | NULL | range | idx\_fk\_address\_id | idx\_fk\_address\_id | 2 | | 2 | ☠️ **100.00%** | ☠️ **O(n)** | Using index condition; Using filesort |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* **range**: 只检索给定范围的行, 使用一个索引来选择行. key列显示使用了哪个索引. key_len包含所使用索引的最长关键元素.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
-
-* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
-
-* **Using index condition**: 在5.6版本后加入的新特性(Index Condition Pushdown)。Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行。
-
-
-## 为sakila库的customer表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列address\_id添加索引,散粒度为: 100.00%; 为列last\_name添加索引,散粒度为: 100.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`customer\` add index \`idx\_address\_id\_last\_name\` (\`address\_id\`,\`last\_name\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: 0BE2D79E2F1E7CB0
@@ -1635,50 +852,21 @@ ORDER BY
title
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 9.00% | ☠️ **O(n)** | Using where; Using filesort |
-
-
-
-### Explain信息解读
+## 不建议使用 SELECT * 类型查询
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+* **Item:** COL.001
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+* **Severity:** L1
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 为sakila库的film表添加索引
+## 不建议使用连续判断
-* **Item:** IDX.001
+* **Item:** RES.009
* **Severity:** L2
-* **Content:** 为列release\_year添加索引,散粒度为: 0.10%; 为列length添加索引,散粒度为: 14.00%; 为列title添加索引,散粒度为: 100.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_release\_year\_length\_title\` (\`release\_year\`,\`length\`,\`title\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
## '!=' 运算符是非标准的
@@ -1688,54 +876,21 @@ ORDER BY
* **Content:** "<>"才是标准SQL中的不等于运算符。
-# Query: 4E73AA068370E6A8
-
-★ ★ ★ ★ ☆ 90分
-
-```sql
-
-SELECT
- title
-FROM
- film
-WHERE
- release_year = 1995
-```
-
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 10.00% | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
+# Query: 4E73AA068370E6A8
-* **Content:** 为列release\_year添加索引,散粒度为: 0.10%;
+★ ★ ★ ★ ★ 100分
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_release\_year\` (\`release\_year\`) ;
+```sql
+SELECT
+ title
+FROM
+ film
+WHERE
+ release_year = 1995
+```
+## OK
# Query: BA7111449E4F1122
@@ -1752,40 +907,13 @@ WHERE
AND LENGTH = 70
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ref | idx\_fk\_language\_id | idx\_fk\_language\_id | 1 | const | 1 | 10.00% | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+## 不建议使用连续判断
-#### Type信息解读
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
+* **Item:** RES.009
* **Severity:** L2
-* **Content:** 为列length添加索引,散粒度为: 14.00%; 为列language\_id添加索引,散粒度为: 0.10%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_language\_id\` (\`length\`,\`language\_id\`) ;
-
-
+* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
# Query: B13E0ACEAF8F3119
@@ -1802,44 +930,13 @@ WHERE
AND LENGTH > 70
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | range | idx\_fk\_language\_id | idx\_fk\_language\_id | 1 | | 1 | 33.33% | ☠️ **O(n)** | Using index condition; Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
+## 不建议使用连续判断
-* **range**: 只检索给定范围的行, 使用一个索引来选择行. key列显示使用了哪个索引. key_len包含所使用索引的最长关键元素.
-
-#### Extra信息解读
-
-* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
-
-* **Using index condition**: 在5.6版本后加入的新特性(Index Condition Pushdown)。Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行。
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
+* **Item:** RES.009
* **Severity:** L2
-* **Content:** 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
-
-
+* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
# Query: A3FAB6027484B88B
@@ -1858,54 +955,21 @@ ORDER BY
release_year
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ref | idx\_title | idx\_title | 767 | const | 1 | 10.00% | ☠️ **O(n)** | Using index condition; Using where; Using filesort |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+## 不建议使用 SELECT * 类型查询
-#### Type信息解读
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
-
-* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
-
-* **Using index condition**: 在5.6版本后加入的新特性(Index Condition Pushdown)。Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行。
+* **Item:** COL.001
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+* **Severity:** L1
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 为sakila库的film表添加索引
+## 不建议使用连续判断
-* **Item:** IDX.001
+* **Item:** RES.009
* **Severity:** L2
-* **Content:** 为列title添加索引,散粒度为: 100.00%; 为列length添加索引,散粒度为: 14.00%; 为列release\_year添加索引,散粒度为: 0.10%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_title\_length\_release\_year\` (\`title\`,\`length\`,\`release\_year\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
# Query: CB42080E9F35AB07
@@ -1924,58 +988,25 @@ ORDER BY
release_year
```
-## Explain信息
+## 不建议使用 SELECT * 类型查询
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ref | idx\_title | idx\_title | 767 | const | 1 | 33.33% | ☠️ **O(n)** | Using index condition; Using where; Using filesort |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
-
-* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
-
-* **Using index condition**: 在5.6版本后加入的新特性(Index Condition Pushdown)。Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行。
+* **Item:** COL.001
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+* **Severity:** L1
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 为sakila库的film表添加索引
+## 不建议使用连续判断
-* **Item:** IDX.001
+* **Item:** RES.009
* **Severity:** L2
-* **Content:** 为列title添加索引,散粒度为: 100.00%; 为列length添加索引,散粒度为: 14.00%; 为列release\_year添加索引,散粒度为: 0.10%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_title\_length\_release\_year\` (\`title\`,\`length\`,\`release\_year\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
# Query: C4A212A42400411D
-★ ★ ★ ★ ☆ 85分
+★ ★ ★ ★ ☆ 95分
```sql
@@ -1989,50 +1020,13 @@ ORDER BY
release_year
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 33.33% | ☠️ **O(n)** | Using where; Using filesort |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%; 为列release\_year添加索引,散粒度为: 0.10%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_release\_year\` (\`length\`,\`release\_year\`) ;
-
-
-
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: 4ECCA9568BE69E68
@@ -2047,51 +1041,29 @@ FROM
INNER JOIN country b ON a. country_id= b. country_id
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *b* | NULL | ALL | PRIMARY | NULL | | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *a* | NULL | ref | idx\_fk\_country\_id | idx\_fk\_country\_id | 2 | sakila.b.country\_id | 5 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-
-## 建议使用AS关键字显示声明一个别名
+## 建议使用 AS 关键字显示声明一个别名
* **Item:** ALI.001
* **Severity:** L0
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用AS关键字比隐含别名(如"tbl alias")更易懂。
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: 485D56FC88BBBDB9
@@ -2106,51 +1078,29 @@ FROM
LEFT JOIN country b ON a. country_id= b. country_id
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *a* | NULL | ALL | | NULL | | | 600 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *b* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.a.country\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例:'SELECT * FROM ref_table,tbl WHERE ref_table.key_column=tbl.column;'.
-
-
-## 建议使用AS关键字显示声明一个别名
+## 建议使用 AS 关键字显示声明一个别名
* **Item:** ALI.001
* **Severity:** L0
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用AS关键字比隐含别名(如"tbl alias")更易懂。
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: 0D0DABACEDFF5765
@@ -2165,55 +1115,33 @@ FROM
RIGHT JOIN country b ON a. country_id= b. country_id
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *b* | NULL | ALL | | NULL | | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *a* | NULL | ref | idx\_fk\_country\_id | idx\_fk\_country\_id | 2 | sakila.b.country\_id | 5 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-
-## 建议使用AS关键字显示声明一个别名
+## 建议使用 AS 关键字显示声明一个别名
* **Item:** ALI.001
* **Severity:** L0
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用AS关键字比隐含别名(如"tbl alias")更易懂。
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: 1E56C6CCEA2131CC
-★ ★ ★ ★ ☆ 80分
+★ ★ ★ ★ ☆ 90分
```sql
@@ -2226,73 +1154,33 @@ WHERE
b. last_update IS NULL
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *a* | NULL | ALL | | NULL | | | 600 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *b* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.a.country\_id | 1 | 10.00% | ☠️ **O(n)** | Using where; Not exists |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例:'SELECT * FROM ref_table,tbl WHERE ref_table.key_column=tbl.column;'.
-
-#### Extra信息解读
-
-* **Not exists**: MySQL能够对LEFT JOIN查询进行优化, 并且在查找到符合LEFT JOIN条件的行后, 则不再查找更多的行.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的country表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列last\_update添加索引,散粒度为: 0.92%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`country\` add index \`idx\_last\_update\` (\`last\_update\`) ;
-
-
-
-## 建议使用AS关键字显示声明一个别名
+## 建议使用 AS 关键字显示声明一个别名
* **Item:** ALI.001
* **Severity:** L0
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用AS关键字比隐含别名(如"tbl alias")更易懂。
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-## 应尽量避免在WHERE子句中对字段进行NULL值判断
+## 应尽量避免在 WHERE 子句中对字段进行 NULL 值判断
* **Item:** ARG.006
* **Severity:** L1
-* **Content:** 使用IS NULL或IS NOT NULL将可能导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null;可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0;
+* **Content:** 使用 IS NULL 或 IS NOT NULL 将可能导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null;可以在num上设置默认值0,确保表中 num 列没有 NULL 值,然后这样查询: select id from t where num=0;
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: F5D30BCAC1E206A1
-★ ★ ★ ★ ☆ 80分
+★ ★ ★ ★ ☆ 90分
```sql
@@ -2305,69 +1193,29 @@ WHERE
a. last_update IS NULL
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *b* | NULL | ALL | | NULL | | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *a* | NULL | ref | idx\_fk\_country\_id | idx\_fk\_country\_id | 2 | sakila.b.country\_id | 5 | 10.00% | ☠️ **O(n)** | Using where; Not exists |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-#### Extra信息解读
-
-* **Not exists**: MySQL能够对LEFT JOIN查询进行优化, 并且在查找到符合LEFT JOIN条件的行后, 则不再查找更多的行.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的city表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列last\_update添加索引,散粒度为: 0.17%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`city\` add index \`idx\_last\_update\` (\`last\_update\`) ;
-
-
-
-## 建议使用AS关键字显示声明一个别名
+## 建议使用 AS 关键字显示声明一个别名
* **Item:** ALI.001
* **Severity:** L0
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用AS关键字比隐含别名(如"tbl alias")更易懂。
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-## 应尽量避免在WHERE子句中对字段进行NULL值判断
+## 应尽量避免在 WHERE 子句中对字段进行 NULL 值判断
* **Item:** ARG.006
* **Severity:** L1
-* **Content:** 使用IS NULL或IS NOT NULL将可能导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null;可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0;
+* **Content:** 使用 IS NULL 或 IS NOT NULL 将可能导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null;可以在num上设置默认值0,确保表中 num 列没有 NULL 值,然后这样查询: select id from t where num=0;
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
# Query: 17D5BCF21DC2364C
@@ -2388,66 +1236,31 @@ FROM
RIGHT JOIN country b ON a. country_id= b. country_id
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | PRIMARY | *a* | NULL | ALL | | NULL | | | 600 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | PRIMARY | *b* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.a.country\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 2 | UNION | *b* | NULL | ALL | | NULL | | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 2 | UNION | *a* | NULL | ref | idx\_fk\_country\_id | idx\_fk\_country\_id | 2 | sakila.b.country\_id | 5 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 0 | UNION RESULT | ** | NULL | ALL | | NULL | | | 0 | 0.00% | ☠️ **O(n)** | Using temporary |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **PRIMARY**: 最外层的select.
-
-* **UNION**: UNION中的第二个或后面的SELECT查询, 不依赖于外部查询的结果集.
-
-* **UNION RESULT**: UNION查询的结果集.
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例:'SELECT * FROM ref_table,tbl WHERE ref_table.key_column=tbl.column;'.
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-#### Extra信息解读
-
-* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
-
-
-## 建议使用AS关键字显示声明一个别名
+## 建议使用 AS 关键字显示声明一个别名
* **Item:** ALI.001
* **Severity:** L0
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用AS关键字比隐含别名(如"tbl alias")更易懂。
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 如果您不在乎重复的话,建议使用UNION ALL替代UNION
+## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
* **Item:** SUB.002
@@ -2457,7 +1270,7 @@ FROM
# Query: A4911095C201896F
-★ ★ ★ ☆ ☆ 65分
+★ ★ ★ ★ ☆ 85分
```sql
@@ -2478,86 +1291,23 @@ WHERE
b. last_update IS NULL
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | PRIMARY | *b* | NULL | ALL | | NULL | | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | PRIMARY | *a* | NULL | ref | idx\_fk\_country\_id | idx\_fk\_country\_id | 2 | sakila.b.country\_id | 5 | 10.00% | ☠️ **O(n)** | Using where; Not exists |
-| 2 | UNION | *a* | NULL | ALL | | NULL | | | 600 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 2 | UNION | *b* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.a.country\_id | 1 | 10.00% | ☠️ **O(n)** | Using where; Not exists |
-| 0 | UNION RESULT | ** | NULL | ALL | | NULL | | | 0 | 0.00% | ☠️ **O(n)** | Using temporary |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **PRIMARY**: 最外层的select.
-
-* **UNION**: UNION中的第二个或后面的SELECT查询, 不依赖于外部查询的结果集.
-
-* **UNION RESULT**: UNION查询的结果集.
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例:'SELECT * FROM ref_table,tbl WHERE ref_table.key_column=tbl.column;'.
-
-#### Extra信息解读
-
-* **Not exists**: MySQL能够对LEFT JOIN查询进行优化, 并且在查找到符合LEFT JOIN条件的行后, 则不再查找更多的行.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
-
-
-## 为sakila库的city表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列last\_update添加索引,散粒度为: 0.17%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`city\` add index \`idx\_last\_update\` (\`last\_update\`) ;
-
-
-
-## 为sakila库的country表添加索引
-
-* **Item:** IDX.002
-
-* **Severity:** L2
-
-* **Content:** 为列last\_update添加索引,散粒度为: 0.92%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`country\` add index \`idx\_last\_update\` (\`last\_update\`) ;
-
-
-
-## 建议使用AS关键字显示声明一个别名
+## 建议使用 AS 关键字显示声明一个别名
* **Item:** ALI.001
* **Severity:** L0
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用AS关键字比隐含别名(如"tbl alias")更易懂。
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 如果您不在乎重复的话,建议使用UNION ALL替代UNION
+## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
* **Item:** SUB.002
@@ -2576,41 +1326,15 @@ SELECT
FROM
city NATURAL
JOIN country
-```
-
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *country* | NULL | ALL | PRIMARY | NULL | | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *city* | NULL | ref | idx\_fk\_country\_id | idx\_fk\_country\_id | 2 | sakila.country.country\_id | 5 | 10.00% | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
+```
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
# Query: 5C547F08EADBB131
@@ -2625,39 +1349,13 @@ FROM
LEFT JOIN country
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *city* | NULL | ALL | | NULL | | | 600 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *country* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.city.country\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例:'SELECT * FROM ref_table,tbl WHERE ref_table.key_column=tbl.column;'.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
# Query: AF0C1EB58B23D2FA
@@ -2672,39 +1370,13 @@ FROM
RIGHT JOIN country
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *country* | NULL | ALL | | NULL | | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *city* | NULL | ref | idx\_fk\_country\_id | idx\_fk\_country\_id | 2 | sakila.country.country\_id | 5 | ☠️ **100.00%** | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
# Query: 626571EAE84E2C8A
@@ -2718,51 +1390,25 @@ FROM
city a STRAIGHT_JOIN country b ON a. country_id= b. country_id
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *a* | NULL | ALL | idx\_fk\_country\_id | NULL | | | 600 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *b* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.a.country\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例:'SELECT * FROM ref_table,tbl WHERE ref_table.key_column=tbl.column;'.
-
-#### Extra信息解读
-
-* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
-
-
-## 建议使用AS关键字显示声明一个别名
+## 建议使用 AS 关键字显示声明一个别名
* **Item:** ALI.001
* **Severity:** L0
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用AS关键字比隐含别名(如"tbl alias")更易懂。
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
# Query: F76BFFC87914E3D5
-☆ ☆ ☆ ☆ ☆ 0分
+★ ★ ★ ☆ ☆ 60分
```sql
@@ -2778,37 +1424,33 @@ FROM
scott. emp e)
```
-## MySQL返回信息
-
-Unknown database 'scott'
-
-## 建议使用AS关键字显示声明一个别名
+## 建议使用 AS 关键字显示声明一个别名
* **Item:** ALI.001
* **Severity:** L0
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用AS关键字比隐含别名(如"tbl alias")更易懂。
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## MySQL对子查询的优化效果不佳
+## MySQL 对子查询的优化效果不佳
* **Item:** SUB.001
* **Severity:** L4
-* **Content:** MySQL将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为JOIN或LEFT OUTER JOIN。
+* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
-# Query: 18D2299710570E81
+# Query: 7253A3D336F9F3FE
-☆ ☆ ☆ ☆ ☆ 10分
+★ ☆ ☆ ☆ ☆ 30分
```sql
@@ -2823,7 +1465,7 @@ FROM
WHERE
ip= "123.45.67.89"
ORDER BY
- tsdesc
+ ts desc
LIMIT
50, 10) I
JOIN LOG ON (I. id= LOG. id)
@@ -2832,29 +1474,13 @@ ORDER BY
TS desc
```
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
-
-## ORDER BY语句对多个不同条件使用不同方向的排序无法使用索引
-
-* **Item:** CLA.007
-
-* **Severity:** L2
-
-* **Content:** ORDER BY子句中的所有表达式必须按统一的ASC或DESC方向排序,以便利用索引。
-
-## ORDER BY的条件为表达式
-
-* **Item:** CLA.009
-
-* **Severity:** L2
-
-* **Content:** 当ORDER BY条件为表达式或函数时会使用到临时表,如果在未指定WHERE或WHERE条件返回的结果集较大时性能会很差。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
## 同一张表被连接两次
@@ -2862,7 +1488,7 @@ ORDER BY
* **Severity:** L4
-* **Content:** 相同的表在FROM子句中至少出现两次,可以简化为对该表的单次访问。
+* **Content:** 相同的表在 FROM 子句中至少出现两次,可以简化为对该表的单次访问。
## 用字符类型存储IP地址
@@ -2870,15 +1496,15 @@ ORDER BY
* **Severity:** L2
-* **Content:** 字符串字面上看起来像IP地址,但不是INET\_ATON()的参数,表示数据被存储为字符而不是整数。将IP地址存储为整数更为有效。
+* **Content:** 字符串字面上看起来像IP地址,但不是 INET\_ATON() 的参数,表示数据被存储为字符而不是整数。将IP地址存储为整数更为有效。
-## MySQL对子查询的优化效果不佳
+## MySQL 对子查询的优化效果不佳
* **Item:** SUB.001
* **Severity:** L4
-* **Content:** MySQL将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为JOIN或LEFT OUTER JOIN。
+* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
# Query: 7F02E23D44A38A6D
@@ -2893,29 +1519,13 @@ WHERE
city. city_id = 1
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | DELETE | *city* | NULL | const | PRIMARY,
idx\_fk\_country\_id | PRIMARY | 2 | const | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | DELETE | *country* | NULL | const | PRIMARY | PRIMARY | 2 | const | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-
-
-
-### Explain信息解读
-
-#### Type信息解读
-
-* **const**: const用于使用常数值比较PRIMARY KEY时, 当查询的表仅有一行时, 使用system. 例:SELECT * FROM tbl WHERE col =1.
-
-
-## 不建议使用联表更新
+## 不建议使用联表删除或更新
* **Item:** JOI.007
* **Severity:** L4
-* **Content:** 当需要同时更新多张表时建议使用简单SQL,一条SQL只更新一张表,尽量不要将多张表的更新在同一条SQL中完成。
+* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
## 使用DELETE/DROP/TRUNCATE等操作时注意备份
@@ -2927,7 +1537,7 @@ WHERE
# Query: F8314ABD1CBF2FF1
-★ ★ ★ ☆ ☆ 70分
+★ ★ ★ ★ ☆ 80分
```sql
DELETE city
@@ -2938,53 +1548,13 @@ WHERE
country. country IS NULL
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | DELETE | *city* | NULL | ALL | | NULL | | | 600 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *country* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.city.country\_id | 1 | 10.00% | ☠️ **O(n)** | Using where; Not exists |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例:'SELECT * FROM ref_table,tbl WHERE ref_table.key_column=tbl.column;'.
-
-#### Extra信息解读
-
-* **Not exists**: MySQL能够对LEFT JOIN查询进行优化, 并且在查找到符合LEFT JOIN条件的行后, 则不再查找更多的行.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的country表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列country添加索引,散粒度为: 100.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`country\` add index \`idx\_country\` (\`country\`) ;
-
-
-
-## 不建议使用联表更新
+## 不建议使用联表删除或更新
* **Item:** JOI.007
* **Severity:** L4
-* **Content:** 当需要同时更新多张表时建议使用简单SQL,一条SQL只更新一张表,尽量不要将多张表的更新在同一条SQL中完成。
+* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
## 使用DELETE/DROP/TRUNCATE等操作时注意备份
@@ -3007,31 +1577,13 @@ WHERE
a1. country_id= a2. country_id
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | DELETE | *a2* | NULL | ALL | PRIMARY | NULL | | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | DELETE | *a1* | NULL | ref | idx\_fk\_country\_id | idx\_fk\_country\_id | 2 | sakila.a2.country\_id | 5 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-
-
-
-### Explain信息解读
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-
-## 不建议使用联表更新
+## 不建议使用联表删除或更新
* **Item:** JOI.007
* **Severity:** L4
-* **Content:** 当需要同时更新多张表时建议使用简单SQL,一条SQL只更新一张表,尽量不要将多张表的更新在同一条SQL中完成。
+* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
## 使用DELETE/DROP/TRUNCATE等操作时注意备份
@@ -3054,31 +1606,13 @@ WHERE
a1. country_id= a2. country_id
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | DELETE | *a2* | NULL | ALL | PRIMARY | NULL | | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | DELETE | *a1* | NULL | ref | idx\_fk\_country\_id | idx\_fk\_country\_id | 2 | sakila.a2.country\_id | 5 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-
-
-
-### Explain信息解读
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-
-## 不建议使用联表更新
+## 不建议使用联表删除或更新
* **Item:** JOI.007
* **Severity:** L4
-* **Content:** 当需要同时更新多张表时建议使用简单SQL,一条SQL只更新一张表,尽量不要将多张表的更新在同一条SQL中完成。
+* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
## 使用DELETE/DROP/TRUNCATE等操作时注意备份
@@ -3090,7 +1624,7 @@ WHERE
# Query: F16FD63381EF8299
-★ ★ ★ ★ ☆ 90分
+★ ★ ★ ★ ★ 100分
```sql
@@ -3100,37 +1634,6 @@ WHERE
LENGTH > 100
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | DELETE | *film* | NULL | ALL | | NULL | | | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列length添加索引,散粒度为: 14.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
-
-
-
## 使用DELETE/DROP/TRUNCATE等操作时注意备份
* **Item:** SEC.003
@@ -3156,29 +1659,13 @@ WHERE
city. city_id= 10
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | UPDATE | *city* | NULL | const | PRIMARY,
idx\_fk\_country\_id | PRIMARY | 2 | const | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | UPDATE | *country* | NULL | const | PRIMARY | PRIMARY | 2 | const | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-
-
-
-### Explain信息解读
-
-#### Type信息解读
-
-* **const**: const用于使用常数值比较PRIMARY KEY时, 当查询的表仅有一行时, 使用system. 例:SELECT * FROM tbl WHERE col =1.
-
-
-## 不建议使用联表更新
+## 不建议使用联表删除或更新
* **Item:** JOI.007
* **Severity:** L4
-* **Content:** 当需要同时更新多张表时建议使用简单SQL,一条SQL只更新一张表,尽量不要将多张表的更新在同一条SQL中完成。
+* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
# Query: C15BDF2C73B5B7ED
@@ -3198,44 +1685,17 @@ WHERE
city. city_id= 10
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | UPDATE | *city* | NULL | const | PRIMARY,
idx\_fk\_country\_id | PRIMARY | 2 | const | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | UPDATE | *country* | NULL | const | PRIMARY | PRIMARY | 2 | const | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *address* | NULL | ref | idx\_fk\_city\_id | idx\_fk\_city\_id | 2 | const | 1 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* **const**: const用于使用常数值比较PRIMARY KEY时, 当查询的表仅有一行时, 使用system. 例:SELECT * FROM tbl WHERE col =1.
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-#### Extra信息解读
-
-* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
-
-
-## 不建议使用联表更新
+## 不建议使用联表删除或更新
* **Item:** JOI.007
* **Severity:** L4
-* **Content:** 当需要同时更新多张表时建议使用简单SQL,一条SQL只更新一张表,尽量不要将多张表的更新在同一条SQL中完成。
+* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
# Query: FCD1ABF36F8CDAD7
-★ ★ ★ ★ ★ 100分
+★ ★ ★ ★ ☆ 90分
```sql
@@ -3250,21 +1710,13 @@ WHERE
AND city. city_id= 10
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | UPDATE | *city* | NULL | const | PRIMARY,
idx\_fk\_country\_id | PRIMARY | 2 | const | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | UPDATE | *country* | NULL | const | PRIMARY | PRIMARY | 2 | const | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-
-
-
-### Explain信息解读
+## 不建议使用连续判断
-#### Type信息解读
+* **Item:** RES.009
-* **const**: const用于使用常数值比较PRIMARY KEY时, 当查询的表仅有一行时, 使用system. 例:SELECT * FROM tbl WHERE col =1.
+* **Severity:** L2
+* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
# Query: FE409EB794EE91CF
@@ -3280,24 +1732,7 @@ WHERE
language_id = 20
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | UPDATE | *film* | NULL | range | idx\_fk\_language\_id | idx\_fk\_language\_id | 1 | const | 1 | ☠️ **100.00%** | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### Type信息解读
-
-* **range**: 只检索给定范围的行, 使用一个索引来选择行. key列显示使用了哪个索引. key_len包含所使用索引的最长关键元素.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
+## OK
# Query: 3656B13CC4F888E2
@@ -3311,47 +1746,21 @@ FROM
country
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | INSERT | *city* | NULL | ALL | | NULL | | | 0 | 0.00% | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *country* | NULL | index | | PRIMARY | 2 | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **index**: 全表扫描, 只是扫描表的时候按照索引次序进行而不是行. 主要优点就是避免了排序, 但是开销仍然非常大.
-
-#### Extra信息解读
-
-* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
-
-
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## INSERT INTO xx SELECT加锁粒度较大请谨慎
+## INSERT INTO xx SELECT 加锁粒度较大请谨慎
* **Item:** LCK.001
* **Severity:** L3
-* **Content:** INSERT INTO xx SELECT加锁粒度较大请谨慎
+* **Content:** INSERT INTO xx SELECT 加锁粒度较大请谨慎
# Query: 2F7439623B712317
@@ -3365,20 +1774,7 @@ VALUES
(3)
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | INSERT | *city* | NULL | ALL | | NULL | | | 0 | 0.00% | ☠️ **O(n)** | NULL |
-
-
-
-### Explain信息解读
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
+## OK
# Query: 11EC7AAACC97DC0F
@@ -3392,28 +1788,13 @@ FROM
DUAL
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | INSERT | *city* | NULL | ALL | | NULL | | | 0 | 0.00% | ☠️ **O(n)** | NULL |
-
-
-
-### Explain信息解读
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-
-## INSERT INTO xx SELECT加锁粒度较大请谨慎
+## INSERT INTO xx SELECT 加锁粒度较大请谨慎
* **Item:** LCK.001
* **Severity:** L3
-* **Content:** INSERT INTO xx SELECT加锁粒度较大请谨慎
+* **Content:** INSERT INTO xx SELECT 加锁粒度较大请谨慎
# Query: E3DDA1A929236E72
@@ -3427,47 +1808,21 @@ FROM
country
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | REPLACE | *city* | NULL | ALL | | NULL | | | 0 | 0.00% | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *country* | NULL | index | | PRIMARY | 2 | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **index**: 全表扫描, 只是扫描表的时候按照索引次序进行而不是行. 主要优点就是避免了排序, 但是开销仍然非常大.
-
-#### Extra信息解读
-
-* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
-
-
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## INSERT INTO xx SELECT加锁粒度较大请谨慎
+## INSERT INTO xx SELECT 加锁粒度较大请谨慎
* **Item:** LCK.001
* **Severity:** L3
-* **Content:** INSERT INTO xx SELECT加锁粒度较大请谨慎
+* **Content:** INSERT INTO xx SELECT 加锁粒度较大请谨慎
# Query: 466F1AC2F5851149
@@ -3481,20 +1836,7 @@ VALUES
(3)
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | REPLACE | *city* | NULL | ALL | | NULL | | | 0 | 0.00% | ☠️ **O(n)** | NULL |
-
-
-
-### Explain信息解读
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
+## OK
# Query: A7973BDD268F926E
@@ -3508,28 +1850,13 @@ FROM
DUAL
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | REPLACE | *city* | NULL | ALL | | NULL | | | 0 | 0.00% | ☠️ **O(n)** | NULL |
-
-
-
-### Explain信息解读
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-
-## INSERT INTO xx SELECT加锁粒度较大请谨慎
+## INSERT INTO xx SELECT 加锁粒度较大请谨慎
* **Item:** LCK.001
* **Severity:** L3
-* **Content:** INSERT INTO xx SELECT加锁粒度较大请谨慎
+* **Content:** INSERT INTO xx SELECT 加锁粒度较大请谨慎
# Query: 105C870D5DFB6710
@@ -3623,36 +1950,13 @@ FROM
) film
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | index | | idx\_fk\_language\_id | 1 | | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* **index**: 全表扫描, 只是扫描表的时候按照索引次序进行而不是行. 主要优点就是避免了排序, 但是开销仍然非常大.
-
-#### Extra信息解读
-
-* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
-
-
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
## 执行计划中嵌套连接深度过深
@@ -3670,79 +1974,49 @@ FROM
SELECT
*
-FROM
- film
-WHERE
- language_id = (
-SELECT
- language_id
-FROM
- language
-LIMIT
- 1)
-```
-
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | PRIMARY | *film* | NULL | ALL | idx\_fk\_language\_id | NULL | | | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using where |
-| 2 | SUBQUERY | *language* | NULL | index | | PRIMARY | 1 | | 6 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **PRIMARY**: 最外层的select.
-
-* **SUBQUERY**: 子查询中的第一个SELECT查询, 不依赖于外部查询的结果集.
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **index**: 全表扫描, 只是扫描表的时候按照索引次序进行而不是行. 主要优点就是避免了排序, 但是开销仍然非常大.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
-
+FROM
+ film
+WHERE
+ language_id = (
+SELECT
+ language_id
+FROM
+ language
+LIMIT
+ 1)
+```
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 未使用ORDER BY的LIMIT查询
+## 未使用 ORDER BY 的 LIMIT 查询
* **Item:** RES.002
* **Severity:** L4
-* **Content:** 没有ORDER BY的LIMIT会导致非确定性的结果,这取决于查询执行计划。
+* **Content:** 没有 ORDER BY 的 LIMIT 会导致非确定性的结果,这取决于查询执行计划。
-## MySQL对子查询的优化效果不佳
+## MySQL 对子查询的优化效果不佳
* **Item:** SUB.001
* **Severity:** L4
-* **Content:** MySQL将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为JOIN或LEFT OUTER JOIN。
+* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
# Query: 16CB4628D2597D40
@@ -3763,64 +2037,31 @@ FROM
RIGHT JOIN country o ON i. city_id= o. country_id
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | PRIMARY | *i* | NULL | ALL | | NULL | | | 600 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | PRIMARY | *o* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.i.city\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 2 | UNION | *o* | NULL | ALL | | NULL | | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 2 | UNION | *i* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.o.country\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 0 | UNION RESULT | ** | NULL | ALL | | NULL | | | 0 | 0.00% | ☠️ **O(n)** | Using temporary |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **PRIMARY**: 最外层的select.
-
-* **UNION**: UNION中的第二个或后面的SELECT查询, 不依赖于外部查询的结果集.
-
-* **UNION RESULT**: UNION查询的结果集.
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例:'SELECT * FROM ref_table,tbl WHERE ref_table.key_column=tbl.column;'.
-
-#### Extra信息解读
-
-* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
-
-
-## 建议使用AS关键字显示声明一个别名
+## 建议使用 AS 关键字显示声明一个别名
* **Item:** ALI.001
* **Severity:** L0
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用AS关键字比隐含别名(如"tbl alias")更易懂。
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 如果您不在乎重复的话,建议使用UNION ALL替代UNION
+## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
* **Item:** SUB.002
@@ -3830,7 +2071,7 @@ FROM
# Query: EA50643B01E139A8
-☆ ☆ ☆ ☆ ☆ 0分
+★ ☆ ☆ ☆ ☆ 35分
```sql
@@ -3853,53 +2094,45 @@ GROUP BY
first_name
```
-## MySQL返回信息
-
-Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 't.actor_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
-
-## 为sakila库的actor表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列last\_name添加索引,散粒度为: 60.50%; 为列last\_update添加索引,散粒度为: 0.50%; 为列first\_name添加索引,散粒度为: 64.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`actor\` add index \`idx\_last\_name\_last\_update\_first\_name\` (\`last\_name\`,\`last\_update\`,\`first\_name\`) ;
-
-
-
-## 请为GROUP BY显示添加ORDER BY条件
+## 请为 GROUP BY 显示添加 ORDER BY 条件
* **Item:** CLA.008
* **Severity:** L2
-* **Content:** 默认MySQL会对'GROUP BY col1, col2, ...'请求按如下顺序排序'ORDER BY col1, col2, ...'。如果GROUP BY语句不指定ORDER BY条件会导致无谓的排序产生,如果不需要排序建议添加'ORDER BY NULL'。
+* **Content:** 默认 MySQL 会对 'GROUP BY col1, col2, ...' 请求按如下顺序排序 'ORDER BY col1, col2, ...'。如果 GROUP BY 语句不指定 ORDER BY 条件会导致无谓的排序产生,如果不需要排序建议添加 'ORDER BY NULL'。
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 非确定性的GROUP BY
+## 非确定性的 GROUP BY
* **Item:** RES.001
* **Severity:** L4
-* **Content:** SQL返回的列既不在聚合函数中也不是GROUP BY表达式的列中,因此这些值的结果将是非确定性的。如:select a, b, c from tbl where foo="bar" group by a,该SQL返回的结果就是不确定的。
+* **Content:** SQL返回的列既不在聚合函数中也不是 GROUP BY 表达式的列中,因此这些值的结果将是非确定性的。如:select a, b, c from tbl where foo="bar" group by a,该 SQL 返回的结果就是不确定的。
+
+## 不建议使用连续判断
+
+* **Item:** RES.009
+
+* **Severity:** L2
+
+* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
-## MySQL对子查询的优化效果不佳
+## MySQL 对子查询的优化效果不佳
* **Item:** SUB.001
* **Severity:** L4
-* **Content:** MySQL将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为JOIN或LEFT OUTER JOIN。
+* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
# Query: 7598A4EDE6CFA6BE
@@ -3924,60 +2157,23 @@ WHERE
i. city_id is null
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | PRIMARY | *i* | NULL | ALL | | NULL | | | 600 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | PRIMARY | *o* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.i.city\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | Using where; Not exists |
-| 2 | UNION | *o* | NULL | ALL | | NULL | | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 2 | UNION | *i* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.o.country\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | Using where; Not exists |
-| 0 | UNION RESULT | ** | NULL | ALL | | NULL | | | 0 | 0.00% | ☠️ **O(n)** | Using temporary |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **PRIMARY**: 最外层的select.
-
-* **UNION**: UNION中的第二个或后面的SELECT查询, 不依赖于外部查询的结果集.
-
-* **UNION RESULT**: UNION查询的结果集.
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例:'SELECT * FROM ref_table,tbl WHERE ref_table.key_column=tbl.column;'.
-
-#### Extra信息解读
-
-* **Not exists**: MySQL能够对LEFT JOIN查询进行优化, 并且在查找到符合LEFT JOIN条件的行后, 则不再查找更多的行.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
-
-
-## 建议使用AS关键字显示声明一个别名
+## 建议使用 AS 关键字显示声明一个别名
* **Item:** ALI.001
* **Severity:** L0
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用AS关键字比隐含别名(如"tbl alias")更易懂。
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 如果您不在乎重复的话,建议使用UNION ALL替代UNION
+## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
* **Item:** SUB.002
@@ -3997,43 +2193,17 @@ FROM
customer STRAIGHT_JOIN address ON customer. address_id= address. address_id
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *customer* | NULL | ALL | idx\_fk\_address\_id | NULL | | | 599 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | SIMPLE | *address* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.customer.address\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例:'SELECT * FROM ref_table,tbl WHERE ref_table.key_column=tbl.column;'.
-
-#### Extra信息解读
-
-* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
-
-
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
# Query: E48A20D0413512DA
-★ ☆ ☆ ☆ ☆ 20分
+★ ☆ ☆ ☆ ☆ 30分
```sql
@@ -4057,89 +2227,29 @@ ORDER BY
phone desc
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | PRIMARY | *country* | NULL | index | PRIMARY | PRIMARY | 2 | | 109 | ☠️ **100.00%** | ☠️ **O(n)** | Using index; Using temporary; Using filesort |
-| 1 | PRIMARY | *city* | NULL | ref | PRIMARY,
idx\_fk\_country\_id | idx\_fk\_country\_id | 2 | sakila.country.country\_id | 5 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | PRIMARY | *c* | NULL | ALL | | NULL | | | 600 | 10.00% | ☠️ **O(n)** | Using where; Using join buffer (Block Nested Loop) |
-| 1 | PRIMARY | *a* | NULL | ref | PRIMARY,
idx\_fk\_city\_id | idx\_fk\_city\_id | 2 | sakila.city.city\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | PRIMARY | *cu* | NULL | ref | idx\_fk\_address\_id | idx\_fk\_address\_id | 2 | sakila.a.address\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 1 | PRIMARY | ** | NULL | ref | | | 152 | sakila.a.address | 6 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
-| 2 | DERIVED | *a* | NULL | ALL | PRIMARY,
idx\_fk\_city\_id | NULL | | | 603 | ☠️ **100.00%** | ☠️ **O(n)** | Using filesort |
-| 2 | DERIVED | *cu* | NULL | ref | idx\_fk\_store\_id,
idx\_fk\_address\_id | idx\_fk\_address\_id | 2 | sakila.a.address\_id | 1 | 54.42% | ☠️ **O(n)** | Using where |
-| 2 | DERIVED | *city* | NULL | eq\_ref | PRIMARY,
idx\_fk\_country\_id | PRIMARY | 2 | sakila.a.city\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
-| 2 | DERIVED | *country* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.city.country\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **PRIMARY**: 最外层的select.
-
-* **DERIVED**: 用于from子句里有子查询的情况. MySQL会递归执行这些子查询, 把结果放在临时表里.
-
-#### Type信息解读
-
-* **index**: 全表扫描, 只是扫描表的时候按照索引次序进行而不是行. 主要优点就是避免了排序, 但是开销仍然非常大.
-
-* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例:'SELECT * FROM ref_table,tbl WHERE ref_table.key_column=tbl.column;'.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
-
-* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
-
-* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
-
-* **Using join buffer**: 从已有连接中找被读入缓存的数据, 并且通过缓存来完成与当前表的连接.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的city表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列city添加索引,散粒度为: 99.83%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`city\` add index \`idx\_city\` (\`city\`) ;
-
-
-
-## 建议使用AS关键字显示声明一个别名
+## 建议使用 AS 关键字显示声明一个别名
* **Item:** ALI.001
* **Severity:** L0
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用AS关键字比隐含别名(如"tbl alias")更易懂。
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## ORDER BY语句对多个不同条件使用不同方向的排序无法使用索引
+## ORDER BY 语句对多个不同条件使用不同方向的排序无法使用索引
* **Item:** CLA.007
* **Severity:** L2
-* **Content:** ORDER BY子句中的所有表达式必须按统一的ASC或DESC方向排序,以便利用索引。
+* **Content:** ORDER BY 子句中的所有表达式必须按统一的 ASC 或 DESC 方向排序,以便利用索引。
## 同一张表被连接两次
@@ -4147,15 +2257,15 @@ ORDER BY
* **Severity:** L4
-* **Content:** 相同的表在FROM子句中至少出现两次,可以简化为对该表的单次访问。
+* **Content:** 相同的表在 FROM 子句中至少出现两次,可以简化为对该表的单次访问。
-## MySQL对子查询的优化效果不佳
+## MySQL 对子查询的优化效果不佳
* **Item:** SUB.001
* **Severity:** L4
-* **Content:** MySQL将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为JOIN或LEFT OUTER JOIN。
+* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
# Query: B0BA5A7079EA16B3
@@ -4171,48 +2281,25 @@ WHERE
DATE( last_update) = '2006-02-15'
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using where |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 不建议使用SELECT * 类型查询
+## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
-* **Content:** 当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-## 避免在WHERE条件中使用函数或其他运算符
+## 避免在 WHERE 条件中使用函数或其他运算符
* **Item:** FUN.001
* **Severity:** L2
-* **Content:** 虽然在SQL中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧。
+* **Content:** 虽然在 SQL 中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧。也不建议在查询比较条件两侧书写多余的括号,这会对阅读产生比较大的困扰。
# Query: 18A2AD1395A58EAE
-☆ ☆ ☆ ☆ ☆ 0分
+★ ★ ★ ☆ ☆ 60分
```sql
@@ -4224,33 +2311,29 @@ GROUP BY
DATE( last_update)
```
-## MySQL返回信息
-
-Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'optimizer_RSq3xBEF0TXgZsHj.film.last_update' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
-
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## 请为GROUP BY显示添加ORDER BY条件
+## 请为 GROUP BY 显示添加 ORDER BY 条件
* **Item:** CLA.008
* **Severity:** L2
-* **Content:** 默认MySQL会对'GROUP BY col1, col2, ...'请求按如下顺序排序'ORDER BY col1, col2, ...'。如果GROUP BY语句不指定ORDER BY条件会导致无谓的排序产生,如果不需要排序建议添加'ORDER BY NULL'。
+* **Content:** 默认 MySQL 会对 'GROUP BY col1, col2, ...' 请求按如下顺序排序 'ORDER BY col1, col2, ...'。如果 GROUP BY 语句不指定 ORDER BY 条件会导致无谓的排序产生,如果不需要排序建议添加 'ORDER BY NULL'。
-## GROUP BY的条件为表达式
+## GROUP BY 的条件为表达式
* **Item:** CLA.010
* **Severity:** L2
-* **Content:** 当GROUP BY条件为表达式或函数时会使用到临时表,如果在未指定WHERE或WHERE条件返回的结果集较大时性能会很差。
+* **Content:** 当 GROUP BY 条件为表达式或函数时会使用到临时表,如果在未指定 WHERE 或 WHERE 条件返回的结果集较大时性能会很差。
# Query: 60F234BA33AAC132
@@ -4266,48 +2349,25 @@ ORDER BY
DATE( last_update)
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using filesort |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
-
-
-## SELECT未指定WHERE条件
+## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
-* **Content:** SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-## ORDER BY的条件为表达式
+## ORDER BY 的条件为表达式
* **Item:** CLA.009
* **Severity:** L2
-* **Content:** 当ORDER BY条件为表达式或函数时会使用到临时表,如果在未指定WHERE或WHERE条件返回的结果集较大时性能会很差。
+* **Content:** 当 ORDER BY 条件为表达式或函数时会使用到临时表,如果在未指定 WHERE 或 WHERE 条件返回的结果集较大时性能会很差。
# Query: 1ED2B7ECBA4215E1
-★ ★ ★ ★ ☆ 80分
+★ ★ ★ ★ ☆ 90分
```sql
@@ -4323,50 +2383,13 @@ GROUP BY
description
```
-## Explain信息
-
-| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
-|---|---|---|---|---|---|---|---|---|---|---|---|---|
-| 1 | SIMPLE | *film* | NULL | ALL | | NULL | | | 1000 | 20.00% | ☠️ **O(n)** | Using where; Using temporary |
-
-
-
-### Explain信息解读
-
-#### SelectType信息解读
-
-* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
-
-#### Type信息解读
-
-* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
-
-#### Extra信息解读
-
-* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
-
-* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
-
-
-## 为sakila库的film表添加索引
-
-* **Item:** IDX.001
-
-* **Severity:** L2
-
-* **Content:** 为列description添加索引,散粒度为: 100.00%;
-
-* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_description\` (\`description\`(255)) ;
-
-
-
-## 请为GROUP BY显示添加ORDER BY条件
+## 请为 GROUP BY 显示添加 ORDER BY 条件
* **Item:** CLA.008
* **Severity:** L2
-* **Content:** 默认MySQL会对'GROUP BY col1, col2, ...'请求按如下顺序排序'ORDER BY col1, col2, ...'。如果GROUP BY语句不指定ORDER BY条件会导致无谓的排序产生,如果不需要排序建议添加'ORDER BY NULL'。
+* **Content:** 默认 MySQL 会对 'GROUP BY col1, col2, ...' 请求按如下顺序排序 'ORDER BY col1, col2, ...'。如果 GROUP BY 语句不指定 ORDER BY 条件会导致无谓的排序产生,如果不需要排序建议添加 'ORDER BY NULL'。
# Query: 255BAC03F56CDBC7
@@ -4380,13 +2403,7 @@ ADD
index idx_city_id( city_id)
```
-## 提醒:请将索引属性顺序与查询对齐
-
-* **Item:** KEY.004
-
-* **Severity:** L0
-
-* **Content:** 如果为列创建复合索引,请确保查询属性与索引属性的顺序相同,以便DBMS在处理查询时使用索引。如果查询和索引属性订单没有对齐,那么DBMS可能无法在查询处理期间使用索引。
+## OK
# Query: C315BC4EE0F4E523
@@ -4398,7 +2415,7 @@ ALTER TABLE
inventory
ADD
index `idx_store_film` (
- `store_id`, `film_id` )
+ `store_id`, `film_id`)
```
## 提醒:请将索引属性顺序与查询对齐
@@ -4419,13 +2436,13 @@ ALTER TABLE
inventory
ADD
index `idx_store_film` (
- `store_id`, `film_id` ),
- ADD
+ `store_id`, `film_id`),
+ ADD
index `idx_store_film` (
- `store_id`, `film_id` ),
- ADD
+ `store_id`, `film_id`),
+ ADD
index `idx_store_film` (
- `store_id`, `film_id` )
+ `store_id`, `film_id`)
```
## 提醒:请将索引属性顺序与查询对齐
@@ -4436,3 +2453,160 @@ ADD
* **Content:** 如果为列创建复合索引,请确保查询属性与索引属性的顺序相同,以便DBMS在处理查询时使用索引。如果查询和索引属性订单没有对齐,那么DBMS可能无法在查询处理期间使用索引。
+# Query: CE8A69541550D286
+
+★ ☆ ☆ ☆ ☆ 30分
+
+```sql
+
+SELECT
+ DATE_FORMAT( t. atm, '%Y-%m-%d'
+),
+COUNT( DISTINCT (
+ t. usr))
+ FROM
+ usr_terminal t
+ WHERE
+ t. atm > '2018-10-22 00:00:00'
+ AND t. agent LIKE '%Chrome%'
+ AND t. system = 'eip'
+ GROUP BY
+ DATE_FORMAT( t. atm, '%Y-%m-%d'
+)
+ORDER BY
+ DATE_FORMAT( t. atm, '%Y-%m-%d'
+)
+```
+
+## 建议使用 AS 关键字显示声明一个别名
+
+* **Item:** ALI.001
+
+* **Severity:** L0
+
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
+
+## 不建议使用前项通配符查找
+
+* **Item:** ARG.001
+
+* **Severity:** L4
+
+* **Content:** 例如 "%foo",查询参数有一个前项通配符的情况无法使用已有索引。
+
+## ORDER BY 的条件为表达式
+
+* **Item:** CLA.009
+
+* **Severity:** L2
+
+* **Content:** 当 ORDER BY 条件为表达式或函数时会使用到临时表,如果在未指定 WHERE 或 WHERE 条件返回的结果集较大时性能会很差。
+
+## GROUP BY 的条件为表达式
+
+* **Item:** CLA.010
+
+* **Severity:** L2
+
+* **Content:** 当 GROUP BY 条件为表达式或函数时会使用到临时表,如果在未指定 WHERE 或 WHERE 条件返回的结果集较大时性能会很差。
+
+## ORDER BY 多个列但排序方向不同时可能无法使用索引
+
+* **Item:** KEY.008
+
+* **Severity:** L4
+
+* **Content:** 在 MySQL 8.0之前当 ORDER BY 多个列指定的排序方向不同时将无法使用已经建立的索引。
+
+## 不建议使用连续判断
+
+* **Item:** RES.009
+
+* **Severity:** L2
+
+* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
+
+# Query: C11ECE7AE5F80CE5
+
+★ ★ ☆ ☆ ☆ 45分
+
+```sql
+create table hello. t (id int unsigned)
+```
+
+## 建议为表添加注释
+
+* **Item:** CLA.011
+
+* **Severity:** L1
+
+* **Content:** 为表添加注释能够使得表的意义更明确,从而为日后的维护带来极大的便利。
+
+## 请为列添加默认值
+
+* **Item:** COL.004
+
+* **Severity:** L1
+
+* **Content:** 请为列添加默认值,如果是 ALTER 操作,请不要忘记将原字段的默认值写上。字段无默认值,当表较大时无法在线变更表结构。
+
+## 列未添加注释
+
+* **Item:** COL.005
+
+* **Severity:** L1
+
+* **Content:** 建议对表中每个列添加注释,来明确每个列在表中的含义及作用。
+
+## 未指定主键或主键非 int 或 bigint
+
+* **Item:** KEY.007
+
+* **Severity:** L4
+
+* **Content:** 未指定主键或主键非 int 或 bigint,建议将主键设置为 int unsigned 或 bigint unsigned。
+
+## 请为表选择合适的存储引擎
+
+* **Item:** TBL.002
+
+* **Severity:** L4
+
+* **Content:** 建表或修改表的存储引擎时建议使用推荐的存储引擎,如:innodb
+
+# Query: 291F95B7DCB74C21
+
+★ ★ ★ ★ ☆ 95分
+
+```sql
+
+SELECT
+ *
+FROM
+ tb
+WHERE
+ data >= ''
+```
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 084DA3E3EE38DD85
+
+★ ★ ★ ★ ★ 100分
+
+```sql
+
+ALTER TABLE
+ tb alter column id
+DROP
+ DEFAULT
+```
+
+## OK
+
diff --git a/doc/example/main_test.sh b/doc/example/main_test.sh
index dfc6412618942d7c0203e400733ffe7f5e7cfd44..8eb2d7c2bb449a88509884d4d951fd13db74dda0 100755
--- a/doc/example/main_test.sh
+++ b/doc/example/main_test.sh
@@ -1,17 +1,21 @@
#!/bin/bash
-
+GOPATH=$(go env GOPATH)
PROJECT_PATH=${GOPATH}/src/github.com/XiaoMi/soar/
if [ "$1x" == "-updatex" ]; then
- cd "${PROJECT_PATH}" && ./bin/soar -list-test-sqls | ./bin/soar -config=../etc/soar.yaml > ./doc/example/main_test.md
+ cd "${PROJECT_PATH}" && ./bin/soar -list-test-sqls | ./bin/soar -config=../etc/soar.yaml > ./doc/example/main_test.md
+ if [ ! $? -eq 0 ]; then
+ exit 1
+ fi
else
- cd "${PROJECT_PATH}" && ./bin/soar -list-test-sqls | ./bin/soar -config=../etc/soar.yaml > ./doc/example/main_test.log
- # optimizer_XXX 库名,散粒度,以及索引先后顺序每次可能会不一致
- DIFF_LINES=$(cat ./doc/example/main_test.log ./doc/example/main_test.md | grep -v "optimizer\|散粒度" | sort | uniq -u | wc -l)
- if [ "${DIFF_LINES}" -gt 0 ]; then
- git diff ./doc/example/main_test.log ./doc/example/main_test.md
- fi
+ cd "${PROJECT_PATH}" && ./bin/soar -list-test-sqls | ./bin/soar -config=../etc/soar.yaml > ./doc/example/main_test.log
+ if [ ! $? -eq 0 ]; then
+ exit 1
+ fi
+ # optimizer_XXX 库名,散粒度,以及索引先后顺序每次可能会不一致
+ DIFF_LINES=$(cat ./doc/example/main_test.log ./doc/example/main_test.md | grep -v "optimizer\|散粒度" | sort | uniq -u | wc -l)
+ if [ "${DIFF_LINES}" -gt 0 ]; then
+ git diff ./doc/example/main_test.log ./doc/example/main_test.md
+ fi
fi
-
-
diff --git a/doc/heuristic.md b/doc/heuristic.md
index 1e135d7e686f725269a92573265cf263ed54b6e5..5ca0ffb9f0bdfeeb0bce6939392d3ab27efbe44f 100644
--- a/doc/heuristic.md
+++ b/doc/heuristic.md
@@ -532,6 +532,16 @@ CREATE TABLE tab (a varchar(3500));
```sql
CREATE TABLE tab (a BOOLEAN);
```
+## 不建议使用精度在秒级以下的时间数据类型
+
+* **Item**:COL.019
+* **Severity**:L1
+* **Content**:使用高精度的时间数据类型带来的存储空间消耗相对较大;MySQL 在5.6.4以上才可以支持精确到微秒的时间数据类型,使用时需要考虑版本兼容问题。
+* **Case**:
+
+```sql
+CREATE TABLE t1 (t TIME(3), dt DATETIME(6));
+```
## 消除不必要的 DISTINCT 条件
* **Item**:DIS.001
diff --git a/vendor/github.com/pingcap/parser/ast/dml.go b/vendor/github.com/pingcap/parser/ast/dml.go
index e29f8be5bf3617c0607fec7ee8496307bd5a78d6..088eb74446a5935dcf2afb905601ec413d3dfe0b 100644
--- a/vendor/github.com/pingcap/parser/ast/dml.go
+++ b/vendor/github.com/pingcap/parser/ast/dml.go
@@ -135,7 +135,18 @@ type TableName struct {
// Restore implements Recoverable interface.
func (n *TableName) Restore(sb *strings.Builder) error {
- return errors.New("Not implemented")
+ if n.Schema.String() != "" {
+ WriteName(sb, n.Schema.String())
+ sb.WriteString(".")
+ }
+ WriteName(sb, n.Name.String())
+ for _, value := range n.IndexHints {
+ sb.WriteString(" ")
+ if err := value.Restore(sb); err != nil {
+ return errors.Annotate(err, "An error occurred while splicing IndexHints")
+ }
+ }
+ return nil
}
// IndexHintType is the type for index hint use, ignore or force.
@@ -166,6 +177,48 @@ type IndexHint struct {
HintScope IndexHintScope
}
+// IndexHint Restore (The const field uses switch to facilitate understanding)
+func (n *IndexHint) Restore(sb *strings.Builder) error {
+ indexHintType := ""
+ switch n.HintType {
+ case 1:
+ indexHintType = "USE INDEX"
+ case 2:
+ indexHintType = "IGNORE INDEX"
+ case 3:
+ indexHintType = "FORCE INDEX"
+ default: // Prevent accidents
+ return errors.New("IndexHintType has an error while matching")
+ }
+
+ indexHintScope := ""
+ switch n.HintScope {
+ case 1:
+ indexHintScope = ""
+ case 2:
+ indexHintScope = " FOR JOIN"
+ case 3:
+ indexHintScope = " FOR ORDER BY"
+ case 4:
+ indexHintScope = " FOR GROUP BY"
+ default: // Prevent accidents
+ return errors.New("IndexHintScope has an error while matching")
+ }
+
+ sb.WriteString(indexHintType)
+ sb.WriteString(indexHintScope)
+ sb.WriteString(" (")
+ for i, value := range n.IndexNames {
+ if i > 0 {
+ sb.WriteString(", ")
+ }
+ WriteName(sb, value.O)
+ }
+ sb.WriteString(")")
+
+ return nil
+}
+
// Accept implements Node Accept interface.
func (n *TableName) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
diff --git a/vendor/github.com/pingcap/parser/types/field_type.go b/vendor/github.com/pingcap/parser/types/field_type.go
index af2a2b29184be3e194778499833862df3467c46f..d01d57b116f6533c782d9b899987c6d0b6156dc6 100644
--- a/vendor/github.com/pingcap/parser/types/field_type.go
+++ b/vendor/github.com/pingcap/parser/types/field_type.go
@@ -50,6 +50,12 @@ func NewFieldType(tp byte) *FieldType {
}
}
+// Clone returns a copy of itself.
+func (ft *FieldType) Clone() *FieldType {
+ ret := *ft
+ return &ret
+}
+
// Equal checks whether two FieldType objects are equal.
func (ft *FieldType) Equal(other *FieldType) bool {
// We do not need to compare whole `ft.Flag == other.Flag` when wrapping cast upon an Expression.
diff --git a/vendor/github.com/pingcap/tidb/types/field_type.go b/vendor/github.com/pingcap/tidb/types/field_type.go
index 8f5c0edbb71a513b9d5263c7dede64ad97d44599..602c27ca9a34f895ad9b17b793ca48a79f677e3f 100644
--- a/vendor/github.com/pingcap/tidb/types/field_type.go
+++ b/vendor/github.com/pingcap/tidb/types/field_type.go
@@ -40,12 +40,6 @@ func NewFieldType(tp byte) *FieldType {
}
}
-// CloneFieldType clones the given FieldType.
-func CloneFieldType(src *FieldType) *FieldType {
- ft := *src
- return &ft
-}
-
// AggFieldType aggregates field types for a multi-argument function like `IF`, `IFNULL`, `COALESCE`
// whose return type is determined by the arguments' FieldTypes.
// Aggregation is performed by MergeFieldType function.
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 04efd6b62ef29b684908b1dc0fbd23b98d9fc16d..d8fbea1f3a49162f3585cdee9769f5cf8ed45096 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -107,104 +107,104 @@
{
"checksumSHA1": "xbV0lm0Qw8rFC82Dttxbf5ypBjA=",
"path": "github.com/pingcap/parser",
- "revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
- "revisionTime": "2018-12-11T02:45:40Z"
+ "revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
+ "revisionTime": "2018-12-12T04:21:31Z"
},
{
- "checksumSHA1": "zrZ2JfaxdfwpArtuyiPjgH9GKeY=",
+ "checksumSHA1": "RosqMriA/39ZEtqGgNU+bOfRBVc=",
"path": "github.com/pingcap/parser/ast",
- "revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
- "revisionTime": "2018-12-11T02:45:40Z"
+ "revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
+ "revisionTime": "2018-12-12T04:21:31Z"
},
{
"checksumSHA1": "skWGV4FNvD3vr+5olepaPPnylUw=",
"path": "github.com/pingcap/parser/auth",
- "revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
- "revisionTime": "2018-12-11T02:45:40Z"
+ "revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
+ "revisionTime": "2018-12-12T04:21:31Z"
},
{
"checksumSHA1": "t4UHo966WzU9Z0IJkyGHRp0loOk=",
"path": "github.com/pingcap/parser/charset",
- "revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
- "revisionTime": "2018-12-11T02:45:40Z"
+ "revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
+ "revisionTime": "2018-12-12T04:21:31Z"
},
{
"checksumSHA1": "SInoXbsRe0tnBwmatmtZYfSFbdk=",
"path": "github.com/pingcap/parser/format",
- "revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
- "revisionTime": "2018-12-11T02:45:40Z"
+ "revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
+ "revisionTime": "2018-12-12T04:21:31Z"
},
{
"checksumSHA1": "reRV2qecd6NpB7tIW3JeK46K/sk=",
"path": "github.com/pingcap/parser/model",
- "revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
- "revisionTime": "2018-12-11T02:45:40Z"
+ "revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
+ "revisionTime": "2018-12-12T04:21:31Z"
},
{
"checksumSHA1": "QBa9yiMDQNl2cLLwqlRoNTpCPNg=",
"path": "github.com/pingcap/parser/mysql",
- "revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
- "revisionTime": "2018-12-11T02:45:40Z"
+ "revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
+ "revisionTime": "2018-12-12T04:21:31Z"
},
{
"checksumSHA1": "oNBCSwJRykKuzIKgPCttatB9hAo=",
"path": "github.com/pingcap/parser/opcode",
- "revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
- "revisionTime": "2018-12-11T02:45:40Z"
+ "revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
+ "revisionTime": "2018-12-12T04:21:31Z"
},
{
"checksumSHA1": "XvnUllvwMYd6HrMvMiKnn4cGN2M=",
"path": "github.com/pingcap/parser/terror",
- "revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
- "revisionTime": "2018-12-11T02:45:40Z"
+ "revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
+ "revisionTime": "2018-12-12T04:21:31Z"
},
{
- "checksumSHA1": "s96v2EoeGKcWHO3mpMOQk/z2iaI=",
+ "checksumSHA1": "YoDiJ3sniNqxkP5X/BDkx6efteA=",
"path": "github.com/pingcap/parser/types",
- "revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
- "revisionTime": "2018-12-11T02:45:40Z"
+ "revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
+ "revisionTime": "2018-12-12T04:21:31Z"
},
{
"checksumSHA1": "fWqL/7jTYOiqDNmiUcQi3u45Hw0=",
"path": "github.com/pingcap/tidb/sessionctx/stmtctx",
- "revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
- "revisionTime": "2018-12-10T15:48:53Z"
+ "revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
+ "revisionTime": "2018-12-13T09:52:39Z"
},
{
- "checksumSHA1": "U/TFas5WBPWG2DARj51bcfoN0xQ=",
+ "checksumSHA1": "0CCq+3fAyaXs9XU+xWaRvbbtSOQ=",
"path": "github.com/pingcap/tidb/types",
- "revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
- "revisionTime": "2018-12-10T15:48:53Z"
+ "revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
+ "revisionTime": "2018-12-13T09:52:39Z"
},
{
"checksumSHA1": "DWVD7+ygtT66IQ+cqXmMJ5OVqUk=",
"path": "github.com/pingcap/tidb/types/json",
- "revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
- "revisionTime": "2018-12-10T15:48:53Z"
+ "revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
+ "revisionTime": "2018-12-13T09:52:39Z"
},
{
"checksumSHA1": "Zp5ME8OXNTmHnYTwJJUZlydN4/U=",
"path": "github.com/pingcap/tidb/types/parser_driver",
- "revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
- "revisionTime": "2018-12-10T15:48:53Z"
+ "revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
+ "revisionTime": "2018-12-13T09:52:39Z"
},
{
"checksumSHA1": "s709bhSrG2Ec35406mGtrySid4s=",
"path": "github.com/pingcap/tidb/util/execdetails",
- "revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
- "revisionTime": "2018-12-10T15:48:53Z"
+ "revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
+ "revisionTime": "2018-12-13T09:52:39Z"
},
{
"checksumSHA1": "nUC7zVoAMNR2a+z2iGqHoN2AkFE=",
"path": "github.com/pingcap/tidb/util/hack",
- "revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
- "revisionTime": "2018-12-10T15:48:53Z"
+ "revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
+ "revisionTime": "2018-12-13T09:52:39Z"
},
{
"checksumSHA1": "xSyepiuqsoaaeDch7cXeumvVHKM=",
"path": "github.com/pingcap/tidb/util/memory",
- "revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
- "revisionTime": "2018-12-10T15:48:53Z"
+ "revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
+ "revisionTime": "2018-12-13T09:52:39Z"
},
{
"checksumSHA1": "SmYeIK/fIYXNu8IKxD6HOVQVTuU=",
@@ -401,62 +401,62 @@
{
"checksumSHA1": "aKn1oKcY74N8TRLm3Ayt7Q4bbI4=",
"path": "vitess.io/vitess/go/bytes2",
- "revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
- "revisionTime": "2018-12-10T16:22:31Z"
+ "revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
+ "revisionTime": "2018-12-13T22:28:01Z"
},
{
"checksumSHA1": "JVCEN4UGRmg3TofIBdzZMZ3G0Ww=",
"path": "vitess.io/vitess/go/hack",
- "revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
- "revisionTime": "2018-12-10T16:22:31Z"
+ "revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
+ "revisionTime": "2018-12-13T22:28:01Z"
},
{
"checksumSHA1": "e1WJ7vCnVrlQQQlc6n/FewCDMso=",
"path": "vitess.io/vitess/go/sqltypes",
- "revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
- "revisionTime": "2018-12-10T16:22:31Z"
+ "revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
+ "revisionTime": "2018-12-13T22:28:01Z"
},
{
"checksumSHA1": "ntFIQYkBS51G6y+FEkjFW40+HOU=",
"path": "vitess.io/vitess/go/vt/log",
- "revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
- "revisionTime": "2018-12-10T16:22:31Z"
+ "revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
+ "revisionTime": "2018-12-13T22:28:01Z"
},
{
"checksumSHA1": "tPQFPwbMdjuX0qjNl4Zl8zc37JQ=",
"path": "vitess.io/vitess/go/vt/proto/query",
- "revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
- "revisionTime": "2018-12-10T16:22:31Z"
+ "revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
+ "revisionTime": "2018-12-13T22:28:01Z"
},
{
"checksumSHA1": "o0tR/c7lgr0pLkxk7CdvjiNDAKU=",
"path": "vitess.io/vitess/go/vt/proto/topodata",
- "revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
- "revisionTime": "2018-12-10T16:22:31Z"
+ "revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
+ "revisionTime": "2018-12-13T22:28:01Z"
},
{
"checksumSHA1": "77UojBqi0yyeQvR70j7C3kcKclQ=",
"path": "vitess.io/vitess/go/vt/proto/vtgate",
- "revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
- "revisionTime": "2018-12-10T16:22:31Z"
+ "revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
+ "revisionTime": "2018-12-13T22:28:01Z"
},
{
"checksumSHA1": "QpWGhoVDwM+8+sgYLI/YU+95iGU=",
"path": "vitess.io/vitess/go/vt/proto/vtrpc",
- "revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
- "revisionTime": "2018-12-10T16:22:31Z"
+ "revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
+ "revisionTime": "2018-12-13T22:28:01Z"
},
{
"checksumSHA1": "lENrUY/YyxwYFHYN+21TBH92P3U=",
"path": "vitess.io/vitess/go/vt/sqlparser",
- "revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
- "revisionTime": "2018-12-10T16:22:31Z"
+ "revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
+ "revisionTime": "2018-12-13T22:28:01Z"
},
{
"checksumSHA1": "oF4XzuOzwvj1iduX/lYqNSyY/HM=",
"path": "vitess.io/vitess/go/vt/vterrors",
- "revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
- "revisionTime": "2018-12-10T16:22:31Z"
+ "revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
+ "revisionTime": "2018-12-13T22:28:01Z"
}
],
"rootPath": "github.com/XiaoMi/soar"