MYSQL 不依赖其他表构建日期范围内的连续月份或日期

本文介绍如何使用SQL生成指定日期范围内的连续日期和连续月份,包括定义变量、创建记录及通过存储过程实现合同周期内各月的数据填充。

--  指定时间内的连续日期

-- 定义自增变量
SET @i = -1;
-- 指定日期范围
SET @sdt = '2015-08-01';
SET @edt = '2015-08-31';

-- 创建指定日期间记录数天数需要 加 1
SET @sql = REPEAT(" select 1 as tid union all", DATEDIFF(@edt, @sdt)+1);
-- 将最后一个union all 去掉
SET @sql = LEFT(@sql, LENGTH(@sql) - LENGTH("union all"));
-- 拼接sql
SET @sql = CONCAT("select date_add('", @sdt, "', interval @i:=@i+1 day) as date from (",@sql,") as tmp");
PREPARE stmt FROM @sql;
EXECUTE stmt;


--  指定时间内的连续月份

-- 定义自增变量
SET @i = -1;
-- 指定日期范围
SET @sdt = '2015-01-01';
SET @edt = '2015-12-31';

-- 创建指定月份间记录
SET @sql = REPEAT(" select 1 as tid union all",
(SELECT
PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM @edt),
EXTRACT(YEAR_MONTH FROM @sdt))+1));
-- 将最后一个union all 去掉
SET @sql = LEFT(@sql,LENGTH(@sql)-LENGTH(" union all"));
-- 拼接sql
SET @sql = CONCAT("select 1 AS tid, date_add('",@sdt,"', interval @i:=@i+1 MONTH) as mh 
FROM (",@sql,") AS t");
PREPARE stmt FROM @sql;
EXECUTE stmt;



应用示例:

-- 创建基础资料表

CREATE TABLE `contract` (
  `id` int(11) DEFAULT NULL,
  `startday` datetime DEFAULT NULL,
  `endday` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

INSERT INTO contract (id, startday, endday) 
VALUES('1', '2014-01-01 00:00:00', '2015-12-31 00:00:00');
INSERT INTO contract (id, startday, endday) 
VALUES('2', '2012-01-01 00:00:00', '2015-12-31 00:00:00');


-- 创建资料月明细表

CREATE TABLE `finance` (
  `id` int(11) DEFAULT NULL,
  `contract_id` int(11) DEFAULT NULL,
  `targetmonth` datetime DEFAULT NULL,
  `money` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

INSERT INTO myTmpDB.finance (id, contract_id, targetmonth, money) 
VALUES('1','1','2015-06-16 16:52:15','50') ;
INSERT INTO myTmpDB.finance (id, contract_id, targetmonth, money) 
VALUES('2','1','2015-07-16 16:52:15','60') ;
INSERT INTO myTmpDB.finance (id, contract_id, targetmonth, money) 
VALUES('3','2','2015-01-16 16:52:15','80') ;


--  创建存储过程

DELIMITER $$

DROP PROCEDURE IF EXISTS p_contract $$

CREATE PROCEDURE p_contract(IN p_cid INT)
BEGIN
-- 指定合同编号
SET @id = p_cid;
-- 定义自增变量
SET @i = -1;
-- 定义报表日期区间
SET @sdt = (SELECT startday FROM contract WHERE id = @id);
SET @edt = (SELECT CASE WHEN endday > NOW() THEN NOW() 
ELSE endday END FROM contract WHERE id = @id);
-- 指定日期间月份数
SET @cnt =((SELECT
PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM 
CASE WHEN endday > NOW() THEN NOW() ELSE endday END),
EXTRACT(YEAR_MONTH FROM startday))
FROM contract WHERE id = @id)+1);
-- 创建期间记录
SET @sql1 = REPEAT(" select 1 as tid union all", @cnt);
SET @sql1 = LEFT(@sql1, LENGTH(@sql1)-LENGTH(" union all"));
-- 拼接sql
SET @sql2 = CONCAT("select DATE_FORMAT(DATE_ADD(startday, 
 interval @i:=@i+1 MONTH), '%Y-%m')AS yrmh,
 t1.id, t1.startday, t1.endday 
 FROM contract AS t1
 INNER JOIN(",@sql1,") AS t2
 WHERE t1.id ='", @id, "'");


SET @sql3 = CONCAT("
 SELECT tt1.id, tt1.startday, tt1.endday, tt1.yrmh,
 IFNULL(tt2.money,0) AS money FROM 
 (", @sql2,")as tt1
 LEFT JOIN finance AS tt2
 ON tt1.id = tt2.contract_id 
 and tt1.yrmh = DATE_FORMAT(tt2.targetmonth, '%Y-%m') 
 order by tt1.yrmh ");
PREPARE stmt FROM @sql3;
EXECUTE stmt;
END$$


-- 存储过程调用:

CALL p_contract(1)





### 如何在 MySQL 中执行日期范围查询 在 MySQL 数据库中,日期范围查询是一种常见的操作方式。通过合理设计 SQL 查询语句以及利用索引来提升性能,可以有效减少查询耗时并提高效率。 #### 使用 `BETWEEN` 关键字进行日期范围查询 最简单的实现方法是使用 `BETWEEN` 运算符来指定起始和结束日期之间的记录。例如: ```sql SELECT * FROM orders WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31'; ``` 上述查询会返回所有订单日期介于 2023 年 1 月 1 日到 2023 年 12 月 31 日的记录[^1]。 #### 利用比较运算符进行更灵活的条件设置 如果需要更加复杂的逻辑者排除某些边界值,则可以通过组合 `<`, `<=`, `>`, 和 `>=` 来构建查询条件。如下所示: ```sql SELECT * FROM logs WHERE log_time >= '2023-09-01 00:00:00' AND log_time < '2023-10-01 00:00:00'; ``` 此例子精确指定了日志时间为 2023 年 9 月份的数据,并且会包含任何属于十月的第一条记录的时间戳[^3]。 #### 创建适合的索引来加速查询过程 为了进一步改善涉及大量数据集的大规模日期区间扫描现,在相关列上建立合适的索引至关重要。通常情况下,对于频繁使用的过滤字段应该创建单列复合索引。例如针对上面提到的例子可以在 `order_date` 者 `log_time` 上定义一个普通 B-tree 类型的索引: ```sql CREATE INDEX idx_order_date ON orders(order_date); CREATE INDEX idx_log_time ON logs(log_time); ``` 当面对非常庞大的格(如超过数百万甚至千万级别),仅靠常规手段未必能够满足高性能需求。此时可考虑采用分区技术按时间段分割原始资料存储结构;这样做的好处在于允许独立管理各个子集合内的元组分布情况从而简化全局维护工作量的同时也提升了局部访问速度[^4]。 另外值得注意的是自 MySQL 8.0 起官方已经移除了旧版中存在的 Query Cache 功能模块因此现在每次运行相同 SELECT 请求都会重新计算结果而依赖缓存副本[^2]。 ### 结论 综上所述,正确编写 SQL 可以显著影响最终检索出来的信息质量及其响应时效长短。同时也要记得定期更新统计信息保持最新状态以便让 CBO 做出明智决策选取最佳路径完成任务目标达成预期效果最大化收益最小化开销比率平衡点找到理想解决方案方向迈进一大步向前发展断进步成长壮大起来吧!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值