news 2026/10/17 22:10:53

多条件查询与增删改工程化:LIKE、Map、Not、Order实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
多条件查询与增删改工程化:LIKE、Map、Not、Order实战

1. 多条件查询与增删改的工程化拆解

1.1 为什么“多条件查询”是业务代码的分水岭

刚入行的开发者写查询,往往停留在“根据ID取一条记录”的阶段。一旦业务方甩过来一个筛选面板——状态是“已上架”,标题里包含“春季”,价格区间在某个范围,还要按创建时间倒序——很多人就开始在代码里堆if了。我见过最夸张的一个接口,光拼查询条件就写了三百多行,后面接手的人根本不敢动。

多条件查询之所以是分水岭,是因为它同时考验三件事:对ORM查询构造器的理解深度、对参数边界情况的处理能力、对SQL执行效率的敏感度。这三样缺一个,代码要么写不出来,要么写出来跑得慢,要么上线后出各种诡异的数据错乱。

这篇内容围绕一个典型的MVC分层示例展开,把多条件查询里的LIKE模糊匹配、Map条件构造、Not取反、Order排序这几个高频操作讲透,同时把增、改、删的完整链路串起来。适合已经能写单表CRUD、但一遇到复杂筛选就发怵的开发者,也适合想梳理自己代码分层习惯的老手对照检查。

1.2 技术选型的取舍逻辑

标题里提到了两个生态的写法,一个是Go语言里常见的gorm+gin组合,另一个是PHP生态里laravel那套查询构造器。把这两者放在一起看,其实很有意思——它们解决的是同一类问题,但设计哲学完全不同。

gorm是典型的链式调用风格,db.Where(...).Where(...).Order(...).Find(...),每一步返回一个新的*gorm.DB对象,条件层层叠加。它的优势是类型安全,Go的编译器能帮你挡掉一部分拼写错误;劣势是动态条件拼接时,代码容易变得冗长,尤其是条件数量不固定的时候。

laravel的查询构造器则是另一种思路,它更偏向“流式接口 + 闭包分组”,->where()->orWhere()->whereIn()一路点下去,配合when()方法可以优雅地处理“条件存在才拼接”的场景。PHP的弱类型在这里反而成了优势,写起来更灵活,但代价是运行时才暴露错误。

我个人的选择习惯是:如果项目对类型安全要求高、团队以静态语言为主,走gorm那条路;如果追求开发速度、业务逻辑变动频繁,laravel那套写法更顺手。但不管选哪个,核心的查询构造思路是相通的,理解了其中一个,另一个上手就是半天的事。

1.3 MVC分层在这个场景下的具体职责

很多人写MVC,写着写着就把逻辑全塞进Controller了。查询条件在Controller里拼,业务规则在Controller里判断,数据库操作也在Controller里调。短期看没问题,一旦要加个定时任务复用同样的查询逻辑,就傻眼了。

在这个示例里,我建议的分层是这样的:

  • Controller层:只负责接收HTTP请求参数、做基础的类型转换和参数校验、调用Service层、组装返回结构。它不应该知道SQL长什么样。
  • Service层:承载业务规则。比如“普通用户只能查自己创建的数据,管理员可以查全部”,这种判断放在这里。它调用Repository层拿数据。
  • Repository层:专门和数据库打交道。查询构造、条件拼接、排序规则都在这一层完成。换数据库或者换ORM,只改这一层。
  • Model层:定义数据结构、表名映射、字段约束。保持干净,不要塞业务方法。

这样分的好处是,当业务方说“这个筛选条件要改一下”,你只需要动Repository层的一个方法,Controller和Service完全不用碰。测试也好写,Repository层可以单独做集成测试。

2. 多条件查询的核心细节与实操要点

2.1 LIKE模糊查询的三种匹配模式与性能陷阱

LIKE查询看起来简单,但用不好就是性能杀手。先明确三种匹配模式的区别:

模式写法能否走索引适用场景
前缀匹配LIKE '关键词%'可以走索引搜索框自动补全、编号前缀筛选
后缀匹配LIKE '%关键词'不走索引极少使用,除非数据量极小
全模糊匹配LIKE '%关键词%'不走索引通用搜索,数据量必须可控

前缀匹配能走索引,是因为B+树索引本身是有序的。就像字典按拼音排序,你要找“zhang”开头的字,翻到z开头的区域就行。但你要找“包含ang的字”,就只能一页一页翻。全模糊匹配就是这个道理,数据库只能全表扫描。

