MarketingSystemDataTool/server/internal/exporter/evaluate.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
}