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

news2024/11/16 23:40:15

文章目录

  • 前言
  • 一、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()
BEGIN
    DECLARE i INT DEFAULT 1;
    START TRANSACTION; -- 开始事务
    WHILE i <= 1000000 DO
        INSERT 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.coloradmin.cn/o/1276868.html

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

相关文章

CSS 多主题切换思路

前言 本篇仅提供多主题切换思路&#xff0c;示例简单且清晰。 实现 步骤一&#xff1a;多主题(颜色)定义 定义根伪类 :root&#xff0c;代码第 2 和 7 行。分别定义了默认和带参数的伪类&#xff1b;定义 CSS 变量&#xff0c;注意变量名需要以两个减号&#xff08;--&…

03数据仓库Flume

Flume 功能 Flume主要作用&#xff0c;就是实时读取服务器本地磁盘数据&#xff0c;将数据写入到 HDFS。 Flume是 Cloudera提供的高可用&#xff0c;高可靠性&#xff0c;分布式的海量日志采集、聚合和传输的系统工具。 Flume 架构 Flume组成架构如下图所示&#xff1a; A…

jQuery选择器、操作DOM、事件处理机制、动画、ADJX操作知识点梳理

jQuery 核心理念就是写的更少&#xff0c;做的更多实现的代码更加简洁有效的提高开发效率 jQuery跟JavaScript的用法是不一样的 跟jQuery相继诞生的JavaScript库还有很多&#xff0c;不包括node.js 关于代码$("li").get(0)&#xff0c;获取DOM对象 jQuery对象声…

MMdetection3.0 问题:DETR验证集上AP值全为0.0000

MMdetection3.0 问题&#xff1a;DETR验证集上AP值全为0.0000 条件&#xff1a; 1、选择的是NWPU-VHR-10数据集&#xff0c;其中训练集455张&#xff0c;验证、测试相同均为195张。 2、在Faster-rcnn、YOLOv3模型上均能训练成功&#xff0c;但是在DETR训练时&#xff0c;出现验…

前端学习系列之CSS

目录 CSS 简介 发展史 优势 基本语法 引用方式 内部样式 行内样式 外部样式 选择器 id选择器 class选择器 标签选择器 子代选择器 后代选择器 相邻兄弟选择器 后续兄弟选择器 交集选择器 并集选择器 通配符选择器 伪类选择器 属性选择器 CSS基本属性 优…

pycharm中绘制一个3D曲线

import numpy as np import matplotlib.pyplot as plt # 中文的设置 import matplotlib as mp1 from mpl_toolkits.mplot3d import Axes3D mp1.rcParams["font.sans-serif"] ["kaiti"] mp1.rcParams["axes.unicode_minus"] False # 数据创建 X…

Python过滤掉特定区域内的矩形框

Python过滤掉特定区域内的矩形框 前言前提条件相关介绍实验环境过滤掉特定区域内的矩形框方法一&#xff1a;直接法&#xff08;for循环遍历&#xff09;代码实现输出结果 方法二&#xff1a;列表推导式代码实现输出结果 前言 由于本人水平有限&#xff0c;难免出现错漏&#x…

51k+ Star!动画图解、一键运行的数据结构与算法教程!

大家好&#xff0c;我是 Java陈序员。 我们都知道&#xff0c;《数据结构与算法》 —— 是程序员的必修课。 无论是使用什么编程语音&#xff0c;亦或者是前后端开发&#xff0c;都需要修好《数据结构与算法》这门课&#xff01; 在各个互联网大产的面试中&#xff0c;对数据…

RocketMQ事务消息源码解析

RocketMQ提供了事务消息的功能&#xff0c;采用2PC(两阶段协议)补偿机制&#xff08;事务回查&#xff09;的分布式事务功能&#xff0c;通过这种方式能达到分布式事务的最终一致。 一. 概述 半事务消息&#xff1a;指的是发送至broker但是还没被commit的消息&#xff0c;在半…

【Linux】24、文件系统、磁盘 IO

