C#和MySQL技巧分享:日期的模糊查询

文章目录

  • 前言
  • 一、EF Core 模糊查询
  • 二、MySql 日期模糊查询分析和优化
    • 2.1 测试环境准备
      • 2.1.1 创建数据库
      • 2.1.2 查看测试数据
    • 2.2 查询日期的运行效率对比
    • 2.3 运行效率优化
  • 三、EF Core 模糊查询优化
    • 3.1 字符串转日期
    • 3.2 使用日期格式查询
  • 四、优化建议
  • 总结


前言

在处理数据库查询时,特别是在涉及到模糊查询和日期字段时,我们常常面临一个挑战:如何在确保查询效率的同时,实现精确和灵活的数据检索?众所周知,直接转换数据库字段类型进行匹配往往会导致查询效率下降,甚至引发全表搜索的问题,这在处理大量数据时尤为明显。因此,找到一种既能保持数据库性能又能满足查询需求的方法显得尤为重要。


一、EF Core 模糊查询

在使用Entity Framework Core(EF Core)进行数据库操作时,模糊查询是一个常用而又复杂的功能。在EF Core中,进行模糊查询通常涉及到Contains方法的使用。然而,如果直接对非字符串字段应用ToString()然后进行比较,EF Core会将其解析为字符串匹配。这种做法虽然在某些场景下有效,但可能导致效率问题。以下是一个典型的例子:

expression = expression.And(p => // ... 其他条件p.CreateTime.ToString().Contains(strlike)) ||// ... 其他条件p.PayStatus.Contains(strlike));

这段代码试图在多个字段上应用模糊匹配,包括将日期时间转换为字符串进行匹配。这种操作会在SQL级别产生如下查询:
在这里插入图片描述

WHERE (`e`.`id` = `e7`.`sale_order_id`) AND ((@__strLike_1 LIKE '') OR (LOCATE(@__strLike_1, CAST(`e8`.`create_time` AS char) COLLATE utf8mb4_bin) > 0)))

这样做是非常不可取的做法,需要接受严厉的批评!

二、MySql 日期模糊查询分析和优化

2.1 测试环境准备

我用到的是MySql数据库,Mysql Workbench可视化桌面软件。

2.1.1 创建数据库

-- 检查并删除已存在的数据库
DROP DATABASE IF EXISTS `date_time_test`;-- 创建数据库
CREATE DATABASE `date_time_test`;-- 使用新创建的数据库
USE `date_time_test`;-- 检查并删除已存在的表
DROP TABLE IF EXISTS sales_orders;-- 创建表
CREATE TABLE sales_orders (id INT AUTO_INCREMENT PRIMARY KEY,create_time DATETIME,order_no VARCHAR(50)
);-- 定义分隔符
DELIMITER $$-- 创建插入数据的存储过程
CREATE PROCEDURE InsertTestData()
BEGINDECLARE i INT DEFAULT 1;START TRANSACTION; -- 开始事务WHILE i <= 1000000 DOINSERT INTO sales_orders (create_time, order_no) VALUES (NOW() - INTERVAL FLOOR(RAND() * 365) DAY, CONCAT('Order_', LPAD(i, 8, '0')));IF i % 10000 = 0 THEN -- 每10000条数据提交一次事务COMMIT;START TRANSACTION;END IF;SET i = i + 1;END WHILE;COMMIT; -- 最后提交剩余的事务
END$$-- 恢复默认分隔符
DELIMITER ;-- 调用存储过程以生成数据
CALL InsertTestData();

2.1.2 查看测试数据

我这里运行了两次存储过程,因此生成了2百万的随机数据。

select count(1) from sales_orders

在这里插入图片描述

2.2 查询日期的运行效率对比

我们对比一下我能想到的几个写法:

SELECT count(1) FROM sales_orders
WHERE DATE_FORMAT(sales_orders.create_time, '%Y-%m-%d') = '2023-12-01';SELECT count(1) FROM sales_orders
WHERE LOCATE('2023-12-01', CAST(sales_orders.create_time AS char) COLLATE utf8mb4_bin) > 0;select count(1) from sales_orders
WHERE sales_orders.create_time >= '2023-12-01 00:00:00' and sales_orders.create_time < '2023-12-02 00:00:00';-- 错误写法 这个只能查询到'2023-12-01 00:00:00'这个时间点的结果
select count(1) from sales_orders
WHERE sales_orders.create_time = '2023-12-01';

在这里插入图片描述

SELECT count(1) FROM sales_orders
WHERE DATE_FORMAT(sales_orders.create_time, '%Y-%m-%d') = '2023-12-01';

运行效率1

SELECT count(1) FROM sales_orders
WHERE LOCATE('2023-12-01', CAST(sales_orders.create_time AS char) COLLATE utf8mb4_bin) > 0;

运行效率2

select count(1) from sales_orders
WHERE sales_orders.create_time >= '2023-12-01 00:00:00' and sales_orders.create_time < '2023-12-02 00:00:00';

运行效率3

select count(1) from sales_orders
WHERE sales_orders.create_time = '2023-12-01';

运行效率4

上面的截图是 MySQL 查询的 EXPLAIN 输出,用于显示 MySQL 如何执行特定的查询。我将解释输出中的一些关键部分:

  1. Access Type: ALL

    • 这意味着 MySQL 正在进行全表扫描,即它查看表中的每一行来找到匹配的行。这通常是最慢的访问类型,尤其是在处理大型表时。
  2. Cost Hint: Very High

    • 这表明查询的成本非常高。在 MySQL 中,成本是一个相对的度量,用于表示执行查询所需的工作量。
  3. No usable indexes were found for the table

    • MySQL 没有找到可用的索引来优化这个查询。如果没有索引,数据库就必须执行全表扫描,这是非常低效的。
  4. Filtered: 100.00%

    • 这个值表示查询条件过滤掉的数据百分比。在这种情况下,100% 表示没有行被过滤掉,或者说每一行都被检查了,这通常是因为查询条件没有排除任何行。这个值通常是指在表的全行中,有多少百分比的行是与查询条件相匹配的。在理想情况下(即查询非常有效地限制了结果集),这个百分比应该是较低的。
  5. Rows Examined per Scan: 1994535

    • 这表示每次扫描时检查了多少行。在这个查询中,几乎检查了两百万行,这是因为进行了全表扫描。
  6. Hint: 100% is best, < 1% is worst

    • 这个提示是关于 Filtered 百分比。这个提示可能是指行的选择性。在这种特定情况下,因为查询没有过滤任何行(可能是由于查询条件的设计),所以解释器提示:实际上并没有行被排除在外。
  7. 成本细节

    • Read: 读取数据的成本。
    • Eval: 计算 WHERE 条件的成本。
    • Prefix: 到达当前查询点的总成本。
    • Data_Read: 每次连接操作读取的数据量。

2.3 运行效率优化

我们已经知道将搜索字符串转换目标数据类型,会提高查询效率。在这种情况下,查询慢的原因很可能是由于缺乏有效的索引。在 create_time 字段上创建一个索引可能会显著提高查询的性能,因为索引可以让数据库快速定位到那些匹配特定日期时间范围的行,而不必扫描整个表。
我们可以通过以下SQL语句创建一个:

CREATE INDEX idx_create_time ON sales_orders(create_time);

在这里插入图片描述
运行效率5
经过索引化后,查询效率提升非常明显。

三、EF Core 模糊查询优化

在前文中,我们探讨了如何在C#和MySQL中实现模糊搜索,特别是在涉及日期字段时的一些常见问题。我们发现,直接将日期字段转换为字符串进行查询是一种常见但效率低下的做法。现在,我们将专注于优化这种方法,以提高查询的效率和准确性。

3.1 字符串转日期

首先,我们将模糊搜索字段尝试转换为日期类型,如果转换成功,则进行日期模糊搜索;如果转换失败,则查询时不考虑搜索日期字段。

下面是我尝试转换的静态方法,允许输入单个日期或者日期范围。