实操中我的建议是:

  • 如果业务允许,尽量引导用户使用前缀匹配。比如搜索订单号,用户输入“ORD2024”,那就用前缀匹配。
  • 如果必须全模糊,一定要配合其他条件缩小范围。比如先按时间范围过滤,再在结果集里做LIKE。
  • 数据量超过十万行且必须全模糊的,考虑引入专门的全文检索方案,不要硬扛。

在gorm里写LIKE要注意参数拼接方式:

// 推荐:使用参数占位符,gorm会自动处理转义 keyword := "%" + searchTerm + "%" db.Where("title LIKE ?", keyword) // 不推荐:手动拼接字符串,有注入风险 db.Where("title LIKE '%" + searchTerm + "%'")

在laravel里则是:

// 推荐 $query->where('title', 'like', '%' . $searchTerm . '%'); // 多个字段OR模糊 $query->where(function ($q) use ($searchTerm) { $q->where('title', 'like', "%{$searchTerm}%") ->orWhere('description', 'like', "%{$searchTerm}%"); });

注意:LIKE的转义字符问题经常被忽略。如果用户输入的关键词本身包含%或_,不处理的话会变成通配符,导致查询结果不符合预期。gorm和laravel都提供了转义方法,记得用上。

2.2 Map条件构造:动态查询的利器

当查询条件数量不固定时,用Map来构造条件是最高效的方式。比如前端传过来一个筛选对象,有的字段有值,有的没有,你不可能为每种组合写一个方法。

gorm里的Map条件写法:

conditions := map[string]interface{}{ "status": 1, "is_delete": 0, } if title != "" { conditions["title LIKE ?"] = "%" + title + "%" } if minPrice > 0 { conditions["price >= ?"] = minPrice } db.Where(conditions).Find(&results)

laravel里更直接,因为它的where方法本身就接受数组:

$conditions = ['status' => 1, 'is_delete' => 0]; if (!empty($title)) { $conditions[] = ['title', 'like', "%{$title}%"]; } $query->where($conditions);

Map构造的核心价值在于“条件与数据分离”。你可以先把所有可能的条件收集到一个Map里,最后一次性传给查询构造器。这样做的好处是,条件的增删改只影响Map的填充逻辑,查询语句本身保持稳定。

但Map方式也有坑。第一,Map的遍历顺序是不确定的,虽然大多数情况下不影响查询结果,但如果你的条件之间有依赖关系(比如某个条件依赖于前一个条件的结果),就不能用Map。第二,Map方式不利于复用,如果多个接口需要相同的条件组合,最好抽成一个独立的条件构造方法。

我的经验是:简单等值条件用Map,复杂条件(涉及OR、分组、子查询)用链式调用。两者可以混用,gorm和laravel都支持在链式调用中间插入Map条件。

2.3 Not取反条件的正确使用姿势

Not条件在业务里很常见:“排除已删除的”、“状态不是草稿的”、“ID不在黑名单里的”。但很多人写Not会写出性能问题。

gorm里的Not写法:

// 排除单个值 db.Not("status = ?", 3) // 排除多个值 db.Not("id IN ?", []int{1, 2, 3}) // 排除NULL(注意:SQL里NULL的比较很特殊) db.Where("deleted_at IS NOT NULL")

laravel里的Not写法:

$query->where('status', '!=', 3); $query->whereNotIn('id', [1, 2, 3]); $query->whereNotNull('deleted_at');

这里有一个非常容易踩的坑:!=和NOT IN遇到NULL值时的行为。在SQL里,NULL != 3的结果不是true,而是NULL(也就是未知)。这意味着如果status字段允许为NULL,用!=过滤会把NULL值的行也排除掉,但你可能本意是想保留它们的。

正确的做法是明确处理NULL:

-- 想保留NULL的情况 WHERE (status != 3 OR status IS NULL) -- 想排除NULL的情况 WHERE status != 3 AND status IS NOT NULL

提示:在设计表结构时,尽量给字段设置默认值并加上NOT NULL约束。这样能避免大量NULL相关的边界问题,查询写起来也简单得多。

2.4 Order排序的稳定性与索引配合

排序看起来是最简单的,ORDER BY created_at DESC谁都会写。但排序的坑在于稳定性和性能。