文章目录 一、文件系统1.1 索引节点和目录项1.2 虚拟文件系统 VFS1.3 文件系统 I/O1.5 性能观测1.5.1 容量1.5.2 缓存1.5.3 find 命令的缓存 二、磁盘 I/O2.1 通用块层2.2 I/O 栈2.3 磁盘性能指标2.3.1 磁盘 I/O 观测2.3.2 进程 I/O 观测 2.4 案例&#xff1a;找到打大量日志的…

UiPath学习笔记

文章目录 前言RPA介绍UiPath下载安装组件内容 前言 最近有一个项目的采集调研涉及到了客户端的采集&#xff0c;就取了解了一下RPA和UIPATH&#xff0c;记录一下 RPA介绍 RPA&#xff08;Robotic Process Automation&#xff1a;机器人处理自动化&#xff09;&#xff0c;是…

聊聊什么是IO流

目录 Java IOIO 基础Java IO 流了解吗&#xff1f; IO 设计模式1、装饰器模式2、适配器模式适配器模式和装饰器模式有什么区别呢&#xff1f;3、工厂模式4、观察者模式 IO 模型有哪些常见的 IO 模型&#xff1f;BIO(Blocking I/O)NIO (Non-blocking/New I/O)AIO (Asynchronous …

Java包(package)

1、概念 为了更好的组织类&#xff0c;用于区别类名的命名空间&#xff0c;其实就是基于工程的一个文件路径&#xff0c;如&#xff1a; 2、作用 三个作用&#xff1a; 1&#xff09;区分相同名称的类。 2&#xff09;能够较好地管理大量的类。 3&#xff09;控制访问范围。 在…

网站实现验证码功能

一、验证码 一般来说&#xff0c;网站在登录的时候会生成一个验证码来验证是否是人类还是爬虫&#xff0c;还有一个好处是防止恶意人士对密码进行爆破。 二、流程图 三、详细说明 3.1 后端生成验证码 Override public Result<Map<String, String>> getVerifica…

国内哪个超声波清洗机品牌比较好?质量好超声波清洗机总结

近年来超声波清洗机可以说是非常火爆&#xff0c;可以清洗化妆刷、眼镜、牙套等等一些小物件&#xff0c;大物件物品可以入手大型超声波清洗机&#xff0c;总之现在超声波清洗机已经衍生到可以在家使用&#xff0c;不再是在眼镜店看到它的身影或者是一些工业领域上&#xff0c;…

第二节:服务拆分(案例)

一、服务拆分注意事项 1.1 拆分原则 每个微服务&#xff0c;不要重复开发相同业务&#xff08;例如在单体项目中用到了一个查询&#xff0c;这个查询功能能够查询出订单信息、商品信息、用户信息&#xff0c;那么在拆分微服务时就不要将其写在一起了&#xff0c;订单的微服务只…

1、RocketMQ源码分析(一)

RocketMQ简单介绍 RabbitMQ的底层是使用erlang语言编写的&#xff0c;不便分析其底层&#xff0c;RocketMQ作为原阿里下经历阿里双十一严格考验的中间件&#xff0c;同时也是使用我们熟悉的java语言编写&#xff0c;我们先把入门的基础必备了解透&#xff0c;然后在去分析源码…

基于WebSocket实现客户聊天室

目录 一、实现聊天室原理 二、聊天室前端代码 三、聊天室后端代码&#xff08;重点&#xff09; 四、聊天室实现效果展示 一、实现聊天室原理 1.1 介绍websocket协议 websocket是一种通信协议&#xff0c;再通过websocket实现弹幕聊天室时候&#xff0c;实现原理是客户端首…

使用K-means把人群分类

1.前言 K-mean 是无监督的聚类算法 算法分类&#xff1a; 2.实现步骤 1.数据加工&#xff1a;把数据转为全数字&#xff08;比如性别男女&#xff0c;转换为0 和 1&#xff09; 2.模型训练 fit 3.预测 3.代码 原数据类似这样(source&#xff1a;http:img-blog.csdnimg.cn…

vmware 安装 AlmaLinux OS 8.6

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