SQL Server拆分Hijri日期别名列至年/月/日列的最优方案
嘿,针对你要拆分波斯历日期列的需求,我给你几个性能优先的实现方案,咱们从最靠谱的开始说:
方案1:直接复用日期转换逻辑(性能最优)
既然HK2CreatedDateShamsi是通过dbo.DateToShamsi(dbo.Health_Komak2.CDate)生成的,那最优的方式是跳过生成完整字符串的步骤,直接从函数内部的转换逻辑里提取年、月、日。
比如如果DateToShamsi函数内部是先计算出波斯历的年、月、日,再拼接成yyyy/mm/dd格式的字符串,那你可以把这些计算逻辑抽出来(要么新建三个小函数,要么直接把逻辑写在视图里),直接生成HK2Year、HK2Month、HK2Day列,完全避免字符串拆分的开销。
举个例子,假设我们新建三个辅助函数:
CREATE FUNCTION dbo.GetShamsiYear(@date DATETIME) RETURNS VARCHAR(4) AS BEGIN -- 这里复制DateToShamsi中计算年的逻辑 DECLARE @year VARCHAR(4) -- 你的年计算代码 RETURN @year END CREATE FUNCTION dbo.GetShamsiMonth(@date DATETIME) RETURNS VARCHAR(2) AS BEGIN -- 复制DateToShamsi中计算月的逻辑 DECLARE @month VARCHAR(2) -- 你的月计算代码 RETURN @month END CREATE FUNCTION dbo.GetShamsiDay(@date DATETIME) RETURNS VARCHAR(2) AS BEGIN -- 复制DateToShamsi中计算日的逻辑 DECLARE @day VARCHAR(2) -- 你的日计算代码 RETURN @day END
然后修改视图:
ALTER VIEW allcases AS SELECT -- 保留原有列 dbo.DateToShamsi(dbo.Health_Komak2.CDate) AS HK2CreatedDateShamsi, -- 直接生成年月日列 dbo.GetShamsiYear(dbo.Health_Komak2.CDate) AS HK2Year, dbo.GetShamsiMonth(dbo.Health_Komak2.CDate) AS HK2Month, dbo.GetShamsiDay(dbo.Health_Komak2.CDate) AS HK2Day, -- 其他原有列... FROM dbo.Health_Komak2 -- 其他视图逻辑...
这种方式完全没有字符串操作的开销,而且每个日期只计算一次波斯历转换,性能拉满。
方案2:基于固定格式拆分(性能次优,适合无法修改函数的场景)
如果没办法修改DateToShamsi函数,也不能抽内部逻辑,那就要尽量减少函数调用次数,同时利用固定格式来快速拆分。
核心原则:只调用一次DateToShamsi函数,然后对生成的字符串做拆分,绝对不要在拆分年、月、日时分别调用函数(那样会让函数执行3次,性能直接砍到1/3)。
因为你提到格式是固定的yyyy/mm/dd(比如1396/01/05),所以可以直接按固定位置截取,比用CHARINDEX查找分隔符更快:
ALTER VIEW allcases AS SELECT -- 保留原有日期字符串列 temp.HK2CreatedDateShamsi, -- 按固定位置拆分年月日 LEFT(temp.HK2CreatedDateShamsi, 4) AS HK2Year, SUBSTRING(temp.HK2CreatedDateShamsi, 6, 2) AS HK2Month, RIGHT(temp.HK2CreatedDateShamsi, 2) AS HK2Day, -- 其他原有列... FROM ( -- 子查询里只调用一次DateToShamsi SELECT dbo.DateToShamsi(dbo.Health_Komak2.CDate) AS HK2CreatedDateShamsi, -- 其他原有列... FROM dbo.Health_Komak2 -- 其他视图逻辑... ) AS temp
如果格式不是完全固定的(比如月份/日期可能是1位),那再用CHARINDEX来定位分隔符,但还是要保证只调用一次DateToShamsi:
ALTER VIEW allcases AS SELECT temp.HK2CreatedDateShamsi, -- 拆分年份:从开头到第一个/ LEFT(temp.HK2CreatedDateShamsi, CHARINDEX('/', temp.HK2CreatedDateShamsi) - 1) AS HK2Year, -- 拆分月份:第一个/到第二个/之间的部分 SUBSTRING(temp.HK2CreatedDateShamsi, CHARINDEX('/', temp.HK2CreatedDateShamsi) + 1, CHARINDEX('/', temp.HK2CreatedDateShamsi, CHARINDEX('/', temp.HK2CreatedDateShamsi) + 1) - CHARINDEX('/', temp.HK2CreatedDateShamsi) - 1) AS HK2Month, -- 拆分日期:第二个/之后的部分 RIGHT(temp.HK2CreatedDateShamsi, LEN(temp.HK2CreatedDateShamsi) - CHARINDEX('/', temp.HK2CreatedDateShamsi, CHARINDEX('/', temp.HK2CreatedDateShamsi) + 1)) AS HK2Day, -- 其他原有列... FROM ( SELECT dbo.DateToShamsi(dbo.Health_Komak2.CDate) AS HK2CreatedDateShamsi, -- 其他原有列... FROM dbo.Health_Komak2 -- 其他视图逻辑... ) AS temp
避坑提醒
- 绝对不要这么写:
LEFT(dbo.DateToShamsi(...), 4) AS HK2Year,然后月和日也分别调用DateToShamsi,这会导致函数被执行3次,性能损耗极大。 - 如果你的视图数据量很大,优先选择方案1,因为字符串操作不管怎么优化都不如直接计算日期部件高效。
内容的提问来源于stack exchange,提问作者穆罕默德 - Moh
相关产品推荐
相关产品推荐

