diff --git a/.gitignore b/.gitignore
index eecdba0afbe7452c5c33a22a075815882bb5942e..7dbe1bab031e0687ff27fe56b004c41d7b40004c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
bin/
release/
+test/tmp/
common/version.go
doc/blueprint/
*.iml
diff --git a/.travis.yml b/.travis.yml
index b707490de2d89cfe351ead8b9b005d224fa12e50..b79a35b319a37ca85c447065772fccb375963add 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,11 +20,15 @@ services:
before_install:
- docker pull mysql
+ - sudo add-apt-repository ppa:duggan/bats --yes
+ - sudo apt-get update -qq
+ - sudo apt-get install -qq bats
script:
- make build
- make docker
- make cover
+ - make test-cli
after_success:
- bash <(curl -s https://codecov.io/bash)
diff --git a/Makefile b/Makefile
index 9f7636a94d155395338c1a1c43cb29b992d59c82..95d88c8bdefc5512b15b0f323ca76304924bd2be 100644
--- a/Makefile
+++ b/Makefile
@@ -72,15 +72,24 @@ fmt: go_version_check
.PHONY: test
test:
@echo "$(CGREEN)Run all test cases ...$(CEND)"
- go test -race ./...
+ go test -timeout 10m -race ./...
@echo "test Success!"
# Rule golang test cases with `-update` flag
+.PHONY: test-update
test-update:
@echo "$(CGREEN)Run all test cases with -update flag ...$(CEND)"
go test ./... -update
@echo "test-update Success!"
+# Using bats test framework run all cli test cases
+# https://github.com/sstephenson/bats
+.PHONY: test-cli
+test-cli: build
+ @echo "$(CGREEN)Run all cli test cases ...$(CEND)"
+ bats ./test
+ @echo "test-cli Success!"
+
# Code Coverage
# colorful coverage numerical >=90% GREEN, <80% RED, Other YELLOW
.PHONY: cover
@@ -180,46 +189,44 @@ release: build
docker:
@echo "$(CGREEN)Build mysql test enviorment ...$(CEND)"
@docker stop soar-mysql 2>/dev/null || true
- @docker wait soar-mysql 2>/dev/null || true
+ @docker wait soar-mysql 2>/dev/null >/dev/null || true
@echo "docker run --name soar-mysql $(MYSQL_RELEASE):$(MYSQL_VERSION)"
@docker run --name soar-mysql --rm -d \
-e MYSQL_ROOT_PASSWORD=1tIsB1g3rt \
-e MYSQL_DATABASE=sakila \
-p 3306:3306 \
- -v `pwd`/doc/example/sakila.sql.gz:/docker-entrypoint-initdb.d/sakila.sql.gz \
+ -v `pwd`/test/sql/init.sql.gz:/docker-entrypoint-initdb.d/init.sql.gz \
$(MYSQL_RELEASE):$(MYSQL_VERSION)
@echo "waiting for sakila database initializing "
- @while ! docker exec soar-mysql mysql --user=root --password=1tIsB1g3rt --host "127.0.0.1" --silent -NBe "do 1" >/dev/null 2>&1 ; do \
- printf '.' ; \
- sleep 1 ; \
- done ; \
- echo '.'
- @echo "mysql test enviorment is ready!"
+ @timeout=180; while [ $${timeout} -gt 0 ] ; do \
+ if ! docker exec soar-mysql mysql --user=root --password=1tIsB1g3rt --host "127.0.0.1" --silent -NBe "do 1" >/dev/null 2>&1 ; then \
+ timeout=`expr $$timeout - 1`; \
+ printf '.' ; sleep 1 ; \
+ else \
+ echo "." ; echo "mysql test enviorment is ready!" ; break ; \
+ fi ; \
+ if [ $$timeout = 0 ] ; then \
+ echo "." ; echo "$(CRED)docker soar-mysql start timeout(180 s)!$(CEND)" ; exit 1 ; \
+ fi ; \
+ done
.PHONY: docker-connect
docker-connect:
- docker exec -it soar-mysql mysql --user=root --password=1tIsB1g3rt --host "127.0.0.1"
+ @docker exec -it soar-mysql mysql --user=root --password=1tIsB1g3rt --host "127.0.0.1" sakila
# attach docker container with bash interactive mode
.PHONY: docker-it
docker-it:
docker exec -it soar-mysql /bin/bash
-.PHONY: main_test
-main_test: install
- @echo "$(CGREEN)running main_test ...$(CEND)"
- @echo "soar -list-test-sqls | soar"
- @./doc/example/main_test.sh
- @echo "main_test Success!"
-
.PHONY: daily
-daily: | deps fmt vendor docker cover doc lint release install main_test clean logo
+daily: | deps fmt vendor docker cover doc lint release install test-cli clean logo
@echo "$(CGREEN)daily build finished ...$(CEND)"
# 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 main_test doc lint logo
+daily-quick: | deps fmt cover test-cli doc lint logo
@echo "$(CGREEN)daily-quick build finished ...$(CEND)"
.PHONY: logo
@@ -238,7 +245,7 @@ clean:
rm -f ${BINARY}.$${GOOS}-$${GOARCH} ;\
done ;\
done
- rm -f ${BINARY} coverage.*
+ rm -f ${BINARY} coverage.* test/tmp/*
find . -name "*.log" -delete
git clean -fi
docker stop soar-mysql 2>/dev/null || true
diff --git a/advisor/index.go b/advisor/index.go
index d1351cff4758466b947993e6d7d60f1788ba6a00..27fd85c53a6b40606521db8f6115df61f0d0f1b6 100644
--- a/advisor/index.go
+++ b/advisor/index.go
@@ -18,6 +18,7 @@ package advisor
import (
"fmt"
+ "sort"
"strings"
"github.com/XiaoMi/soar/ast"
@@ -989,7 +990,13 @@ func (idxAdvs IndexAdvises) Format() map[string]Rule {
rules[advKey].Content = strings.Trim(rules[advKey].Content, common.Config.Delimiter)
}
+ var sortAdvs []string
for adv := range rules {
+ sortAdvs = append(sortAdvs, adv)
+ }
+ sort.Strings(sortAdvs)
+
+ for _, adv := range sortAdvs {
key := fmt.Sprintf("IDX.%03d", number)
ddl := ast.MergeAlterTables(sqls[adv]...)
// 由于传入合并的SQL都是一张表的,所以一定只会输出一条ddl语句
diff --git a/common/tricks.go b/common/tricks.go
index 9e89a5795127ed0709a510dafbb5e2271dd4a916..53360caa703cbf4ba104e3f80943ebf6d1bd0d5d 100644
--- a/common/tricks.go
+++ b/common/tricks.go
@@ -20,7 +20,6 @@ import (
"bufio"
"bytes"
"fmt"
- "io"
"io/ioutil"
"os"
"path/filepath"
@@ -67,24 +66,22 @@ func captureOutput(f func()) string {
r, w, _ := os.Pipe()
os.Stdout = w
- // execute function
- f()
-
- outC := make(chan string)
// copy the output in a separate goroutine so printing can't block indefinitely
+ outC := make(chan string)
go func() {
- var buf bytes.Buffer
- _, err := io.Copy(&buf, r)
+ buf, err := ioutil.ReadAll(r)
if err != nil {
- Log.Warning(err.Error())
+ panic(err)
}
- outC <- buf.String()
+ outC <- string(buf)
}()
+ // execute function
+ f()
+
// back to normal state
- err := w.Close()
- if err != nil {
- Log.Warning(err.Error())
+ if err := w.Close(); err != nil {
+ panic(err)
}
os.Stdout = oldStdout // restoring the real stdout
out := <-outC
diff --git a/common/tricks_test.go b/common/tricks_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..063750ee2a183403251e72790762034862d6bbdf
--- /dev/null
+++ b/common/tricks_test.go
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2018 Xiaomi, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package common
+
+import (
+ "fmt"
+ "strings"
+ "testing"
+ "time"
+)
+
+func TestCaptureOutput(t *testing.T) {
+ c1 := make(chan string, 1)
+ // test output buf large than 65535
+ length := 1<<16 + 1
+ go func() {
+ str := captureOutput(
+ func() {
+ var str []string
+ for i := 0; i < length; i++ {
+ str = append(str, "a")
+ }
+ fmt.Println(strings.Join(str, ""))
+ },
+ )
+ c1 <- str
+ }()
+
+ select {
+ case res := <-c1:
+ if len(res) <= length {
+ t.Errorf("want %d, got %d", length, len(res))
+ }
+ case <-time.After(1 * time.Second):
+ t.Error("capture timeout, pipe read hangup")
+ }
+}
diff --git a/database/explain.go b/database/explain.go
index 75c3f728b968b7448f26718212b9f5373aef936a..df00651655fba57ea6f2b00ab585f1f51cd5a55e 100644
--- a/database/explain.go
+++ b/database/explain.go
@@ -22,6 +22,7 @@ import (
"fmt"
"regexp"
"runtime"
+ "sort"
"strconv"
"strings"
@@ -652,6 +653,7 @@ func ExplainInfoTranslator(exp *ExplainInfo) string {
}
if len(selectTypeBuf) > 0 {
buf = append(buf, fmt.Sprint("#### SelectType信息解读\n"))
+ sort.Strings(selectTypeBuf)
buf = append(buf, strings.Join(selectTypeBuf, "\n"))
}
@@ -681,6 +683,7 @@ func ExplainInfoTranslator(exp *ExplainInfo) string {
}
if len(accessTypeBuf) > 0 {
buf = append(buf, fmt.Sprint("#### Type信息解读\n"))
+ sort.Strings(accessTypeBuf)
buf = append(buf, strings.Join(accessTypeBuf, "\n"))
}
@@ -693,10 +696,11 @@ func ExplainInfoTranslator(exp *ExplainInfo) string {
for _, row := range rows {
for k, c := range explainExtra {
if strings.Contains(row.Extra, k) {
- if k == "Impossible WHERE" {
- if strings.Contains(row.Extra, "Impossible WHERE noticed after reading const tables") {
- continue
- }
+ if k == "Impossible WHERE" && strings.Contains(row.Extra, "Impossible WHERE noticed after reading const tables") {
+ continue
+ }
+ if k == "Using index" && strings.Contains(row.Extra, "Using index condition") {
+ continue
}
warn := false
for _, w := range common.Config.ExplainWarnExtra {
@@ -716,6 +720,7 @@ func ExplainInfoTranslator(exp *ExplainInfo) string {
}
if len(extraTypeBuf) > 0 {
buf = append(buf, fmt.Sprint("#### Extra信息解读\n"))
+ sort.Strings(extraTypeBuf)
buf = append(buf, strings.Join(extraTypeBuf, "\n"))
}
diff --git a/database/explain_test.go b/database/explain_test.go
index 54320b74eec1e27c9276135c9146a0bdb6e9aab6..f5705b6cafe8e9860a35a91175ef7b244b8eed82 100644
--- a/database/explain_test.go
+++ b/database/explain_test.go
@@ -26,6 +26,7 @@ import (
)
var sqls = []string{
+ `use sakila`, // not explain able sql, will convert to empty!
`select * from city where country_id = 44;`,
`select * from address where address2 is not null;`,
`select * from address where address2 is null;`,
diff --git a/deps.sh b/deps.sh
index 68b5b4a8ba98a73cf59efbd68712baf59aa90c3f..21a3e948d8f2f3712dba3502e774ac97bd48a1b1 100755
--- a/deps.sh
+++ b/deps.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-NEEDED_COMMANDS="docker git go govendor retool"
+NEEDED_COMMANDS="docker git go govendor retool bats"
for cmd in ${NEEDED_COMMANDS} ; do
if ! command -v "${cmd}" &> /dev/null ; then
@@ -25,3 +25,7 @@ done
# retool
## go get github.com/twitchtv/retool
+
+# bats https://github.com/sstephenson/bats
+## Ubuntu: apt-get install bats
+## Mac: brew install bats
diff --git a/doc/example/main_test.md b/doc/example/main_test.md
deleted file mode 100644
index 1ff04158d22a5d6454361b7ce782af0cdd9685a9..0000000000000000000000000000000000000000
--- a/doc/example/main_test.md
+++ /dev/null
@@ -1,2612 +0,0 @@
-# Query: C3FAEDA6AD6D762B
-
-★ ★ ★ ★ ☆ 95分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH = 86
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: E969B9297DA79BA6
-
-★ ★ ★ ★ ☆ 90分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH IS 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;
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: 8A106444D14B9880
-
-★ ★ ★ ☆ ☆ 60分
-
-```sql
-
-SELECT
- *
-FROM
- film
-HAVING
- title = 'abc'
-```
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## 不建议使用 HAVING 子句
-
-* **Item:** CLA.013
-
-* **Severity:** L3
-
-* **Content:** 将查询的 HAVING 子句改写为 WHERE 中的查询条件,可以在查询处理期间使用索引。
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: A0C5E62C724A121A
-
-★ ★ ★ ★ ☆ 95分
-
-```sql
-
-SELECT
- *
-FROM
- sakila. film
-WHERE
- LENGTH >= 60
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: 868317D1973FD1B0
-
-★ ★ ★ ★ ☆ 95分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH BETWEEN 60
- AND 84
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: 707FE669669FA075
-
-★ ★ ★ ★ ☆ 95分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- title LIKE 'AIR%'
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: DF916439ABD07664
-
-★ ★ ★ ★ ☆ 90分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- title IS NOT 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;
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: B9336971FF3D3792
-
-★ ★ ★ ★ ☆ 85分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH = 114
- AND title = 'ALABAMA DEVIL'
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 不建议使用连续判断
-
-* **Item:** RES.009
-
-* **Severity:** L2
-
-* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
-
-# Query: 68E48001ECD53152
-
-★ ★ ★ ★ ☆ 85分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH > 100
- AND title = 'ALABAMA DEVIL'
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 不建议使用连续判断
-
-* **Item:** RES.009
-
-* **Severity:** L2
-
-* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
-
-# Query: 12FF1DAA3D425FA9
-
-★ ★ ★ ★ ☆ 85分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH > 100
- AND language_id < 10
- AND title = 'xyz'
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 不建议使用连续判断
-
-* **Item:** RES.009
-
-* **Severity:** L2
-
-* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
-
-# Query: E84CBAAC2E12BDEA
-
-★ ★ ★ ★ ☆ 85分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH > 100
- AND language_id < 10
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 不建议使用连续判断
-
-* **Item:** RES.009
-
-* **Severity:** L2
-
-* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
-
-# Query: 6A0F035BD4E01018
-
-★ ★ ★ ☆ ☆ 75分
-
-```sql
-
-SELECT
- release_year, SUM( LENGTH)
-FROM
- film
-WHERE
- LENGTH = 123
- AND language_id = 1
-GROUP BY
- release_year
-```
-
-## 请为 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'。
-
-## 使用 SUM(COL) 时需注意 NPE 问题
-
-* **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
-
-## 不建议使用连续判断
-
-* **Item:** RES.009
-
-* **Severity:** L2
-
-* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
-
-# Query: 23D176AEA2947002
-
-★ ★ ★ ★ ☆ 85分
-
-```sql
-
-SELECT
- release_year, SUM( LENGTH)
-FROM
- film
-WHERE
- LENGTH >= 123
-GROUP BY
- release_year
-```
-
-## 请为 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'。
-
-## 使用 SUM(COL) 时需注意 NPE 问题
-
-* **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
-
-# Query: 73DDF6E6D9E40384
-
-★ ★ ★ ☆ ☆ 65分
-
-```sql
-
-SELECT
- release_year, language_id, SUM( LENGTH)
-FROM
- film
-GROUP BY
- 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'。
-
-## 使用 SUM(COL) 时需注意 NPE 问题
-
-* **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
-
-# Query: B3C502B4AA344196
-
-★ ★ ★ ☆ ☆ 75分
-
-```sql
-
-SELECT
- release_year, SUM( LENGTH)
-FROM
- film
-WHERE
- LENGTH = 123
-GROUP BY
- release_year, (LENGTH+ language_id)
-```
-
-## 请为 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 条件返回的结果集较大时性能会很差。
-
-## 使用 SUM(COL) 时需注意 NPE 问题
-
-* **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
-
-# Query: 47044E1FE1A965A5
-
-★ ★ ★ ☆ ☆ 70分
-
-```sql
-
-SELECT
- release_year, SUM( film_id)
-FROM
- film
-GROUP BY
- release_year
-```
-
-## 最外层 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: 2BA1217F6C8CF0AB
-
-★ ★ ☆ ☆ ☆ 45分
-
-```sql
-
-SELECT
- *
-FROM
- address
-GROUP BY
- address, district
-```
-
-## 最外层 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'。
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 非确定性的 GROUP BY
-
-* **Item:** RES.001
-
-* **Severity:** L4
-
-* **Content:** SQL返回的列既不在聚合函数中也不是 GROUP BY 表达式的列中,因此这些值的结果将是非确定性的。如:select a, b, c from tbl where foo="bar" group by a,该 SQL 返回的结果就是不确定的。
-
-# Query: 863A85207E4F410D
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-
-SELECT
- title
-FROM
- film
-WHERE
- ABS( language_id) = 3
-GROUP BY
- title
-```
-
-## 请为 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'。
-
-## 避免在 WHERE 条件中使用函数或其他运算符
-
-* **Item:** FUN.001
-
-* **Severity:** L2
-
-* **Content:** 虽然在 SQL 中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧。也不建议在查询比较条件两侧书写多余的括号,这会对阅读产生比较大的困扰。
-
-# Query: DF59FD602E4AA368
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-
-SELECT
- language_id
-FROM
- film
-WHERE
- LENGTH = 123
-GROUP BY
- release_year
-ORDER BY
- language_id
-```
-
-## 非确定性的 GROUP BY
-
-* **Item:** RES.001
-
-* **Severity:** L4
-
-* **Content:** SQL返回的列既不在聚合函数中也不是 GROUP BY 表达式的列中,因此这些值的结果将是非确定性的。如:select a, b, c from tbl where foo="bar" group by a,该 SQL 返回的结果就是不确定的。
-
-# Query: F6DBEAA606D800FC
-
-★ ★ ★ ★ ★ 100分
-
-```sql
-
-SELECT
- release_year
-FROM
- film
-WHERE
- LENGTH = 123
-GROUP BY
- release_year
-ORDER BY
- release_year
-```
-
-## OK
-
-# Query: 6E9B96CA3F0E6BDA
-
-★ ★ ★ ☆ ☆ 65分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH = 123
-ORDER BY
- release_year ASC, language_id DESC
-```
-
-## ORDER BY 语句对多个不同条件使用不同方向的排序无法使用索引
-
-* **Item:** CLA.007
-
-* **Severity:** L2
-
-* **Content:** ORDER BY 子句中的所有表达式必须按统一的 ASC 或 DESC 方向排序,以便利用索引。
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## ORDER BY 多个列但排序方向不同时可能无法使用索引
-
-* **Item:** KEY.008
-
-* **Severity:** L4
-
-* **Content:** 在 MySQL 8.0之前当 ORDER BY 多个列指定的排序方向不同时将无法使用已经建立的索引。
-
-# Query: 2EAACFD7030EA528
-
-★ ★ ★ ★ ★ 100分
-
-```sql
-
-SELECT
- release_year
-FROM
- film
-WHERE
- LENGTH = 123
-GROUP BY
- release_year
-ORDER BY
- release_year
-LIMIT
- 10
-```
-
-## OK
-
-# Query: 5CE2F187DBF2A710
-
-★ ★ ★ ★ ☆ 95分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH = 123
-ORDER BY
- release_year
-LIMIT
- 10
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: E75234155B5E2E14
-
-★ ★ ★ ☆ ☆ 75分
-
-```sql
-
-SELECT
- *
-FROM
- film
-ORDER BY
- release_year
-LIMIT
- 10
-```
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: AFEEBF10A8D74E32
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-
-SELECT
- film_id
-FROM
- film
-ORDER BY
- release_year
-LIMIT
- 10
-```
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-# Query: 965D5AC955824512
-
-★ ★ ★ ★ ☆ 95分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH > 100
-ORDER BY
- LENGTH
-LIMIT
- 10
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: 1E2CF4145EE706A5
-
-★ ★ ★ ★ ☆ 95分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH < 100
-ORDER BY
- LENGTH
-LIMIT
- 10
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: A314542EEE8571EE
-
-★ ★ ★ ★ ☆ 95分
-
-```sql
-
-SELECT
- *
-FROM
- customer
-WHERE
- address_id in (224, 510)
-ORDER BY
- last_name
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: 0BE2D79E2F1E7CB0
-
-★ ★ ★ ★ ☆ 85分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- release_year = 2016
- AND LENGTH != 1
-ORDER BY
- title
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 不建议使用连续判断
-
-* **Item:** RES.009
-
-* **Severity:** L2
-
-* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
-
-## '!=' 运算符是非标准的
-
-* **Item:** STA.001
-
-* **Severity:** L0
-
-* **Content:** "<>"才是标准SQL中的不等于运算符。
-
-# Query: 4E73AA068370E6A8
-
-★ ★ ★ ★ ★ 100分
-
-```sql
-
-SELECT
- title
-FROM
- film
-WHERE
- release_year = 1995
-```
-
-## OK
-
-# Query: BA7111449E4F1122
-
-★ ★ ★ ★ ☆ 90分
-
-```sql
-
-SELECT
- title, replacement_cost
-FROM
- film
-WHERE
- language_id = 5
- AND LENGTH = 70
-```
-
-## 不建议使用连续判断
-
-* **Item:** RES.009
-
-* **Severity:** L2
-
-* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
-
-# Query: B13E0ACEAF8F3119
-
-★ ★ ★ ★ ☆ 90分
-
-```sql
-
-SELECT
- title
-FROM
- film
-WHERE
- language_id > 5
- AND LENGTH > 70
-```
-
-## 不建议使用连续判断
-
-* **Item:** RES.009
-
-* **Severity:** L2
-
-* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
-
-# Query: A3FAB6027484B88B
-
-★ ★ ★ ★ ☆ 85分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH = 100
- AND title = 'xyz'
-ORDER BY
- release_year
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 不建议使用连续判断
-
-* **Item:** RES.009
-
-* **Severity:** L2
-
-* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
-
-# Query: CB42080E9F35AB07
-
-★ ★ ★ ★ ☆ 85分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH > 100
- AND title = 'xyz'
-ORDER BY
- release_year
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 不建议使用连续判断
-
-* **Item:** RES.009
-
-* **Severity:** L2
-
-* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
-
-# Query: C4A212A42400411D
-
-★ ★ ★ ★ ☆ 95分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- LENGTH > 100
-ORDER BY
- release_year
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: 4ECCA9568BE69E68
-
-★ ★ ★ ☆ ☆ 75分
-
-```sql
-
-SELECT
- *
-FROM
- city a
- INNER JOIN country b ON a. country_id= b. country_id
-```
-
-## 建议使用 AS 关键字显示声明一个别名
-
-* **Item:** ALI.001
-
-* **Severity:** L0
-
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: 485D56FC88BBBDB9
-
-★ ★ ★ ☆ ☆ 75分
-
-```sql
-
-SELECT
- *
-FROM
- city a
- LEFT JOIN country b ON a. country_id= b. country_id
-```
-
-## 建议使用 AS 关键字显示声明一个别名
-
-* **Item:** ALI.001
-
-* **Severity:** L0
-
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: 0D0DABACEDFF5765
-
-★ ★ ★ ☆ ☆ 75分
-
-```sql
-
-SELECT
- *
-FROM
- city a
- RIGHT JOIN country b ON a. country_id= b. country_id
-```
-
-## 建议使用 AS 关键字显示声明一个别名
-
-* **Item:** ALI.001
-
-* **Severity:** L0
-
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: 1E56C6CCEA2131CC
-
-★ ★ ★ ★ ☆ 90分
-
-```sql
-
-SELECT
- *
-FROM
- city a
- LEFT JOIN country b ON a. country_id= b. country_id
-WHERE
- b. last_update IS NULL
-```
-
-## 建议使用 AS 关键字显示声明一个别名
-
-* **Item:** ALI.001
-
-* **Severity:** L0
-
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-
-## 应尽量避免在 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;
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: F5D30BCAC1E206A1
-
-★ ★ ★ ★ ☆ 90分
-
-```sql
-
-SELECT
- *
-FROM
- city a
- RIGHT JOIN country b ON a. country_id= b. country_id
-WHERE
- a. last_update IS NULL
-```
-
-## 建议使用 AS 关键字显示声明一个别名
-
-* **Item:** ALI.001
-
-* **Severity:** L0
-
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-
-## 应尽量避免在 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;
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-# Query: 17D5BCF21DC2364C
-
-★ ★ ★ ☆ ☆ 65分
-
-```sql
-
-SELECT
- *
-FROM
- city a
- LEFT JOIN country b ON a. country_id= b. country_id
-UNION
-SELECT
- *
-FROM
- city a
- RIGHT JOIN country b ON a. country_id= b. country_id
-```
-
-## 建议使用 AS 关键字显示声明一个别名
-
-* **Item:** ALI.001
-
-* **Severity:** L0
-
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
-
-* **Item:** SUB.002
-
-* **Severity:** L2
-
-* **Content:** 与去除重复的UNION不同,UNION ALL允许重复元组。如果您不关心重复元组,那么使用UNION ALL将是一个更快的选项。
-
-# Query: A4911095C201896F
-
-★ ★ ★ ★ ☆ 85分
-
-```sql
-
-SELECT
- *
-FROM
- city a
- RIGHT JOIN country b ON a. country_id= b. country_id
-WHERE
- a. last_update IS NULL
-UNION
-SELECT
- *
-FROM
- city a
- LEFT JOIN country b ON a. country_id= b. country_id
-WHERE
- b. last_update IS NULL
-```
-
-## 建议使用 AS 关键字显示声明一个别名
-
-* **Item:** ALI.001
-
-* **Severity:** L0
-
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
-
-* **Item:** SUB.002
-
-* **Severity:** L2
-
-* **Content:** 与去除重复的UNION不同,UNION ALL允许重复元组。如果您不关心重复元组,那么使用UNION ALL将是一个更快的选项。
-
-# Query: 3FF20E28EC9CBEF9
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-
-SELECT
- country_id, last_update
-FROM
- city NATURAL
- JOIN country
-```
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-# Query: 5C547F08EADBB131
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-
-SELECT
- country_id, last_update
-FROM
- city NATURAL
- LEFT JOIN country
-```
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-# Query: AF0C1EB58B23D2FA
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-
-SELECT
- country_id, last_update
-FROM
- city NATURAL
- RIGHT JOIN country
-```
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-# Query: 626571EAE84E2C8A
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-
-SELECT
- a. country_id, a. last_update
-FROM
- city a STRAIGHT_JOIN country b ON a. country_id= b. country_id
-```
-
-## 建议使用 AS 关键字显示声明一个别名
-
-* **Item:** ALI.001
-
-* **Severity:** L0
-
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-# Query: F76BFFC87914E3D5
-
-★ ★ ★ ☆ ☆ 60分
-
-```sql
-
-SELECT
- d. deptno, d. dname, d. loc
-FROM
- scott. dept d
-WHERE
- d. deptno IN (
-SELECT
- e. deptno
-FROM
- scott. emp e)
-```
-
-## 建议使用 AS 关键字显示声明一个别名
-
-* **Item:** ALI.001
-
-* **Severity:** L0
-
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## MySQL 对子查询的优化效果不佳
-
-* **Item:** SUB.001
-
-* **Severity:** L4
-
-* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
-
-# Query: 7253A3D336F9F3FE
-
-★ ☆ ☆ ☆ ☆ 30分
-
-```sql
-
-SELECT
- visitor_id, url
-FROM
- (
-SELECT
- id
-FROM
- LOG
-WHERE
- ip= "123.45.67.89"
-ORDER BY
- ts desc
-LIMIT
- 50, 10) I
- JOIN LOG ON (I. id= LOG. id)
- JOIN url ON (url. id= LOG. url_id)
-ORDER BY
- TS desc
-```
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## 同一张表被连接两次
-
-* **Item:** JOI.002
-
-* **Severity:** L4
-
-* **Content:** 相同的表在 FROM 子句中至少出现两次,可以简化为对该表的单次访问。
-
-## 用字符类型存储IP地址
-
-* **Item:** LIT.001
-
-* **Severity:** L2
-
-* **Content:** 字符串字面上看起来像IP地址,但不是 INET\_ATON() 的参数,表示数据被存储为字符而不是整数。将IP地址存储为整数更为有效。
-
-## MySQL 对子查询的优化效果不佳
-
-* **Item:** SUB.001
-
-* **Severity:** L4
-
-* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
-
-# Query: 7F02E23D44A38A6D
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-DELETE city, country
-FROM
- city
- INNER JOIN country using (country_id)
-WHERE
- city. city_id = 1
-```
-
-## 不建议使用联表删除或更新
-
-* **Item:** JOI.007
-
-* **Severity:** L4
-
-* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
-
-## 使用DELETE/DROP/TRUNCATE等操作时注意备份
-
-* **Item:** SEC.003
-
-* **Severity:** L0
-
-* **Content:** 在执行高危操作之前对数据进行备份是十分有必要的。
-
-# Query: F8314ABD1CBF2FF1
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-DELETE city
-FROM
- city
- LEFT JOIN country ON city. country_id = country. country_id
-WHERE
- country. country IS NULL
-```
-
-## 不建议使用联表删除或更新
-
-* **Item:** JOI.007
-
-* **Severity:** L4
-
-* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
-
-## 使用DELETE/DROP/TRUNCATE等操作时注意备份
-
-* **Item:** SEC.003
-
-* **Severity:** L0
-
-* **Content:** 在执行高危操作之前对数据进行备份是十分有必要的。
-
-# Query: 1A53649C43122975
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-DELETE a1, a2
-FROM
- city AS a1
- INNER JOIN country AS a2
-WHERE
- a1. country_id= a2. country_id
-```
-
-## 不建议使用联表删除或更新
-
-* **Item:** JOI.007
-
-* **Severity:** L4
-
-* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
-
-## 使用DELETE/DROP/TRUNCATE等操作时注意备份
-
-* **Item:** SEC.003
-
-* **Severity:** L0
-
-* **Content:** 在执行高危操作之前对数据进行备份是十分有必要的。
-
-# Query: B862978586C6338B
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-
-DELETE FROM
- a1, a2 USING city AS a1
- INNER JOIN country AS a2
-WHERE
- a1. country_id= a2. country_id
-```
-
-## 不建议使用联表删除或更新
-
-* **Item:** JOI.007
-
-* **Severity:** L4
-
-* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
-
-## 使用DELETE/DROP/TRUNCATE等操作时注意备份
-
-* **Item:** SEC.003
-
-* **Severity:** L0
-
-* **Content:** 在执行高危操作之前对数据进行备份是十分有必要的。
-
-# Query: F16FD63381EF8299
-
-★ ★ ★ ★ ★ 100分
-
-```sql
-
-DELETE FROM
- film
-WHERE
- LENGTH > 100
-```
-
-## 使用DELETE/DROP/TRUNCATE等操作时注意备份
-
-* **Item:** SEC.003
-
-* **Severity:** L0
-
-* **Content:** 在执行高危操作之前对数据进行备份是十分有必要的。
-
-# Query: 08CFE41C7D20AAC8
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-
-UPDATE
- city
- INNER JOIN country USING( country_id)
-SET
- city. city = 'Abha',
- city. last_update = '2006-02-15 04:45:25',
- country. country = 'Afghanistan'
-WHERE
- city. city_id= 10
-```
-
-## 不建议使用联表删除或更新
-
-* **Item:** JOI.007
-
-* **Severity:** L4
-
-* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
-
-# Query: C15BDF2C73B5B7ED
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-
-UPDATE
- city
- INNER JOIN country ON city. country_id = country. country_id
- INNER JOIN address ON city. city_id = address. city_id
-SET
- city. city = 'Abha',
- city. last_update = '2006-02-15 04:45:25',
- country. country = 'Afghanistan'
-WHERE
- city. city_id= 10
-```
-
-## 不建议使用联表删除或更新
-
-* **Item:** JOI.007
-
-* **Severity:** L4
-
-* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
-
-# Query: FCD1ABF36F8CDAD7
-
-★ ★ ★ ★ ☆ 90分
-
-```sql
-
-UPDATE
- city, country
-SET
- city. city = 'Abha',
- city. last_update = '2006-02-15 04:45:25',
- country. country = 'Afghanistan'
-WHERE
- city. country_id = country. country_id
- AND city. city_id= 10
-```
-
-## 不建议使用连续判断
-
-* **Item:** RES.009
-
-* **Severity:** L2
-
-* **Content:** 类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
-
-# Query: FE409EB794EE91CF
-
-★ ★ ★ ★ ★ 100分
-
-```sql
-
-UPDATE
- film
-SET
- LENGTH = 10
-WHERE
- language_id = 20
-```
-
-## OK
-
-# Query: 3656B13CC4F888E2
-
-★ ★ ★ ☆ ☆ 65分
-
-```sql
-INSERT INTO city (country_id)
-SELECT
- country_id
-FROM
- country
-```
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## INSERT INTO xx SELECT 加锁粒度较大请谨慎
-
-* **Item:** LCK.001
-
-* **Severity:** L3
-
-* **Content:** INSERT INTO xx SELECT 加锁粒度较大请谨慎
-
-# Query: 2F7439623B712317
-
-★ ★ ★ ★ ★ 100分
-
-```sql
-INSERT INTO city (country_id)
-VALUES
- (1),
- (2),
- (3)
-```
-
-## OK
-
-# Query: 11EC7AAACC97DC0F
-
-★ ★ ★ ★ ☆ 85分
-
-```sql
-INSERT INTO city (country_id)
-SELECT
- 10
-FROM
- DUAL
-```
-
-## INSERT INTO xx SELECT 加锁粒度较大请谨慎
-
-* **Item:** LCK.001
-
-* **Severity:** L3
-
-* **Content:** INSERT INTO xx SELECT 加锁粒度较大请谨慎
-
-# Query: E3DDA1A929236E72
-
-★ ★ ★ ☆ ☆ 65分
-
-```sql
-REPLACE INTO city (country_id)
-SELECT
- country_id
-FROM
- country
-```
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## INSERT INTO xx SELECT 加锁粒度较大请谨慎
-
-* **Item:** LCK.001
-
-* **Severity:** L3
-
-* **Content:** INSERT INTO xx SELECT 加锁粒度较大请谨慎
-
-# Query: 466F1AC2F5851149
-
-★ ★ ★ ★ ★ 100分
-
-```sql
-REPLACE INTO city (country_id)
-VALUES
- (1),
- (2),
- (3)
-```
-
-## OK
-
-# Query: A7973BDD268F926E
-
-★ ★ ★ ★ ☆ 85分
-
-```sql
-REPLACE INTO city (country_id)
-SELECT
- 10
-FROM
- DUAL
-```
-
-## INSERT INTO xx SELECT 加锁粒度较大请谨慎
-
-* **Item:** LCK.001
-
-* **Severity:** L3
-
-* **Content:** INSERT INTO xx SELECT 加锁粒度较大请谨慎
-
-# Query: 105C870D5DFB6710
-
-★ ★ ★ ☆ ☆ 65分
-
-```sql
-
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- (
-SELECT
- film_id
-FROM
- film
-) film
-) film
-) film
-) film
-) film
-) film
-) film
-) film
-) film
-) film
-) film
-) film
-) film
-) film
-) film
-) film
-```
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## 执行计划中嵌套连接深度过深
-
-* **Item:** SUB.004
-
-* **Severity:** L3
-
-* **Content:** MySQL对子查询的优化效果不佳,MySQL将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。
-
-# Query: 16C2B14E7DAA9906
-
-★ ☆ ☆ ☆ ☆ 35分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- language_id = (
-SELECT
- language_id
-FROM
- language
-LIMIT
- 1)
-```
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 未使用 ORDER BY 的 LIMIT 查询
-
-* **Item:** RES.002
-
-* **Severity:** L4
-
-* **Content:** 没有 ORDER BY 的 LIMIT 会导致非确定性的结果,这取决于查询执行计划。
-
-## MySQL 对子查询的优化效果不佳
-
-* **Item:** SUB.001
-
-* **Severity:** L4
-
-* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
-
-# Query: 16CB4628D2597D40
-
-★ ★ ★ ☆ ☆ 65分
-
-```sql
-
-SELECT
- *
-FROM
- city i
- LEFT JOIN country o ON i. city_id= o. country_id
-UNION
-SELECT
- *
-FROM
- city i
- RIGHT JOIN country o ON i. city_id= o. country_id
-```
-
-## 建议使用 AS 关键字显示声明一个别名
-
-* **Item:** ALI.001
-
-* **Severity:** L0
-
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
-
-* **Item:** SUB.002
-
-* **Severity:** L2
-
-* **Content:** 与去除重复的UNION不同,UNION ALL允许重复元组。如果您不关心重复元组,那么使用UNION ALL将是一个更快的选项。
-
-# Query: EA50643B01E139A8
-
-★ ☆ ☆ ☆ ☆ 35分
-
-```sql
-
-SELECT
- *
-FROM
- (
-SELECT
- *
-FROM
- actor
-WHERE
- last_update= '2006-02-15 04:34:33'
- AND last_name= 'CHASE'
-) t
-WHERE
- last_update= '2006-02-15 04:34:33'
- AND last_name= 'CHASE'
-GROUP BY
- first_name
-```
-
-## 请为 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'。
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 非确定性的 GROUP BY
-
-* **Item:** RES.001
-
-* **Severity:** L4
-
-* **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 对子查询的优化效果不佳
-
-* **Item:** SUB.001
-
-* **Severity:** L4
-
-* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
-
-# Query: 7598A4EDE6CFA6BE
-
-★ ★ ★ ★ ☆ 85分
-
-```sql
-
-SELECT
- *
-FROM
- city i
- LEFT JOIN country o ON i. city_id= o. country_id
-WHERE
- o. country_id is null
-UNION
-SELECT
- *
-FROM
- city i
- RIGHT JOIN country o ON i. city_id= o. country_id
-WHERE
- i. city_id is null
-```
-
-## 建议使用 AS 关键字显示声明一个别名
-
-* **Item:** ALI.001
-
-* **Severity:** L0
-
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
-
-* **Item:** SUB.002
-
-* **Severity:** L2
-
-* **Content:** 与去除重复的UNION不同,UNION ALL允许重复元组。如果您不关心重复元组,那么使用UNION ALL将是一个更快的选项。
-
-# Query: 1E8B70E30062FD13
-
-★ ★ ★ ★ ☆ 80分
-
-```sql
-
-SELECT
- first_name, last_name, email
-FROM
- customer STRAIGHT_JOIN address ON customer. address_id= address. address_id
-```
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-# Query: E48A20D0413512DA
-
-★ ☆ ☆ ☆ ☆ 30分
-
-```sql
-
-SELECT
- ID, name
-FROM
- (
-SELECT
- address
-FROM
- customer_list
-WHERE
- SID= 1
-ORDER BY
- phone
-LIMIT
- 50, 10) a
- JOIN customer_list l ON (a. address= l. address)
- JOIN city c ON (c. city= l. city)
-ORDER BY
- phone desc
-```
-
-## 建议使用 AS 关键字显示声明一个别名
-
-* **Item:** ALI.001
-
-* **Severity:** L0
-
-* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
-
-## 最外层 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 方向排序,以便利用索引。
-
-## 同一张表被连接两次
-
-* **Item:** JOI.002
-
-* **Severity:** L4
-
-* **Content:** 相同的表在 FROM 子句中至少出现两次,可以简化为对该表的单次访问。
-
-## MySQL 对子查询的优化效果不佳
-
-* **Item:** SUB.001
-
-* **Severity:** L4
-
-* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
-
-# Query: B0BA5A7079EA16B3
-
-★ ★ ★ ★ ☆ 85分
-
-```sql
-
-SELECT
- *
-FROM
- film
-WHERE
- DATE( last_update) = '2006-02-15'
-```
-
-## 不建议使用 SELECT * 类型查询
-
-* **Item:** COL.001
-
-* **Severity:** L1
-
-* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
-
-## 避免在 WHERE 条件中使用函数或其他运算符
-
-* **Item:** FUN.001
-
-* **Severity:** L2
-
-* **Content:** 虽然在 SQL 中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧。也不建议在查询比较条件两侧书写多余的括号,这会对阅读产生比较大的困扰。
-
-# Query: 18A2AD1395A58EAE
-
-★ ★ ★ ☆ ☆ 60分
-
-```sql
-
-SELECT
- last_update
-FROM
- film
-GROUP BY
- DATE( last_update)
-```
-
-## 最外层 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'。
-
-## GROUP BY 的条件为表达式
-
-* **Item:** CLA.010
-
-* **Severity:** L2
-
-* **Content:** 当 GROUP BY 条件为表达式或函数时会使用到临时表,如果在未指定 WHERE 或 WHERE 条件返回的结果集较大时性能会很差。
-
-# Query: 60F234BA33AAC132
-
-★ ★ ★ ☆ ☆ 70分
-
-```sql
-
-SELECT
- last_update
-FROM
- film
-ORDER BY
- DATE( last_update)
-```
-
-## 最外层 SELECT 未指定 WHERE 条件
-
-* **Item:** CLA.001
-
-* **Severity:** L4
-
-* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
-
-## ORDER BY 的条件为表达式
-
-* **Item:** CLA.009
-
-* **Severity:** L2
-
-* **Content:** 当 ORDER BY 条件为表达式或函数时会使用到临时表,如果在未指定 WHERE 或 WHERE 条件返回的结果集较大时性能会很差。
-
-# Query: 1ED2B7ECBA4215E1
-
-★ ★ ★ ★ ☆ 90分
-
-```sql
-
-SELECT
- description
-FROM
- film
-WHERE
- description IN( 'NEWS',
- 'asd'
-)
-GROUP BY
- description
-```
-
-## 请为 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: 255BAC03F56CDBC7
-
-★ ★ ★ ★ ★ 100分
-
-```sql
-
-ALTER TABLE
- address
-ADD
- index idx_city_id( city_id)
-```
-
-## OK
-
-# Query: C315BC4EE0F4E523
-
-★ ★ ★ ★ ★ 100分
-
-```sql
-
-ALTER TABLE
- inventory
-ADD
- index `idx_store_film` (
- `store_id`, `film_id`)
-```
-
-## 提醒:请将索引属性顺序与查询对齐
-
-* **Item:** KEY.004
-
-* **Severity:** L0
-
-* **Content:** 如果为列创建复合索引,请确保查询属性与索引属性的顺序相同,以便DBMS在处理查询时使用索引。如果查询和索引属性订单没有对齐,那么DBMS可能无法在查询处理期间使用索引。
-
-# Query: 9BB74D074BA0727C
-
-★ ★ ★ ★ ★ 100分
-
-```sql
-
-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`)
-```
-
-## 提醒:请将索引属性顺序与查询对齐
-
-* **Item:** KEY.004
-
-* **Severity:** L0
-
-* **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
deleted file mode 100755
index 8eb2d7c2bb449a88509884d4d951fd13db74dda0..0000000000000000000000000000000000000000
--- a/doc/example/main_test.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/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
- 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
- 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/example/sakila.sql.gz b/doc/example/sakila.sql.gz
deleted file mode 100644
index 5cdf8c7798a5220a6388cc2e4a50620fef4ad311..0000000000000000000000000000000000000000
Binary files a/doc/example/sakila.sql.gz and /dev/null differ
diff --git a/test/fixture/test_Run_all_test_cases.golden b/test/fixture/test_Run_all_test_cases.golden
new file mode 100644
index 0000000000000000000000000000000000000000..27a102f90cc27088bab8adb78e9729dae49e94c1
--- /dev/null
+++ b/test/fixture/test_Run_all_test_cases.golden
@@ -0,0 +1,4643 @@
+# Query: C3FAEDA6AD6D762B
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+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 | NULL | NULL | 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
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: E969B9297DA79BA6
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+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 | NULL | NULL | 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
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
+
+
+
+## 应尽量避免在 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;
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 8A106444D14B9880
+
+★ ★ ★ ☆ ☆ 60分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+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 | NULL | NULL | NULL | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## 不建议使用 HAVING 子句
+
+* **Item:** CLA.013
+
+* **Severity:** L3
+
+* **Content:** 将查询的 HAVING 子句改写为 WHERE 中的查询条件,可以在查询处理期间使用索引。
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: A0C5E62C724A121A
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ sakila. film
+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 | NULL | NULL | 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
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 868317D1973FD1B0
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ LENGTH BETWEEN 60
+ 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 | NULL | NULL | 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
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 707FE669669FA075
+
+★ ★ ★ ★ ☆ 95分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+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 | NULL | 2 | ☠️ **100.00%** | ☠️ **O(n)** | Using index condition |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* **range**: 只检索给定范围的行, 使用一个索引来选择行. key列显示使用了哪个索引. key_len包含所使用索引的最长关键元素.
+
+#### Extra信息解读
+
+* **Using index condition**: 在5.6版本后加入的新特性(Index Condition Pushdown)。Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行。
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: DF916439ABD07664
+
+★ ★ ★ ★ ☆ 90分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+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 | NULL | 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 值判断
+
+* **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;
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: B9336971FF3D3792
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ LENGTH = 114
+ 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信息解读
+
+* **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
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_title\_length\` (\`title\`,\`length\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 68E48001ECD53152
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ LENGTH > 100
+ 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 |
+
+
+
+### 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
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_title\_length\` (\`title\`,\`length\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 12FF1DAA3D425FA9
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ LENGTH > 100
+ AND language_id < 10
+ 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信息解读
+
+* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_title\_length\` (\`title\`,\`length\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: E84CBAAC2E12BDEA
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ LENGTH > 100
+ 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 | NULL | 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
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 6A0F035BD4E01018
+
+★ ★ ★ ☆ ☆ 75分
+
+```sql
+
+SELECT
+ release_year, SUM( LENGTH)
+FROM
+ film
+WHERE
+ LENGTH = 123
+ AND language_id = 1
+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 | NULL | NULL | 1000 | 10.00% | ☠️ **O(n)** | Using where; Using temporary |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_language\_id\_release\_year\` (\`length\`,\`language\_id\`,\`release\_year\`) ;
+
+
+
+## 请为 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'。
+
+## 使用 SUM(COL) 时需注意 NPE 问题
+
+* **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
+
+# Query: 23D176AEA2947002
+
+★ ★ ★ ☆ ☆ 75分
+
+```sql
+
+SELECT
+ release_year, SUM( LENGTH)
+FROM
+ film
+WHERE
+ LENGTH >= 123
+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 | NULL | NULL | NULL | 1000 | 33.33% | ☠️ **O(n)** | Using where; Using temporary |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_release\_year\` (\`length\`,\`release\_year\`) ;
+
+
+
+## 请为 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'。
+
+## 使用 SUM(COL) 时需注意 NPE 问题
+
+* **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
+
+# Query: 73DDF6E6D9E40384
+
+★ ★ ☆ ☆ ☆ 55分
+
+```sql
+
+SELECT
+ release_year, language_id, SUM( LENGTH)
+FROM
+ film
+GROUP BY
+ release_year, 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 | NULL | NULL | 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
+
+
+* **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'。
+
+## 使用 SUM(COL) 时需注意 NPE 问题
+
+* **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
+
+# Query: B3C502B4AA344196
+
+★ ★ ★ ☆ ☆ 65分
+
+```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 | NULL | NULL | NULL | 1000 | 10.00% | ☠️ **O(n)** | Using where; Using temporary |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **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 条件返回的结果集较大时性能会很差。
+
+## 使用 SUM(COL) 时需注意 NPE 问题
+
+* **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
+
+# Query: 47044E1FE1A965A5
+
+★ ★ ★ ☆ ☆ 60分
+
+```sql
+
+SELECT
+ release_year, SUM( film_id)
+FROM
+ film
+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 | NULL | NULL | 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
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_release\_year\` (\`release\_year\`) ;
+
+
+
+## 最外层 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: 2BA1217F6C8CF0AB
+
+★ ☆ ☆ ☆ ☆ 35分
+
+```sql
+
+SELECT
+ *
+FROM
+ address
+GROUP BY
+ address, district
+```
+
+## 为sakila库的address表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`address\` add index \`idx\_address\_district\` (\`address\`,\`district\`) ;
+
+
+
+## 最外层 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'。
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+## 非确定性的 GROUP BY
+
+* **Item:** RES.001
+
+* **Severity:** L4
+
+* **Content:** SQL返回的列既不在聚合函数中也不是 GROUP BY 表达式的列中,因此这些值的结果将是非确定性的。如:select a, b, c from tbl where foo="bar" group by a,该 SQL 返回的结果就是不确定的。
+
+# Query: 863A85207E4F410D
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+SELECT
+ title
+FROM
+ film
+WHERE
+ ABS( language_id) = 3
+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 | NULL | 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 条件
+
+* **Item:** CLA.008
+
+* **Severity:** L2
+
+* **Content:** 默认 MySQL 会对 'GROUP BY col1, col2, ...' 请求按如下顺序排序 'ORDER BY col1, col2, ...'。如果 GROUP BY 语句不指定 ORDER BY 条件会导致无谓的排序产生,如果不需要排序建议添加 'ORDER BY NULL'。
+
+## 避免在 WHERE 条件中使用函数或其他运算符
+
+* **Item:** FUN.001
+
+* **Severity:** L2
+
+* **Content:** 虽然在 SQL 中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧。也不建议在查询比较条件两侧书写多余的括号,这会对阅读产生比较大的困扰。
+
+# Query: DF59FD602E4AA368
+
+★ ★ ★ ☆ ☆ 70分
+
+```sql
+
+SELECT
+ language_id
+FROM
+ film
+WHERE
+ LENGTH = 123
+GROUP BY
+ release_year
+ORDER BY
+ language_id
+```
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_release\_year\` (\`length\`,\`release\_year\`) ;
+
+
+
+## 非确定性的 GROUP BY
+
+* **Item:** RES.001
+
+* **Severity:** L4
+
+* **Content:** SQL返回的列既不在聚合函数中也不是 GROUP BY 表达式的列中,因此这些值的结果将是非确定性的。如:select a, b, c from tbl where foo="bar" group by a,该 SQL 返回的结果就是不确定的。
+
+# Query: F6DBEAA606D800FC
+
+★ ★ ★ ★ ☆ 90分
+
+```sql
+
+SELECT
+ release_year
+FROM
+ film
+WHERE
+ LENGTH = 123
+GROUP BY
+ release_year
+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 | NULL | NULL | NULL | 1000 | 10.00% | ☠️ **O(n)** | Using where; Using temporary; Using filesort |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_release\_year\` (\`length\`,\`release\_year\`) ;
+
+
+
+# Query: 6E9B96CA3F0E6BDA
+
+★ ★ ☆ ☆ ☆ 55分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ LENGTH = 123
+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 | NULL | NULL | NULL | 1000 | 10.00% | ☠️ **O(n)** | Using where; Using filesort |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
+
+
+
+## ORDER BY 语句对多个不同条件使用不同方向的排序无法使用索引
+
+* **Item:** CLA.007
+
+* **Severity:** L2
+
+* **Content:** ORDER BY 子句中的所有表达式必须按统一的 ASC 或 DESC 方向排序,以便利用索引。
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+## ORDER BY 多个列但排序方向不同时可能无法使用索引
+
+* **Item:** KEY.008
+
+* **Severity:** L4
+
+* **Content:** 在 MySQL 8.0之前当 ORDER BY 多个列指定的排序方向不同时将无法使用已经建立的索引。
+
+# Query: 2EAACFD7030EA528
+
+★ ★ ★ ★ ☆ 90分
+
+```sql
+
+SELECT
+ release_year
+FROM
+ film
+WHERE
+ LENGTH = 123
+GROUP BY
+ release_year
+ORDER BY
+ release_year
+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 | NULL | NULL | NULL | 1000 | 10.00% | ☠️ **O(n)** | Using where; Using temporary; Using filesort |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_release\_year\` (\`length\`,\`release\_year\`) ;
+
+
+
+# Query: 5CE2F187DBF2A710
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ LENGTH = 123
+ORDER BY
+ release_year
+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 | NULL | NULL | NULL | 1000 | 10.00% | ☠️ **O(n)** | Using where; Using filesort |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_release\_year\` (\`length\`,\`release\_year\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: E75234155B5E2E14
+
+★ ★ ★ ☆ ☆ 75分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+ORDER BY
+ release_year
+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 | NULL | NULL | NULL | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using filesort |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: AFEEBF10A8D74E32
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+SELECT
+ film_id
+FROM
+ film
+ORDER BY
+ release_year
+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 | NULL | NULL | NULL | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using filesort |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+# Query: 965D5AC955824512
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ LENGTH > 100
+ORDER BY
+ LENGTH
+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 | NULL | NULL | NULL | 1000 | 33.33% | ☠️ **O(n)** | Using where; Using filesort |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 1E2CF4145EE706A5
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ LENGTH < 100
+ORDER BY
+ LENGTH
+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 | NULL | NULL | NULL | 1000 | 33.33% | ☠️ **O(n)** | Using where; Using filesort |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: A314542EEE8571EE
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ customer
+WHERE
+ address_id in (224, 510)
+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 | NULL | 2 | ☠️ **100.00%** | ☠️ **O(n)** | Using index condition; Using filesort |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* **range**: 只检索给定范围的行, 使用一个索引来选择行. key列显示使用了哪个索引. key_len包含所使用索引的最长关键元素.
+
+#### Extra信息解读
+
+* **Using index condition**: 在5.6版本后加入的新特性(Index Condition Pushdown)。Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行。
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+
+## 为sakila库的customer表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`customer\` add index \`idx\_address\_id\_last\_name\` (\`address\_id\`,\`last\_name\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 0BE2D79E2F1E7CB0
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ release_year = 2016
+ AND LENGTH != 1
+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 | NULL | NULL | NULL | 1000 | 9.00% | ☠️ **O(n)** | Using where; Using filesort |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_release\_year\_length\_title\` (\`release\_year\`,\`length\`,\`title\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+## '!=' 运算符是非标准的
+
+* **Item:** STA.001
+
+* **Severity:** L0
+
+* **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 | NULL | NULL | 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
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_release\_year\` (\`release\_year\`) ;
+
+
+
+# Query: BA7111449E4F1122
+
+★ ★ ★ ★ ☆ 90分
+
+```sql
+
+SELECT
+ title, replacement_cost
+FROM
+ film
+WHERE
+ language_id = 5
+ 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
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_language\_id\` (\`length\`,\`language\_id\`) ;
+
+
+
+# Query: B13E0ACEAF8F3119
+
+★ ★ ★ ★ ☆ 90分
+
+```sql
+
+SELECT
+ title
+FROM
+ film
+WHERE
+ language_id > 5
+ 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 | NULL | 1 | 33.33% | ☠️ **O(n)** | Using index condition; Using where |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* **range**: 只检索给定范围的行, 使用一个索引来选择行. key列显示使用了哪个索引. key_len包含所使用索引的最长关键元素.
+
+#### Extra信息解读
+
+* **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
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
+
+
+
+# Query: A3FAB6027484B88B
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ LENGTH = 100
+ AND title = 'xyz'
+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或子查询等).
+
+#### Type信息解读
+
+* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
+
+#### Extra信息解读
+
+* **Using index condition**: 在5.6版本后加入的新特性(Index Condition Pushdown)。Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行。
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_title\_length\_release\_year\` (\`title\`,\`length\`,\`release\_year\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: CB42080E9F35AB07
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ LENGTH > 100
+ AND title = 'xyz'
+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 | 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 index condition**: 在5.6版本后加入的新特性(Index Condition Pushdown)。Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行。
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_title\_length\_release\_year\` (\`title\`,\`length\`,\`release\_year\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: C4A212A42400411D
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ LENGTH > 100
+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 | NULL | NULL | NULL | 1000 | 33.33% | ☠️ **O(n)** | Using where; Using filesort |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\_release\_year\` (\`length\`,\`release\_year\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 4ECCA9568BE69E68
+
+★ ★ ★ ☆ ☆ 75分
+
+```sql
+
+SELECT
+ *
+FROM
+ city a
+ 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 | NULL | 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信息解读
+
+* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+
+## 建议使用 AS 关键字显示声明一个别名
+
+* **Item:** ALI.001
+
+* **Severity:** L0
+
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 485D56FC88BBBDB9
+
+★ ★ ★ ☆ ☆ 75分
+
+```sql
+
+SELECT
+ *
+FROM
+ city a
+ 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 | NULL | NULL | 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信息解读
+
+* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例: 'SELECT * FROM RefTbl, tbl WHERE RefTbl.col=tbl.col;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+
+## 建议使用 AS 关键字显示声明一个别名
+
+* **Item:** ALI.001
+
+* **Severity:** L0
+
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 0D0DABACEDFF5765
+
+★ ★ ★ ☆ ☆ 75分
+
+```sql
+
+SELECT
+ *
+FROM
+ city a
+ 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 | NULL | NULL | 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信息解读
+
+* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+
+## 建议使用 AS 关键字显示声明一个别名
+
+* **Item:** ALI.001
+
+* **Severity:** L0
+
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 1E56C6CCEA2131CC
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+SELECT
+ *
+FROM
+ city a
+ LEFT JOIN country b ON a. country_id= b. country_id
+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 | NULL | NULL | 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信息解读
+
+* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例: 'SELECT * FROM RefTbl, tbl WHERE RefTbl.col=tbl.col;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Not exists**: MySQL能够对LEFT JOIN查询进行优化, 并且在查找到符合LEFT JOIN条件的行后, 则不再查找更多的行.
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+
+## 为sakila库的country表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`country\` add index \`idx\_last\_update\` (\`last\_update\`) ;
+
+
+
+## 建议使用 AS 关键字显示声明一个别名
+
+* **Item:** ALI.001
+
+* **Severity:** L0
+
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
+
+## 应尽量避免在 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;
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: F5D30BCAC1E206A1
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+SELECT
+ *
+FROM
+ city a
+ RIGHT JOIN country b ON a. country_id= b. country_id
+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 | NULL | NULL | 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信息解读
+
+* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Not exists**: MySQL能够对LEFT JOIN查询进行优化, 并且在查找到符合LEFT JOIN条件的行后, 则不再查找更多的行.
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+
+## 为sakila库的city表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`city\` add index \`idx\_last\_update\` (\`last\_update\`) ;
+
+
+
+## 建议使用 AS 关键字显示声明一个别名
+
+* **Item:** ALI.001
+
+* **Severity:** L0
+
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
+
+## 应尽量避免在 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;
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+# Query: 17D5BCF21DC2364C
+
+★ ★ ★ ☆ ☆ 65分
+
+```sql
+
+SELECT
+ *
+FROM
+ city a
+ LEFT JOIN country b ON a. country_id= b. country_id
+UNION
+SELECT
+ *
+FROM
+ city a
+ 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 | NULL | NULL | 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 | NULL | NULL | 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 |
+| 2 | UNION | *a* | NULL | ref | idx\_fk\_country\_id | idx\_fk\_country\_id | 2 | sakila.b.country\_id | 5 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **PRIMARY**: 最外层的select.
+
+* **UNION**: UNION中的第二个或后面的SELECT查询, 不依赖于外部查询的结果集.
+
+#### Type信息解读
+
+* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例: 'SELECT * FROM RefTbl, tbl WHERE RefTbl.col=tbl.col;'.
+
+* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+
+## 建议使用 AS 关键字显示声明一个别名
+
+* **Item:** ALI.001
+
+* **Severity:** L0
+
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
+
+* **Item:** SUB.002
+
+* **Severity:** L2
+
+* **Content:** 与去除重复的UNION不同,UNION ALL允许重复元组。如果您不关心重复元组,那么使用UNION ALL将是一个更快的选项。
+
+# Query: A4911095C201896F
+
+★ ★ ★ ☆ ☆ 65分
+
+```sql
+
+SELECT
+ *
+FROM
+ city a
+ RIGHT JOIN country b ON a. country_id= b. country_id
+WHERE
+ a. last_update IS NULL
+UNION
+SELECT
+ *
+FROM
+ city a
+ LEFT JOIN country b ON a. country_id= b. country_id
+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 | NULL | NULL | 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 | NULL | NULL | 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 |
+| 2 | UNION | *b* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.a.country\_id | 1 | 10.00% | ☠️ **O(n)** | Using where; Not exists |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **PRIMARY**: 最外层的select.
+
+* **UNION**: UNION中的第二个或后面的SELECT查询, 不依赖于外部查询的结果集.
+
+#### Type信息解读
+
+* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例: 'SELECT * FROM RefTbl, tbl WHERE RefTbl.col=tbl.col;'.
+
+* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Not exists**: MySQL能够对LEFT JOIN查询进行优化, 并且在查找到符合LEFT JOIN条件的行后, 则不再查找更多的行.
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+
+## 为sakila库的city表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`city\` add index \`idx\_last\_update\` (\`last\_update\`) ;
+
+
+
+## 为sakila库的country表添加索引
+
+* **Item:** IDX.002
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`country\` add index \`idx\_last\_update\` (\`last\_update\`) ;
+
+
+
+## 建议使用 AS 关键字显示声明一个别名
+
+* **Item:** ALI.001
+
+* **Severity:** L0
+
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
+
+* **Item:** SUB.002
+
+* **Severity:** L2
+
+* **Content:** 与去除重复的UNION不同,UNION ALL允许重复元组。如果您不关心重复元组,那么使用UNION ALL将是一个更快的选项。
+
+# Query: 3FF20E28EC9CBEF9
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+SELECT
+ country_id, last_update
+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 | NULL | 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信息解读
+
+* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+# Query: 5C547F08EADBB131
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+SELECT
+ country_id, last_update
+FROM
+ city NATURAL
+ 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 | NULL | NULL | 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信息解读
+
+* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例: 'SELECT * FROM RefTbl, tbl WHERE RefTbl.col=tbl.col;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+# Query: AF0C1EB58B23D2FA
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+SELECT
+ country_id, last_update
+FROM
+ city NATURAL
+ 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 | NULL | NULL | 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信息解读
+
+* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+# Query: 626571EAE84E2C8A
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+SELECT
+ a. country_id, a. last_update
+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 | NULL | 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信息解读
+
+* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例: 'SELECT * FROM RefTbl, tbl WHERE RefTbl.col=tbl.col;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
+
+
+## 建议使用 AS 关键字显示声明一个别名
+
+* **Item:** ALI.001
+
+* **Severity:** L0
+
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+# Query: 50F2AB4243CE2071
+
+★ ★ ★ ☆ ☆ 60分
+
+```sql
+
+SELECT
+ a. address, a. postal_code
+FROM
+ sakila. address a
+WHERE
+ a. city_id IN (
+SELECT
+ c. city_id
+FROM
+ sakila. city c)
+```
+
+## Explain信息
+
+| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
+|---|---|---|---|---|---|---|---|---|---|---|---|---|
+| 1 | SIMPLE | *c* | NULL | index | PRIMARY | idx\_fk\_country\_id | 2 | NULL | 600 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
+| 1 | SIMPLE | *a* | NULL | ref | idx\_fk\_city\_id | idx\_fk\_city\_id | 2 | sakila.c.city\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* **index**: 全表扫描, 只是扫描表的时候按照索引次序进行而不是行. 主要优点就是避免了排序, 但是开销仍然非常大.
+
+* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
+
+#### Extra信息解读
+
+* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
+
+
+## 建议使用 AS 关键字显示声明一个别名
+
+* **Item:** ALI.001
+
+* **Severity:** L0
+
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## MySQL 对子查询的优化效果不佳
+
+* **Item:** SUB.001
+
+* **Severity:** L4
+
+* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
+
+# Query: 584CCEC8069B6947
+
+★ ☆ ☆ ☆ ☆ 30分
+
+```sql
+
+SELECT
+ city
+FROM(
+SELECT
+ city_id
+FROM
+ city
+WHERE
+ city = "A Corua (La Corua)"
+ORDER BY
+ last_update DESC
+LIMIT
+ 50, 10) I
+ JOIN city ON (I. city_id = city. city_id)
+ JOIN country ON (country. country_id = city. country_id)
+ORDER BY
+ city DESC
+```
+
+## Explain信息
+
+| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
+|---|---|---|---|---|---|---|---|---|---|---|---|---|
+| 1 | PRIMARY | ** | NULL | ALL | NULL | NULL | NULL | NULL | 60 | ☠️ **100.00%** | ☠️ **O(n)** | Using temporary; Using filesort |
+| 1 | PRIMARY | *city* | NULL | eq\_ref | PRIMARY,
idx\_fk\_country\_id | PRIMARY | 2 | I.city\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
+| 1 | PRIMARY | *country* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.city.country\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
+| 2 | DERIVED | *city* | NULL | ALL | NULL | NULL | NULL | NULL | 600 | 10.00% | ☠️ **O(n)** | Using where; Using filesort |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **DERIVED**: 用于from子句里有子查询的情况. MySQL会递归执行这些子查询, 把结果放在临时表里.
+
+* **PRIMARY**: 最外层的select.
+
+#### Type信息解读
+
+* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例: 'SELECT * FROM RefTbl, tbl WHERE RefTbl.col=tbl.col;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
+
+
+## 为sakila库的city表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`city\` add index \`idx\_city\_last\_update\` (\`city\`,\`last\_update\`) ;
+
+
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## 同一张表被连接两次
+
+* **Item:** JOI.002
+
+* **Severity:** L4
+
+* **Content:** 相同的表在 FROM 子句中至少出现两次,可以简化为对该表的单次访问。
+
+## MySQL 对子查询的优化效果不佳
+
+* **Item:** SUB.001
+
+* **Severity:** L4
+
+* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
+
+# Query: 7F02E23D44A38A6D
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+DELETE city, country
+FROM
+ city
+ INNER JOIN country using (country_id)
+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 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
+
+## 使用DELETE/DROP/TRUNCATE等操作时注意备份
+
+* **Item:** SEC.003
+
+* **Severity:** L0
+
+* **Content:** 在执行高危操作之前对数据进行备份是十分有必要的。
+
+# Query: F8314ABD1CBF2FF1
+
+★ ★ ★ ☆ ☆ 70分
+
+```sql
+DELETE city
+FROM
+ city
+ LEFT JOIN country ON city. country_id = country. country_id
+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 | NULL | NULL | 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信息解读
+
+* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例: 'SELECT * FROM RefTbl, tbl WHERE RefTbl.col=tbl.col;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Not exists**: MySQL能够对LEFT JOIN查询进行优化, 并且在查找到符合LEFT JOIN条件的行后, 则不再查找更多的行.
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+
+## 为sakila库的country表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`country\` add index \`idx\_country\` (\`country\`) ;
+
+
+
+## 不建议使用联表删除或更新
+
+* **Item:** JOI.007
+
+* **Severity:** L4
+
+* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
+
+## 使用DELETE/DROP/TRUNCATE等操作时注意备份
+
+* **Item:** SEC.003
+
+* **Severity:** L0
+
+* **Content:** 在执行高危操作之前对数据进行备份是十分有必要的。
+
+# Query: 1A53649C43122975
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+DELETE a1, a2
+FROM
+ city AS a1
+ INNER JOIN country AS a2
+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 | NULL | 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信息解读
+
+* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+
+## 不建议使用联表删除或更新
+
+* **Item:** JOI.007
+
+* **Severity:** L4
+
+* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
+
+## 使用DELETE/DROP/TRUNCATE等操作时注意备份
+
+* **Item:** SEC.003
+
+* **Severity:** L0
+
+* **Content:** 在执行高危操作之前对数据进行备份是十分有必要的。
+
+# Query: B862978586C6338B
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+DELETE FROM
+ a1, a2 USING city AS a1
+ INNER JOIN country AS a2
+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 | NULL | 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信息解读
+
+* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+
+## 不建议使用联表删除或更新
+
+* **Item:** JOI.007
+
+* **Severity:** L4
+
+* **Content:** 当需要同时删除或更新多张表时建议使用简单语句,一条 SQL 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
+
+## 使用DELETE/DROP/TRUNCATE等操作时注意备份
+
+* **Item:** SEC.003
+
+* **Severity:** L0
+
+* **Content:** 在执行高危操作之前对数据进行备份是十分有必要的。
+
+# Query: F16FD63381EF8299
+
+★ ★ ★ ★ ☆ 90分
+
+```sql
+
+DELETE FROM
+ film
+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 | NULL | NULL | 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
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
+
+
+
+## 使用DELETE/DROP/TRUNCATE等操作时注意备份
+
+* **Item:** SEC.003
+
+* **Severity:** L0
+
+* **Content:** 在执行高危操作之前对数据进行备份是十分有必要的。
+
+# Query: 08CFE41C7D20AAC8
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+UPDATE
+ city
+ INNER JOIN country USING( country_id)
+SET
+ city. city = 'Abha',
+ city. last_update = '2006-02-15 04:45:25',
+ country. country = 'Afghanistan'
+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 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
+
+# Query: C15BDF2C73B5B7ED
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+UPDATE
+ city
+ INNER JOIN country ON city. country_id = country. country_id
+ INNER JOIN address ON city. city_id = address. city_id
+SET
+ city. city = 'Abha',
+ city. last_update = '2006-02-15 04:45:25',
+ country. country = 'Afghanistan'
+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 只删除或更新一张表,尽量不要将多张表的操作在同一条语句。
+
+# Query: FCD1ABF36F8CDAD7
+
+★ ★ ★ ★ ★ 100分
+
+```sql
+
+UPDATE
+ city, country
+SET
+ city. city = 'Abha',
+ city. last_update = '2006-02-15 04:45:25',
+ country. country = 'Afghanistan'
+WHERE
+ city. country_id = country. country_id
+ 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信息解读
+
+* **const**: const用于使用常数值比较PRIMARY KEY时, 当查询的表仅有一行时, 使用system. 例:SELECT * FROM tbl WHERE col = 1.
+
+
+# Query: FE409EB794EE91CF
+
+★ ★ ★ ★ ★ 100分
+
+```sql
+
+UPDATE
+ film
+SET
+ LENGTH = 10
+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, 则该查询可能是有问题的.
+
+
+# Query: 3656B13CC4F888E2
+
+★ ★ ★ ☆ ☆ 65分
+
+```sql
+INSERT INTO city (country_id)
+SELECT
+ country_id
+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 | NULL | NULL | NULL | 0 | 0.00% | ☠️ **O(n)** | NULL |
+| 1 | SIMPLE | *country* | NULL | index | NULL | PRIMARY | 2 | NULL | 109 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* **index**: 全表扫描, 只是扫描表的时候按照索引次序进行而不是行. 主要优点就是避免了排序, 但是开销仍然非常大.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
+
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## INSERT INTO xx SELECT 加锁粒度较大请谨慎
+
+* **Item:** LCK.001
+
+* **Severity:** L3
+
+* **Content:** INSERT INTO xx SELECT 加锁粒度较大请谨慎
+
+# Query: 2F7439623B712317
+
+★ ★ ★ ★ ★ 100分
+
+```sql
+INSERT INTO city (country_id)
+VALUES
+ (1),
+ (2),
+ (3)
+```
+
+## Explain信息
+
+| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
+|---|---|---|---|---|---|---|---|---|---|---|---|---|
+| 1 | INSERT | *city* | NULL | ALL | NULL | NULL | NULL | NULL | 0 | 0.00% | ☠️ **O(n)** | NULL |
+
+
+
+### Explain信息解读
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+
+# Query: 11EC7AAACC97DC0F
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+INSERT INTO city (country_id)
+SELECT
+ 10
+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 | NULL | NULL | NULL | 0 | 0.00% | ☠️ **O(n)** | NULL |
+
+
+
+### Explain信息解读
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+
+## INSERT INTO xx SELECT 加锁粒度较大请谨慎
+
+* **Item:** LCK.001
+
+* **Severity:** L3
+
+* **Content:** INSERT INTO xx SELECT 加锁粒度较大请谨慎
+
+# Query: E3DDA1A929236E72
+
+★ ★ ★ ☆ ☆ 65分
+
+```sql
+REPLACE INTO city (country_id)
+SELECT
+ country_id
+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 | NULL | NULL | NULL | 0 | 0.00% | ☠️ **O(n)** | NULL |
+| 1 | SIMPLE | *country* | NULL | index | NULL | PRIMARY | 2 | NULL | 109 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* **index**: 全表扫描, 只是扫描表的时候按照索引次序进行而不是行. 主要优点就是避免了排序, 但是开销仍然非常大.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
+
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## INSERT INTO xx SELECT 加锁粒度较大请谨慎
+
+* **Item:** LCK.001
+
+* **Severity:** L3
+
+* **Content:** INSERT INTO xx SELECT 加锁粒度较大请谨慎
+
+# Query: 466F1AC2F5851149
+
+★ ★ ★ ★ ★ 100分
+
+```sql
+REPLACE INTO city (country_id)
+VALUES
+ (1),
+ (2),
+ (3)
+```
+
+## Explain信息
+
+| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
+|---|---|---|---|---|---|---|---|---|---|---|---|---|
+| 1 | REPLACE | *city* | NULL | ALL | NULL | NULL | NULL | NULL | 0 | 0.00% | ☠️ **O(n)** | NULL |
+
+
+
+### Explain信息解读
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+
+# Query: A7973BDD268F926E
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+REPLACE INTO city (country_id)
+SELECT
+ 10
+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 | NULL | NULL | NULL | 0 | 0.00% | ☠️ **O(n)** | NULL |
+
+
+
+### Explain信息解读
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+
+## INSERT INTO xx SELECT 加锁粒度较大请谨慎
+
+* **Item:** LCK.001
+
+* **Severity:** L3
+
+* **Content:** INSERT INTO xx SELECT 加锁粒度较大请谨慎
+
+# Query: 105C870D5DFB6710
+
+★ ★ ★ ☆ ☆ 65分
+
+```sql
+
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ (
+SELECT
+ film_id
+FROM
+ film
+) film
+) film
+) film
+) film
+) film
+) film
+) film
+) film
+) film
+) film
+) film
+) film
+) film
+) film
+) film
+) film
+```
+
+## Explain信息
+
+| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
+|---|---|---|---|---|---|---|---|---|---|---|---|---|
+| 1 | SIMPLE | *film* | NULL | index | NULL | idx\_fk\_language\_id | 1 | NULL | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* **index**: 全表扫描, 只是扫描表的时候按照索引次序进行而不是行. 主要优点就是避免了排序, 但是开销仍然非常大.
+
+#### Extra信息解读
+
+* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
+
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## 执行计划中嵌套连接深度过深
+
+* **Item:** SUB.004
+
+* **Severity:** L3
+
+* **Content:** MySQL对子查询的优化效果不佳,MySQL将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。
+
+# Query: 16C2B14E7DAA9906
+
+★ ☆ ☆ ☆ ☆ 35分
+
+```sql
+
+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 | NULL | NULL | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using where |
+| 2 | SUBQUERY | *language* | NULL | index | NULL | PRIMARY | 1 | NULL | 6 | ☠️ **100.00%** | ☠️ **O(n)** | Using index |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **PRIMARY**: 最外层的select.
+
+* **SUBQUERY**: 子查询中的第一个SELECT查询, 不依赖于外部查询的结果集.
+
+#### Type信息解读
+
+* **index**: 全表扫描, 只是扫描表的时候按照索引次序进行而不是行. 主要优点就是避免了排序, 但是开销仍然非常大.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+## 未使用 ORDER BY 的 LIMIT 查询
+
+* **Item:** RES.002
+
+* **Severity:** L4
+
+* **Content:** 没有 ORDER BY 的 LIMIT 会导致非确定性的结果,这取决于查询执行计划。
+
+## MySQL 对子查询的优化效果不佳
+
+* **Item:** SUB.001
+
+* **Severity:** L4
+
+* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
+
+# Query: 16CB4628D2597D40
+
+★ ★ ★ ☆ ☆ 65分
+
+```sql
+
+SELECT
+ *
+FROM
+ city i
+ LEFT JOIN country o ON i. city_id= o. country_id
+UNION
+SELECT
+ *
+FROM
+ city i
+ 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 | NULL | NULL | 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 | NULL | NULL | 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 |
+| 2 | UNION | *i* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.o.country\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **PRIMARY**: 最外层的select.
+
+* **UNION**: UNION中的第二个或后面的SELECT查询, 不依赖于外部查询的结果集.
+
+#### Type信息解读
+
+* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例: 'SELECT * FROM RefTbl, tbl WHERE RefTbl.col=tbl.col;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+
+## 建议使用 AS 关键字显示声明一个别名
+
+* **Item:** ALI.001
+
+* **Severity:** L0
+
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
+
+* **Item:** SUB.002
+
+* **Severity:** L2
+
+* **Content:** 与去除重复的UNION不同,UNION ALL允许重复元组。如果您不关心重复元组,那么使用UNION ALL将是一个更快的选项。
+
+# Query: EA50643B01E139A8
+
+★ ☆ ☆ ☆ ☆ 35分
+
+```sql
+
+SELECT
+ *
+FROM
+ (
+SELECT
+ *
+FROM
+ actor
+WHERE
+ last_update= '2006-02-15 04:34:33'
+ AND last_name= 'CHASE'
+) t
+WHERE
+ last_update= '2006-02-15 04:34:33'
+ AND last_name= 'CHASE'
+GROUP BY
+ first_name
+```
+
+## 为sakila库的actor表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`actor\` add index \`idx\_last\_name\_last\_update\_first\_name\` (\`last\_name\`,\`last\_update\`,\`first\_name\`) ;
+
+
+
+## 请为 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'。
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+## 非确定性的 GROUP BY
+
+* **Item:** RES.001
+
+* **Severity:** L4
+
+* **Content:** SQL返回的列既不在聚合函数中也不是 GROUP BY 表达式的列中,因此这些值的结果将是非确定性的。如:select a, b, c from tbl where foo="bar" group by a,该 SQL 返回的结果就是不确定的。
+
+## MySQL 对子查询的优化效果不佳
+
+* **Item:** SUB.001
+
+* **Severity:** L4
+
+* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
+
+# Query: 7598A4EDE6CFA6BE
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ city i
+ LEFT JOIN country o ON i. city_id= o. country_id
+WHERE
+ o. country_id is null
+UNION
+SELECT
+ *
+FROM
+ city i
+ RIGHT JOIN country o ON i. city_id= o. country_id
+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 | NULL | NULL | 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 | NULL | NULL | 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 |
+| 2 | UNION | *i* | NULL | eq\_ref | PRIMARY | PRIMARY | 2 | sakila.o.country\_id | 1 | ☠️ **100.00%** | ☠️ **O(n)** | Using where; Not exists |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **PRIMARY**: 最外层的select.
+
+* **UNION**: UNION中的第二个或后面的SELECT查询, 不依赖于外部查询的结果集.
+
+#### Type信息解读
+
+* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例: 'SELECT * FROM RefTbl, tbl WHERE RefTbl.col=tbl.col;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Not exists**: MySQL能够对LEFT JOIN查询进行优化, 并且在查找到符合LEFT JOIN条件的行后, 则不再查找更多的行.
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+
+## 建议使用 AS 关键字显示声明一个别名
+
+* **Item:** ALI.001
+
+* **Severity:** L0
+
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+## 如果您不在乎重复的话,建议使用 UNION ALL 替代 UNION
+
+* **Item:** SUB.002
+
+* **Severity:** L2
+
+* **Content:** 与去除重复的UNION不同,UNION ALL允许重复元组。如果您不关心重复元组,那么使用UNION ALL将是一个更快的选项。
+
+# Query: 1E8B70E30062FD13
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+SELECT
+ first_name, last_name, email
+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 | NULL | 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信息解读
+
+* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例: 'SELECT * FROM RefTbl, tbl WHERE RefTbl.col=tbl.col;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
+
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+# Query: E48A20D0413512DA
+
+★ ☆ ☆ ☆ ☆ 20分
+
+```sql
+
+SELECT
+ ID, name
+FROM
+ (
+SELECT
+ address
+FROM
+ customer_list
+WHERE
+ SID= 1
+ORDER BY
+ phone
+LIMIT
+ 50, 10) a
+ JOIN customer_list l ON (a. address= l. address)
+ JOIN city c ON (c. city= l. city)
+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 | NULL | 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 | NULL | NULL | 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 | NULL | 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信息解读
+
+* **DERIVED**: 用于from子句里有子查询的情况. MySQL会递归执行这些子查询, 把结果放在临时表里.
+
+* **PRIMARY**: 最外层的select.
+
+#### Type信息解读
+
+* **eq_ref**: 除const类型外最好的可能实现的连接类型. 它用在一个索引的所有部分被连接使用并且索引是UNIQUE或PRIMARY KEY, 对于每个索引键, 表中只有一条记录与之匹配. 例: 'SELECT * FROM RefTbl, tbl WHERE RefTbl.col=tbl.col;'.
+
+* **index**: 全表扫描, 只是扫描表的时候按照索引次序进行而不是行. 主要优点就是避免了排序, 但是开销仍然非常大.
+
+* **ref**: 连接不能基于关键字选择单个行, 可能查找到多个符合条件的行. 叫做ref是因为索引要跟某个参考值相比较. 这个参考值或者是一个数, 或者是来自一个表里的多表查询的结果值. 例:'SELECT * FROM tbl WHERE idx_col=expr;'.
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using index**: 只需通过索引就可以从表中获取列的信息, 无需额外去读取真实的行数据. 如果查询使用的列值仅仅是一个简单索引的部分值, 则会使用这种策略来优化查询.
+
+* **Using join buffer**: 从已有连接中找被读入缓存的数据, 并且通过缓存来完成与当前表的连接.
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
+
+
+## 为sakila库的city表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`city\` add index \`idx\_city\` (\`city\`) ;
+
+
+
+## 建议使用 AS 关键字显示声明一个别名
+
+* **Item:** ALI.001
+
+* **Severity:** L0
+
+* **Content:** 在列或表别名(如"tbl AS alias")中, 明确使用 AS 关键字比隐含别名(如"tbl alias")更易懂。
+
+## 最外层 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 方向排序,以便利用索引。
+
+## 同一张表被连接两次
+
+* **Item:** JOI.002
+
+* **Severity:** L4
+
+* **Content:** 相同的表在 FROM 子句中至少出现两次,可以简化为对该表的单次访问。
+
+## MySQL 对子查询的优化效果不佳
+
+* **Item:** SUB.001
+
+* **Severity:** L4
+
+* **Content:** MySQL 将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6 版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为 JOIN 或 LEFT OUTER JOIN。
+
+# Query: B0BA5A7079EA16B3
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+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 | NULL | NULL | 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 * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
+## 避免在 WHERE 条件中使用函数或其他运算符
+
+* **Item:** FUN.001
+
+* **Severity:** L2
+
+* **Content:** 虽然在 SQL 中使用函数可以简化很多复杂的查询,但使用了函数的查询无法利用表中已经建立的索引,该查询将会是全表扫描,性能较差。通常建议将列名写在比较运算符左侧,将查询过滤条件放在比较运算符右侧。也不建议在查询比较条件两侧书写多余的括号,这会对阅读产生比较大的困扰。
+
+# Query: 18A2AD1395A58EAE
+
+★ ★ ★ ☆ ☆ 60分
+
+```sql
+
+SELECT
+ last_update
+FROM
+ film
+GROUP BY
+ DATE( last_update)
+```
+
+## 最外层 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'。
+
+## GROUP BY 的条件为表达式
+
+* **Item:** CLA.010
+
+* **Severity:** L2
+
+* **Content:** 当 GROUP BY 条件为表达式或函数时会使用到临时表,如果在未指定 WHERE 或 WHERE 条件返回的结果集较大时性能会很差。
+
+# Query: 60F234BA33AAC132
+
+★ ★ ★ ☆ ☆ 70分
+
+```sql
+
+SELECT
+ last_update
+FROM
+ film
+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 | NULL | NULL | NULL | 1000 | ☠️ **100.00%** | ☠️ **O(n)** | Using filesort |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* ☠️ **Using filesort**: MySQL会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容. 可能在内存或者磁盘上进行排序. MySQL中无法利用索引完成的排序操作称为'文件排序'.
+
+
+## 最外层 SELECT 未指定 WHERE 条件
+
+* **Item:** CLA.001
+
+* **Severity:** L4
+
+* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
+
+## ORDER BY 的条件为表达式
+
+* **Item:** CLA.009
+
+* **Severity:** L2
+
+* **Content:** 当 ORDER BY 条件为表达式或函数时会使用到临时表,如果在未指定 WHERE 或 WHERE 条件返回的结果集较大时性能会很差。
+
+# Query: 1ED2B7ECBA4215E1
+
+★ ★ ★ ★ ☆ 80分
+
+```sql
+
+SELECT
+ description
+FROM
+ film
+WHERE
+ description IN( 'NEWS',
+ 'asd'
+)
+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 | NULL | NULL | NULL | 1000 | 20.00% | ☠️ **O(n)** | Using where; Using temporary |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+#### Type信息解读
+
+* ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.
+
+#### Extra信息解读
+
+* **Using where**: WHERE条件用于筛选出与下一个表匹配的数据然后返回给客户端. 除非故意做的全表扫描, 否则连接类型是ALL或者是index, 且在Extra列的值中没有Using Where, 则该查询可能是有问题的.
+
+* ☠️ **Using temporary**: 表示MySQL在对查询结果排序时使用临时表. 常见于排序order by和分组查询group by.
+
+
+## 为sakila库的film表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_description\` (\`description\`(255)) ;
+
+
+
+## 请为 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: 255BAC03F56CDBC7
+
+★ ★ ★ ★ ★ 100分
+
+```sql
+
+ALTER TABLE
+ address
+ADD
+ index idx_city_id( city_id)
+```
+
+## OK
+
+# Query: C315BC4EE0F4E523
+
+★ ★ ★ ★ ★ 100分
+
+```sql
+
+ALTER TABLE
+ inventory
+ADD
+ index `idx_store_film` (
+ `store_id`, `film_id`)
+```
+
+## 提醒:请将索引属性顺序与查询对齐
+
+* **Item:** KEY.004
+
+* **Severity:** L0
+
+* **Content:** 如果为列创建复合索引,请确保查询属性与索引属性的顺序相同,以便DBMS在处理查询时使用索引。如果查询和索引属性订单没有对齐,那么DBMS可能无法在查询处理期间使用索引。
+
+# Query: 9BB74D074BA0727C
+
+★ ★ ★ ★ ☆ 90分
+
+```sql
+
+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`)
+```
+
+## 索引名称已存在
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+* **Content:** Duplicate key name 'idx\_store\_film'
+
+* **Case:** 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\`)
+
+
+## 提醒:请将索引属性顺序与查询对齐
+
+* **Item:** KEY.004
+
+* **Severity:** L0
+
+* **Content:** 如果为列创建复合索引,请确保查询属性与索引属性的顺序相同,以便DBMS在处理查询时使用索引。如果查询和索引属性订单没有对齐,那么DBMS可能无法在查询处理期间使用索引。
+
+# Query: C95B5C028C8FFF95
+
+★ ☆ ☆ ☆ ☆ 30分
+
+```sql
+
+SELECT
+ DATE_FORMAT( t. last_update, '%Y-%m-%d'
+),
+COUNT( DISTINCT (
+ t. city))
+ FROM
+ city t
+ WHERE
+ t. last_update > '2018-10-22 00:00:00'
+ AND t. city LIKE '%Chrome%'
+ AND t. city = 'eip'
+ GROUP BY
+ DATE_FORMAT( t. last_update, '%Y-%m-%d'
+)
+ORDER BY
+ DATE_FORMAT( t. last_update, '%Y-%m-%d'
+)
+```
+
+## Explain信息
+
+| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
+|---|---|---|---|---|---|---|---|---|---|---|---|---|
+| 1 | SIMPLE | *NULL* | NULL | NULL | NULL | NULL | NULL | NULL | 0 | 0.00% | ☠️ **O(n)** | NULL |
+
+
+
+### Explain信息解读
+
+#### SelectType信息解读
+
+* **SIMPLE**: 简单SELECT(不使用UNION或子查询等).
+
+
+## 为sakila库的city表添加索引
+
+* **Item:** IDX.001
+
+* **Severity:** L2
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`city\` add index \`idx\_city\_last\_update\` (\`city\`,\`last\_update\`) ;
+
+
+
+## 建议使用 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 多个列指定的排序方向不同时将无法使用已经建立的索引。
+
+# 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
+```
+
diff --git a/test/fixture/test_Simple_Query_Optimizer.golden b/test/fixture/test_Simple_Query_Optimizer.golden
new file mode 100644
index 0000000000000000000000000000000000000000..4442d4df22697c6352c8d1011277cd37041b776b
--- /dev/null
+++ b/test/fixture/test_Simple_Query_Optimizer.golden
@@ -0,0 +1,56 @@
+# Query: 5767EE37339B2402
+
+★ ★ ★ ★ ☆ 85分
+
+```sql
+
+SELECT
+ *
+FROM
+ film
+WHERE
+ LENGTH > 120
+```
+
+## Explain信息
+
+| id | select\_type | table | partitions | type | possible_keys | key | key\_len | ref | rows | filtered | scalability | Extra |
+|---|---|---|---|---|---|---|---|---|---|---|---|---|
+| 1 | SIMPLE | *film* | NULL | ALL | NULL | NULL | NULL | 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
+
+
+* **Case:** ALTER TABLE \`sakila\`.\`film\` add index \`idx\_length\` (\`length\`) ;
+
+
+
+## 不建议使用 SELECT * 类型查询
+
+* **Item:** COL.001
+
+* **Severity:** L1
+
+* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
+
diff --git a/test/main.bats b/test/main.bats
new file mode 100755
index 0000000000000000000000000000000000000000..ac67bb06125d0c8e54ca2d32bbdada8597eb4179
--- /dev/null
+++ b/test/main.bats
@@ -0,0 +1,20 @@
+#!/usr/bin/env bats
+
+load test_helper
+
+@test "Simple Query Optimizer" {
+ ${SOAR_BIN_ENV} -query "select * from film where length > 120" | grep -v "散粒度" > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden
+ run golden_diff ${BATS_TEST_NAME}
+ [ $status -eq 0 ]
+}
+
+@test "Syntax Check" {
+ run ${SOAR_BIN} -query "select * frm film" -only-syntax-check
+ [ $status -eq 1 ]
+}
+
+@test "Run all test cases" {
+ ${SOAR_BIN} -list-test-sqls | ${SOAR_BIN_ENV} | grep -v "散粒度" > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden
+ run golden_diff ${BATS_TEST_NAME}
+ [ $status -eq 0 ]
+}
diff --git a/test/sql/README.md b/test/sql/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..926d54d19d1fb945cdfff6aa035c83a8f1356eea
--- /dev/null
+++ b/test/sql/README.md
@@ -0,0 +1,19 @@
+# Test Database
+
+## sakila
+
+* Download MySQL sakila database from http://downloads.mysql.com/docs/sakila-db.tar.gz .
+* InnoDB added FULLTEXT support in 5.6.10, you should add version comment `/*!50610 xxx */` for `film_text` table and it's triggers.
+* Merge schema and data into one file `sakila.sql`
+
+## world\_x
+
+world\_x contain JSON datatype, SOAR use this database for JSON testing.
+
+* Download MySQL world\_x database from http://downloads.mysql.com/docs/world_x-db.tar.gz .
+* MySQL support JSON datatype since 5.7.8, you should add version comment `/*!50708 xxx */` for `city`, `countryinfo`.
+* Merge `sakila.sql`, `world_x.sql` into init.sql.
+
+```bash
+gzip init.sql
+```
diff --git a/test/sql/init.sql.gz b/test/sql/init.sql.gz
new file mode 100644
index 0000000000000000000000000000000000000000..c3659425f53884ce8626be6beb02e00fbbc14b21
Binary files /dev/null and b/test/sql/init.sql.gz differ
diff --git a/test/test_helper.bash b/test/test_helper.bash
new file mode 100644
index 0000000000000000000000000000000000000000..1af72cc378fad6e4b977e0a0897ab1b19b41b0e9
--- /dev/null
+++ b/test/test_helper.bash
@@ -0,0 +1,14 @@
+setup() {
+ export SOAR_DEV_DIRNAME="${BATS_TEST_DIRNAME}/../"
+ export SOAR_BIN="${SOAR_DEV_DIRNAME}/bin/soar"
+ export SOAR_BIN_ENV="${SOAR_DEV_DIRNAME}/bin/soar -config ${SOAR_DEV_DIRNAME}/etc/soar.yaml"
+ export BATS_TMP_DIRNAME="${BATS_TEST_DIRNAME}/tmp"
+ export BATS_FIXTURE_DIRNAME="${BATS_TEST_DIRNAME}/fixture"
+ mkdir -p "${BATS_TMP_DIRNAME}"
+}
+
+golden_diff() {
+ FUNC_NAME=$1
+ diff "${BATS_TMP_DIRNAME}/${FUNC_NAME}.golden" "${BATS_FIXTURE_DIRNAME}/${FUNC_NAME}.golden" >/dev/null
+ return $?
+}