/// <summary>
/// 尝试根据输入的字符串解析日期或日期范围。
/// 支持的格式包括单个日期(年、年月、年月日)和日期范围(年~年、年月~年月、年月日~年月日)。
/// </summary>
/// <param name="input">输入的日期字符串,可以是单个日期或日期范围。</param>
/// <param name="startDate">解析成功时,返回范围的起始日期。</param>
/// <param name="endDate">解析成功时,返回范围的结束日期。</param>
/// <returns>如果输入格式正确且能成功解析,则返回true;否则返回false。</returns>
/// <remarks>
/// - 单个日期的格式可以是 "yyyy", "yyyy-MM" 或 "yyyy-MM-dd"。
/// - 日期范围由两个这样的日期组成,以~字符分隔。
/// - 方法会根据输入格式确定日期范围:
///   - 年(如 "2023")的范围是该年的1月1日到次年1月1日。
///   - 年月(如 "2023-10")的范围是该月的1日到次月1日。
///   - 年月日(如 "2023-10-12")的范围是该日的0时到次日0时。
///   - 对于日期范围,起始和结束日期根据相同规则确定。
/// - 输入字符串中的日期部分可以包含或不包含前导零(例如 "2023-1-1" 或 "2023-01-01")。
/// </remarks>
public static bool TryParseDateInput(string input, out DateTime startDate, out DateTime endDate)
{var formats = new[] { "yyyy-MM-dd", "yyyy-MM-d","yyyy-M-dd", "yyyy-M-d","yyyy-MM", "yyyy-M", "yyyy" };var parts = input.Split('~');startDate = default;endDate = default;// 单个日期if (parts.Length == 1){if (!DateTime.TryParseExact(input, formats, CultureInfo.InvariantCulture, DateTimeStyles.None, out var date)){return false;}switch (input.Count(f => f == '-')){case 0: // 年startDate = new DateTime(date.Year, 1, 1);endDate = startDate.AddYears(1);break;case 1: // 年月startDate = new DateTime(date.Year, date.Month, 1);endDate = startDate.AddMonths(1);break;default: // 年月日startDate = date;endDate = startDate.AddDays(1);break;}return true;}// 日期范围else if (parts.Length == 2){if (!DateTime.TryParseExact(parts[0], formats, CultureInfo.InvariantCulture, DateTimeStyles.None, out var start) ||!DateTime.TryParseExact(parts[1], formats, CultureInfo.InvariantCulture, DateTimeStyles.None, out var end)){return false;}startDate = new DateTime(start.Year, start.Month, start.Day);endDate = new DateTime(end.Year, end.Month, end.Day);switch (parts[1].Count(f => f == '-')){case 0: // 年endDate = endDate.AddYears(1);break;case 1: // 年月endDate = endDate.AddMonths(1);break;default: // 年月日endDate = endDate.AddDays(1);break;}return true;}else{return false;}
}

3.2 使用日期格式查询