先说稳定性。如果created_at字段有重复值(同一秒创建了多条记录),那么ORDER BY created_at DESC返回的顺序是不确定的。分页的时候就会出现“第一页看到的记录,翻到第二页又出现了”这种诡异现象。

解决办法是加一个唯一的次级排序字段:

db.Order("created_at DESC, id DESC")

这样即使时间相同,也能保证顺序稳定。

再说性能。排序操作如果无法利用索引,数据库就需要把结果集全部加载到内存里做排序(filesort),数据量大的时候非常慢。要让排序走索引,需要满足几个条件:

  • 排序字段上有索引
  • 排序方向一致(要么全ASC,要么全DESC)
  • WHERE条件中用到的字段和排序字段能组成联合索引的最左前缀

举个例子,如果经常执行这样的查询:

SELECT * FROM orders WHERE user_id = 123 ORDER BY created_at DESC

那么建立一个(user_id, created_at)的联合索引,就能同时加速过滤和排序。

在gorm里动态拼接排序字段时,要特别注意防止SQL注入,因为Order()方法不会自动转义:

// 危险:直接拼接用户输入 db.Order(sortField + " " + sortOrder) // 安全:白名单校验 allowedFields := map[string]bool{"created_at": true, "price": true, "id": true} if allowedFields[sortField] { db.Order(sortField + " DESC") }

laravel的orderBy方法同样需要白名单校验,原理是一样的。

3. 完整实操流程与关键环节实现

3.1 数据模型设计与索引规划

在动手写查询之前,先把表结构和索引定好。这是整个工程的地基,地基没打好,后面查询怎么写都快不起来。

假设我们有一个商品表,核心字段包括:自增ID、标题、描述、价格、状态、所属分类、创建时间、更新时间、软删除标记。针对这个表,我通常会规划以下索引:

索引名字段组合用途
idx_status_created(status, created_at)按状态筛选+时间排序
idx_category_price(category_id, price)按分类筛选+价格排序
idx_title_prefix(title(20))标题前缀搜索
idx_deleted(deleted_at)软删除过滤

索引不是越多越好。每个索引都会增加写入时的开销,因为插入和更新数据时,数据库需要同步维护所有索引。我的经验是:单表索引数量控制在5个以内,优先保证高频查询路径的覆盖。

在gorm里定义模型时,可以通过tag指定索引:

type Product struct { ID uint `gorm:"primaryKey"` Title string `gorm:"size:200;index:idx_title_prefix,length:20"` Price float64 `gorm:"type:decimal(10,2)"` Status int `gorm:"default:1;index:idx_status_created,priority:1"` CategoryID uint `gorm:"index:idx_category_price,priority:1"` CreatedAt time.Time `gorm:"index:idx_status_created,priority:2"` UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` }

laravel的迁移文件里写法类似,用$table->index()方法声明。

3.2 查询构造的完整实现

现在进入核心部分。假设我们要实现一个商品列表接口,支持以下筛选条件:

  • 标题模糊搜索(可选)
  • 状态筛选(可选,支持多选)
  • 价格区间(可选)
  • 分类筛选(可选)
  • 排除指定ID(可选)
  • 排序字段和方向(可选,默认按创建时间倒序)
  • 分页

先看gorm版本的Repository层实现:

func (r *ProductRepo) List(params ProductQueryParams) ([]Product, int64, error) { query := r.db.Model(&Product{}) // 标题模糊搜索:使用前缀匹配以利用索引 if params.Title != "" { query = query.Where("title LIKE ?", params.Title+"%") } // 状态多选:用IN查询 if len(params.Statuses) > 0 { query = query.Where("status IN ?", params.Statuses) } // 价格区间 if params.MinPrice > 0 { query = query.Where("price >= ?", params.MinPrice) } if params.MaxPrice > 0 { query = query.Where("price <= ?", params.MaxPrice) } // 分类筛选 if params.CategoryID > 0 { query = query.Where("category_id = ?", params.CategoryID) } // 排除指定ID if len(params.ExcludeIDs) > 0 { query = query.Not("id IN ?", params.ExcludeIDs) } // 统计总数(在分页之前) var total int64 if err := query.Count(&total).Error; err != nil { return nil, 0, err } // 排序:白名单校验 sortField := "created_at" sortOrder := "DESC" allowedSort := map[string]bool{"created_at": true, "price": true, "id": true} if allowedSort[params.SortField] { sortField = params.SortField } if params.SortOrder == "asc" { sortOrder = "ASC" } query = query.Order(sortField + " " + sortOrder + ", id DESC") // 分页 offset := (params.Page - 1) * params.PageSize query = query.Offset(offset).Limit(params.PageSize) var products []Product if err := query.Find(&products).Error; err != nil { return nil, 0, err } return products, total, nil }

再看laravel版本的等价实现:

public function list(array $params) { $query = Product::query(); // 标题模糊搜索 if (!empty($params['title'])) { $query->where('title', 'like', $params['title'] . '%'); } // 状态多选 if (!empty($params['statuses'])) { $query->whereIn('status', $params['statuses']); } // 价格区间 if (!empty($params['min_price'])) { $query->where('price', '>=', $params['min_price']); } if (!empty($params['max_price'])) { $query->where('price', '<=', $params['max_price']); } // 分类筛选 if (!empty($params['category_id'])) { $query->where('category_id', $params['category_id']); } // 排除指定ID if (!empty($params['exclude_ids'])) { $query->whereNotIn('id', $params['exclude_ids']); } // 总数 $total = $query->count(); // 排序 $sortField = in_array($params['sort_field'] ?? '', ['created_at', 'price', 'id']) ? $params['sort_field'] : 'created_at'; $sortOrder = ($params['sort_order'] ?? 'desc') === 'asc' ? 'asc' : 'desc'; $query->orderBy($sortField, $sortOrder)->orderBy('id', 'desc'); // 分页 $page = max(1, (int)($params['page'] ?? 1)); $pageSize = min(100, max(1, (int)($params['page_size'] ?? 20))); $products = $query->offset(($page - 1) * $pageSize)->limit($pageSize)->get(); return ['list' => $products, 'total' => $total]; }

两段代码的逻辑完全一致,只是语法不同。注意几个关键细节:

第一,Count操作要在Offset和Limit之前执行,否则统计的是分页后的数量,不是总数。

第二,排序字段一定要做白名单校验。我见过有人直接把前端传的sort_field拼进Order里,结果被人用id; DROP TABLE这种输入攻击了。虽然现代ORM有一定的防护,但白名单是最稳妥的做法。

第三,分页参数要做边界处理。page最小为1,page_size要设上限(比如100),防止有人传page_size=999999把数据库拖垮。

3.3 新增操作的参数校验与返回值处理

新增看起来简单,但有几个细节容易出问题。

gorm版本:

func (r *ProductRepo) Create(product *Product) error { return r.db.Create(product).Error }

laravel版本:

public function create(array $data) { return Product::create($data); }

新增操作的核心不在代码本身,而在于参数校验和默认值填充。我的习惯是在Service层做这些事情:

  • 检查必填字段是否为空
  • 检查价格是否为正数
  • 检查分类ID是否存在
  • 填充默认状态值
  • 记录创建人信息

这些校验如果放在Controller里,复用性差;放在Model的钩子函数里,又不够灵活。放在Service层是最合适的。

另外,新增成功后要返回完整的记录(包含数据库生成的ID和创建时间),而不是只返回一个成功标志。前端拿到ID后可能需要跳转到详情页,或者做后续的关联操作。

3.4 更新操作的字段白名单与并发控制

更新操作最大的坑是误更新。比如用户只想改标题,结果代码里把整个对象都更新了,状态、价格全被覆盖成了默认值。

gorm里用Updates配合结构体时,零值字段会被忽略:

// 只会更新非零值字段 db.Model(&product).Updates(Product{Title: "新标题", Price: 99.9})

但如果确实需要把某个字段更新为零值,就得用Map或者Select指定字段:

// 明确指定要更新的字段 db.Model(&product).Select("title", "price").Updates(Product{Title: "新标题", Price: 0})

laravel里用update方法时,只有传入的字段会被更新:

$product->update(['title' => '新标题', 'price' => 0]);

并发控制是另一个容易被忽略的点。如果两个请求同时更新同一条记录,后提交的会覆盖先提交的。解决办法是加乐观锁,用版本号字段:

// 更新时检查版本号 result := db.Model(&Product{}). Where("id = ? AND version = ?", product.ID, product.Version). Updates(map[string]interface{}{ "title": product.Title, "version": product.Version + 1, }) if result.RowsAffected == 0 { return errors.New("数据已被其他请求修改,请刷新后重试") }

3.5 删除操作的软删除与硬删除选择

删除分两种:软删除和硬删除。软删除是给记录打一个删除标记,数据还在表里;硬删除是直接从表里移除。

绝大多数业务场景应该用软删除。原因很简单:用户误删了可以恢复,数据分析时还能看到历史记录,关联数据不会因为主记录消失而变成孤儿。

gorm内置了软删除支持,只要模型里有DeletedAt字段:

type Product struct { // ... DeletedAt gorm.DeletedAt `gorm:"index"` } // 软删除 db.Delete(&product) // 查询时会自动排除软删除的记录 db.Find(&products) // 如果要查包含已删除的 db.Unscoped().Find(&products)

laravel里需要手动实现软删除,用SoftDeletestrait:

use Illuminate\Database\Eloquent\SoftDeletes; class Product extends Model { use SoftDeletes; }

硬删除只应该在以下场景使用:数据确实没有任何保留价值(比如临时日志)、数据量太大需要定期清理、合规要求必须彻底删除。硬删除前一定要做好备份,或者先软删除观察一段时间再物理清理。

4. 常见问题与排查技巧实录

4.1 查询结果不符合预期的排查思路

这是最高频的问题。明明条件写对了,返回的数据就是不对。我的排查顺序是这样的:

第一步,打印实际执行的SQL。gorm里开启Debug模式:

db.Debug().Where(...).Find(&results)

laravel里用toSql()或dd():

$query->toSql(); // 或者 DB::listen(function ($query) { Log::info($query->sql, $query->bindings); });

看到真实SQL之后,问题往往一目了然。我遇到过很多次“条件没生效”的情况,一看SQL发现是Where链式调用时变量覆盖了,前面的条件被后面的覆盖掉了。

第二步,检查参数类型。比如前端传过来的价格是字符串"99.9",后端没做类型转换直接拼进查询,数据库在做比较时可能触发隐式类型转换,导致索引失效或者结果不准确。

第三步,检查NULL值。前面说过,NULL在SQL里很特殊。如果某个条件字段允许为NULL,而查询时用了!=或NOT IN,结果可能和预期不一致。

4.2 分页数据重复或丢失的定位方法

分页问题的根源几乎都是排序不稳定。排查方法:

  • 检查排序字段是否有重复值
  • 检查是否加了唯一的次级排序字段
  • 检查分页参数是否正确(offset计算有没有错)

一个实用的验证方法是:连续请求第一页和第二页,把两页的ID合并去重,看总数是否等于page_size * 2。如果少了,说明有重复;如果多了,说明有遗漏。

4.3 常见问题速查表

问题现象可能原因排查方法解决方案
LIKE查询不走索引使用了全模糊匹配EXPLAIN查看执行计划改为前缀匹配或引入全文检索
分页数据重复排序字段有重复值检查排序字段唯一性加ID作为次级排序
更新影响了所有行WHERE条件缺失或错误打印SQL检查WHERE子句确保WHERE条件正确,加LIMIT保护
软删除数据仍被查出查询未启用软删除过滤检查模型是否配置软删除确认DeletedAt字段和查询作用域
并发更新数据错乱缺少乐观锁模拟并发请求测试加版本号字段做乐观锁
查询超时缺少索引或全表扫描EXPLAIN分析补充索引或优化查询条件
参数注入风险直接拼接用户输入代码审查使用参数占位符+白名单校验

4.4 几个我踩过的坑和对应技巧

坑一:LIKE的转义问题。用户搜索“50%折扣”,结果%被当成通配符,搜出来一堆不相关的东西。解决办法是在拼接前转义特殊字符:

keyword := strings.ReplaceAll(searchTerm, "%", "\\%") keyword = strings.ReplaceAll(keyword, "_", "\\_")

坑二:批量删除时的条件遗漏。有次写批量删除,代码是db.Where("id IN ?", ids).Delete(&Product{}),测试时传了空数组,结果IN ()变成了无条件删除,整张表被清空了。后来我养成了习惯,批量操作前先检查参数:

if len(ids) == 0 { return errors.New("请选择要删除的记录") }

坑三:Order字段的SQL注入。前面提过,这里再强调一次。永远不要相信前端传过来的排序字段,一定要用白名单过滤。

坑四:Count和Find的顺序。有次先执行了Limit再执行Count,导致统计的总数永远是分页大小。正确的顺序是先Count再分页。

坑五:软删除和唯一索引的冲突。如果某个字段有唯一索引,软删除后再次插入相同值会报冲突,因为软删除的记录还在表里。解决办法是把唯一索引改成包含删除标记的联合索引,或者用时间戳作为删除标记。

5. 工程化收尾与个人经验补充

5.1 查询参数的结构化封装

当筛选条件越来越多时,把参数散落在方法签名里会变得难以维护。我的做法是定义一个查询参数结构体:

type ProductQueryParams struct { Title string Statuses []int MinPrice float64 MaxPrice float64 CategoryID uint ExcludeIDs []int SortField string SortOrder string Page int PageSize int }

这样做的好处是,Controller层只需要把HTTP参数绑定到这个结构体上,Service层和Repository层都接收同一个结构体。加一个新条件时,只需要改结构体定义和Repository里的拼接逻辑,其他层不用动。

5.2 接口返回结构的统一约定

列表接口的返回结构我习惯固定为:

{ "code": 0, "message": "success", "data": { "list": [], "total": 100, "page": 1, "page_size": 20 } }

total是满足条件的总记录数,不是当前页的数量。前端根据total和page_size计算总页数。这个约定一旦定下来,所有列表接口都保持一致,前端写一次分页组件就能复用。

5.3 关于性能的一点个人体会

多条件查询的性能优化,八成的功夫在索引设计上,两成在查询写法上。我见过太多人花大量时间优化SQL语句,结果一看表结构,该建的索引一个没建。

我的习惯是:每写一个查询接口,先用EXPLAIN跑一遍,看有没有全表扫描,看有没有用到索引。如果发现type是ALL,那就说明有问题,要么加索引,要么改查询条件。

另外,不要过早优化。数据量小的时候,怎么写都快。等到数据量上来了,再根据慢查询日志有针对性地优化。上来就搞分库分表、读写分离,往往是过度设计。

5.4 最后分享一个调试小技巧

开发阶段,我会在Repository层加一个开关,开启后把所有执行的SQL和耗时都打到日志里:

if debugMode { db = db.Debug() }

配合慢查询阈值(比如超过200ms就告警),能快速定位到问题查询。上线后关掉Debug,但保留慢查询日志。这个习惯帮我提前发现了好几个潜在的性能问题。

代码写到最后,拼的不是谁会的语法多,而是谁对边界情况考虑得周全、谁对性能瓶颈有预判、谁的代码别人接手时能看懂。多条件查询这个场景,恰好把这几点都覆盖到了,值得花时间打磨。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 22:08:10

Hadoop NameNode与ResourceManager高可用集群搭建

兄弟们&#xff0c;如果你正在折腾Hadoop完全分布式集群&#xff0c;或者被NameNode单点故障搞得夜不能寐&#xff0c;这篇就是写给你的。这个教程聚焦Hadoop 3.1.4版本&#xff0c;目标只有一个&#xff1a;把NameNode和ResourceManager的高可用&#xff08;HA&#xff09;机制…

作者头像 李华
网站建设 2026/10/17 22:07:00

Codex 一键安装中文版|支持 MCP 插件,桌面 AI 自动化能力详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/17 22:01:59

用代码验证线性代数:从方程组到SVD的工程实践指南

简介&#xff1a;线性代数教材《Linear Algebra with Applications》第十版全球版由Steven J. Leon和Lisette de Pillis合著&#xff0c;是一本广泛应用于大学课堂的经典教材。全书共8章、556页&#xff0c;内容覆盖线性代数课程研究组&#xff08;LACSG&#xff09;推荐的全部…

作者头像 李华
网站建设 2026/10/17 21:59:22

软考高项131个工具背诵版:ITTO映射与过程组分类记忆法

简介&#xff1a;面向2024年软考高级项目管理师&#xff08;高项&#xff09;备考者&#xff0c;这是一份以背诵为导向的工具与技术速记资料&#xff0c;系统整理项目管理中常用的131个工具&#xff0c;逐一定义&#xff0c;并标明其所属过程组与常见知识领域。内容按数据收集、…

作者头像 李华
网站建设 2026/10/17 21:57:58

系统辨识实战指南:从数据预处理到物理约束建模的闭环工作流

简介&#xff1a;本资源是一套面向自动化、控制工程专业本科生及科研初学者的系统辨识实践教学包&#xff0c;聚焦MATLAB在动态系统建模与参数估计中的工程落地。内容覆盖数据预处理&#xff08;iddata构建&#xff09;、主流模型辨识&#xff08;ARX、状态空间、NARMAX等&…

作者头像 李华