77 lines
3.9 KiB
Go
77 lines
3.9 KiB
Go
package exporter
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"strings"
|
|
"marketing-system-data-tool/server/internal/schema"
|
|
)
|
|
|
|
func EvaluateExplain(db *sql.DB, q string, args []interface{}) (int, []string, error) {
|
|
rows, score, err := RunExplain(db, q, args)
|
|
if err != nil { return 0, nil, err }
|
|
sugg := []string{}
|
|
for _, r := range rows {
|
|
// tbl := r.Table.String
|
|
typ := r.Type.String
|
|
if typ == "" && r.SelectType.Valid { typ = r.SelectType.String }
|
|
if typ == "ALL" {
|
|
sugg = append(sugg, "出现全表扫描(ALL),请在过滤或连接列上建立索引")
|
|
}
|
|
if r.Extra.Valid {
|
|
e := r.Extra.String
|
|
if contains(e, "Using temporary") || contains(e, "Using filesort") {
|
|
sugg = append(sugg, "出现临时表或文件排序,请优化排序列及索引覆盖")
|
|
}
|
|
}
|
|
}
|
|
return score, sugg, nil
|
|
}
|
|
|
|
func IndexSuggestions(req BuildRequest) []string {
|
|
sugg := []string{}
|
|
sch := schema.Get(req.Datasource, req.MainTable)
|
|
// Filter-based suggestions
|
|
has := func(k string) bool { _, ok := req.Filters[k]; return ok }
|
|
add := func(s string){ if s != "" { sugg = append(sugg, s) } }
|
|
if has("creator_in") && has("create_time_between") {
|
|
add(fmt.Sprintf("建议在 `%s`(create_time, %s) 建立复合索引以覆盖权限与时间范围", sch.TableName("order"), colName(sch, "creator_in")))
|
|
} else {
|
|
if has("creator_in") { add(fmt.Sprintf("建议在 `%s`(%s) 建立索引以覆盖权限过滤", sch.TableName("order"), colName(sch, "creator_in"))) }
|
|
if has("create_time_between") { add(fmt.Sprintf("建议在 `%s`(create_time) 建立索引以覆盖时间范围", sch.TableName("order"))) }
|
|
}
|
|
if has("plan_id_eq") { add(fmt.Sprintf("建议在 `%s`(%s) 建立索引以覆盖活动/计划过滤", sch.TableName("order"), colName(sch, "plan_id_eq"))) }
|
|
if has("reseller_id_eq") { add(fmt.Sprintf("建议在 `%s`(%s) 建立索引以覆盖分销商过滤", sch.TableName("order"), colName(sch, "reseller_id_eq"))) }
|
|
if has("product_id_eq") { add(fmt.Sprintf("建议在 `%s`(%s) 建立索引以覆盖商品过滤", sch.TableName("order"), colName(sch, "product_id_eq"))) }
|
|
if has("out_trade_no_eq") { add(fmt.Sprintf("建议在 `%s`(%s) 建立索引以覆盖支付流水过滤", sch.TableName("order"), colName(sch, "out_trade_no_eq"))) }
|
|
// Table usage-based join suggestions
|
|
usedTables := map[string]bool{}
|
|
for _, tf := range req.Fields {
|
|
parts := strings.Split(tf, ".")
|
|
if len(parts)==2 { usedTables[parts[0]] = true }
|
|
}
|
|
if req.MainTable == "order_info" {
|
|
add("建议在 `order_info`(order_no) 建立索引以优化与子表的连接")
|
|
if usedTables["order_cash"] { add("建议在 `order_cash`(order_no) 建立索引以优化与主表的连接") }
|
|
if usedTables["order_voucher"] { add("建议在 `order_voucher`(order_no) 建立索引以优化与主表的连接") }
|
|
if usedTables["order_digit"] { add("建议在 `order_digit`(order_no) 建立索引以优化与主表的连接") }
|
|
if usedTables["goods_voucher_batch"] { add("建议在 `goods_voucher_batch`(channel_batch_no) 建立索引以优化与订单立减金的连接") }
|
|
if usedTables["goods_voucher_subject_config"] { add("建议在 `goods_voucher_subject_config`(id) 上确保主键索引以优化连接") }
|
|
if usedTables["merchant"] { add("建议在 `merchant`(id) 上确保主键索引以优化连接") }
|
|
if usedTables["activity"] { add("建议在 `activity`(id) 上确保主键索引以优化连接") }
|
|
}
|
|
return dedup(sugg)
|
|
}
|
|
|
|
func colName(sch schema.Schema, key string) string {
|
|
if _, col, ok := sch.FilterColumn(key); ok { return col }
|
|
return ""
|
|
}
|
|
|
|
func dedup(arr []string) []string {
|
|
m := map[string]bool{}
|
|
out := []string{}
|
|
for _, s := range arr { if !m[s] { m[s]=true; out = append(out, s) } }
|
|
return out
|
|
}
|