// 尝试解析 strLike 为日期范围
bool isDateRange = CommonFunc.TryParseDateInput(strlike, out DateTime startDate, out DateTime endDate);
expression = expression.And(p =>// ... 其他条件p.PayStatus.Contains(strlike) ||(isDateRange && p.CreateTime >= startDate && p.CreateTime < endDate));

优化后的效果
在上图中,我们可以看到一个EF Core查询示例,其中正确应用了针对日期字段的优化技巧。这种做法不仅使代码更加清晰,而且能够显著提高数据库查询的性能。通过这些技巧,我们可以有效地在C#和MySQL环境下处理复杂的模糊搜索需求,尤其是当涉及到日期字段时。

四、优化建议

  1. 避免不必要的类型转换:在数据库查询中,尽量避免将非字符串类型转换为字符串进行匹配。这不仅会降低查询效率,还可能使得数据库无法利用现有索引。

  2. 使用专门的日期函数:对于日期类型的字段,使用专门的日期比较函数而不是将日期转换为字符串。EF Core支持多种日期相关的函数,这些函数可以直接应用于日期字段,提高查询效率。

  3. 考虑索引的影响:确保对于频繁进行模糊匹配的字段建立合适的索引。特别是对于大型数据库,合理的索引对于维持查询性能至关重要。

  4. 分析生成的SQL:在开发过程中,关注EF Core生成的SQL语句。这可以帮助我们理解EF Core是如何将LINQ查询转换为SQL的,并据此做出优化。

  5. 减少全表扫描:尽量减少会导致全表扫描的查询模式。


总结

这篇博客中,我们探讨了在C#和MySQL环境下进行模糊查询日期的处理策略。我们从EF Core的模糊查询开始,深入分析了在MySQL中对日期进行模糊查询的效率问题,并提出了相应的优化方法。最后,我们聚焦于如何在EF Core中优化这些查询,特别是针对日期字段的处理。

通过本文的讨论,我们还学会了如何优化现有的查询方法以提高性能。这些知识对于开发高效、可靠的数据驱动应用程序至关重要,尤其是在处理大量数据和复杂查询的情况下。希望这些技巧能在未来的项目中更好地处理非字符串类型的查询。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/189927.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

go开发之个人微信号机器人开发

简要描述&#xff1a; 下载消息中的文件 请求URL&#xff1a; http://域名地址/getMsgFile 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型…

Linux Spug自动化运维平台本地部署与公网远程访问

文章目录 前言1. Docker安装Spug2 . 本地访问测试3. Linux 安装cpolar4. 配置Spug公网访问地址5. 公网远程访问Spug管理界面6. 固定Spug公网地址 前言 Spug 面向中小型企业设计的轻量级无 Agent 的自动化运维平台&#xff0c;整合了主机管理、主机批量执行、主机在线终端、文件…

版本控制系统Git学习笔记-Git分支操作

文章目录 概述一、Git分支简介1.1 基本概念1.2 创建分支1.3 分支切换1.4 删除分支 二、新建和合并分支2.1 工作流程示意图2.2 新建分支2.3 合并分支2.4 分支示例2.4.1 当前除了主分支&#xff0c;再次创建了两个分支2.4.2 先合并test1分支2.4.3 合并testbranch分支 2.5 解决合并…

算法基础三

电话号码的字母组合 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits "…

【集合篇】Java集合概述

Java 集合概述 集合与容器 容器&#xff08;Container&#xff09;是一个更广泛的术语&#xff0c;用于表示可以容纳、组织和管理其他对象的对象。它是一个更高层次的概念&#xff0c;包括集合&#xff08;Collection&#xff09;在内。集合&#xff08;Collection&#xff0…

深入理解Servlet(中)

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 上篇有一张图&#xff…

Redis的安装

本文采用原生的方式安装Redis&#xff0c;Redis的版本为5.0.5 安装 下载 下载网站&#xff1a;https://download.redis.io/releases/ wget http://download.redis.io/releases/redis-5.0.5.tar.gz解压 tar -zxvf redis-5.0.5.tar.gz进入redis目录 cd redis-5.0.5执行编译…

u盘一插上就提示格式化解决办法,帮助重新使用,避免数据丢失

在我们使用U盘的过程中&#xff0c;有时会遇到一插上就提示格式化的问题。这个问题可能会给我们带来很多麻烦&#xff0c;因为格式化操作会导致数据的丢失。为了解决这一问题&#xff0c;本文将介绍一些解决办法&#xff0c;帮助读者重新使用U盘&#xff0c;并避免数据丢失的风…

【开源】基于Vue和SpringBoot的校园二手交易系统

项目编号&#xff1a; S 009 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S009&#xff0c;文末获取源码。} 项目编号&#xff1a;S009&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 二手商品档案管理模…

利用 FormData 实现文件上传、监控网路速度和上传进度

利用 FormData 实现文件上传 基础功能&#xff1a;上传文件 演示如下&#xff1a; 概括流程&#xff1a; 前端&#xff1a;把文件数据获取并 append 到 FormData 对象中后端&#xff1a;通过 ctx.request.files 对象拿到二进制数据&#xff0c;获得 node 暂存的文件路径 前端…

Leetcode—1094.拼车【中等】

2023每日刷题&#xff08;四十七&#xff09; Leetcode—1094.拼车 模拟实现代码 bool carPooling(int** trips, int tripsSize, int* tripsColSize, int capacity) {int arr[1003] {0};int numPassenger 0, fromidx 0, toidx 0;for(int i 0; i < tripsSize; i) {num…

【嵌入式Linux程序开发综合实验】-1(附流程图) | ARM开发板 | 测试“Hello World” | Makefile文件 | 实现加法相加

任务&#xff1a;编写在标准输出终端输出“Hello World&#xff01;”的C语言代码以及输入指定数字相加结果、Makefile&#xff0c;并分别编译出在PC与ARM上运行的可执行程序文件。 设备以及工具 硬件&#xff1a;Linux开发板、PC机、串口连接线 图1 Linux开发板以及串口接线 …

vmware 安装 AlmaLinux OS 8.6

选择系统镜像 选择镜像 选择安装位置和修改名称 可以自定义硬件&#xff0c;也可以不选择&#xff0c;后面可以再设置 自定义硬件可以设置内存和cpu等信息 安装虚拟机系统 密码如果简单的话需要点击两次done 才能保存

特殊二叉树——堆

&#x1f308;一、堆的基本概念 1.堆&#xff1a;非线性结构&#xff0c;是完全二叉树 2.堆分为大堆和小堆。 大堆&#xff1a;树中任意一个父亲都大于等于孩子&#xff0c;根节点值大于等于其所有子孙节点的值。 小堆&#xff1a;树中任意一个父亲都小于等于孩子&#xff0c;…

兼容jlink OB arm仿真器使用(杜邦线过长导致烧写总是失败)

一、兼容jlink OB的使用&#xff1a; 1、设置中要选择jlink&#xff1b; 2、模式选择SWD模式&#xff08;接三根线&#xff09;&#xff1b; 二、杜邦线过长导致stm32的stlink烧写总是失败 用ST-link烧写提示的错误信息有&#xff1a; Error while accessing a target reso…

Redis中的数据结构

文章目录 第1关&#xff1a;Redis中的数据结构 第1关&#xff1a;Redis中的数据结构 这是上篇文章的第一关&#xff0c;只不过本篇是代码按行做的&#xff0c;方便一下大家使用。 代码如下&#xff1a; redis-cliset hello redislpush educoder-list hellorpush educoder-lis…

51单片机制作数字频率计

文章目录 简介设计思路工作原理Proteus软件仿真软件程序实验现象测量误差和范围总结 简介 数字频率计是能实现对周期性变化信号频率测量的仪器。传统的频率计通常是用很多的逻辑电路和时序电路来实现的&#xff0c;这种电路一般运行较慢&#xff0c;而且测量频率的范围较小。这…

【SpringCloud】注册中心和Ribbon负载均衡

SpringCloud 1.Eureka注册中心 1.1 Eureka的作用 注册中心拉取服务负载均衡远程调用 order-service得知user-service实例地址流程&#xff1a; user-service服务实例启动后&#xff0c;将自己的信息注册到eureka-server&#xff08;Eureka服务端&#xff09;&#xff0c;称…

redis主从复制模式和哨兵机制

目录 第一章、主从复制模式1.1&#xff09;Redis 主从复制模式介绍1.2&#xff09;Redis 主从复制实现、 第二章、哨兵机制2.1&#xff09;容灾处理之哨兵2.2&#xff09;Sentinel 配置 第一章、主从复制模式 1.1&#xff09;Redis 主从复制模式介绍 ①单点故障&#xff1a;数…

honle电源维修UV电源控制器EVG EPS40C-HMI

好乐UV电源控制器维修&#xff1b;honle控制器维修&#xff1b;UV电源维修MUC-Steuermodul 2 LΛmpen D-82166 主要维修型号&#xff1a; EVG EPS 60/120、EVG EPS 100、EVG EPS200、EVG EPS 220、EVG EPS 340、EVG EPS40C-HMI、EVG EPS60 HONLE好乐uv电源维修故障包括&#…