我有两个约会:CREATION_DATE
和START_DATE
。 START_DATE
将始终晚于CREATION_DATE
。我需要计算它们之间的分钟数,除了周末发生的分钟数。
我能找到的每个解决方案都假定其中一个日期发生发生在周末,但是可惜的是,如果CREATION_DATE
是在星期五,而START_DATE
是星期一,则所有周六和周日的计数。
我什至尝试计算从CREATION_DATE
到下一个12am的分钟,发生时间是从星期一的第一个12am到START_DATE
的 plus 分钟,但这都不起作用。
如果我只想数几天,我已经找到了解决方案。我需要知道几分钟。
我们的数据库是托管的,我无法创建VB函数,因此我的解决方案必须全部为SQL。
答案 0 :(得分:1)
基本思路是在开始和结束之间的所有分钟分钟内生成记录,包括周末的记录。然后使用WHERE
子句过滤掉不需要的内容。在许多情况下,这是通过连接到Calendar
表来完成的,因此您也可以查看假期或其他特殊事件,但是为此,我们可以仅使用DATEPART()
函数。
完成此操作后,我们使用GROUP BY
将内容回滚到原始日期值,并使用COUNT()
函数知道我们做了多少工作。
无论您是数天,数分钟,数月还是其他时间,此基本概念都有效。
这个问题尚不清楚,但我将假设您的开始和结束值是表中的列,而不是变量名(无@
)。
WITH Numbers(Number) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id]) - 1
FROM sys.all_columns AS s1
CROSS JOIN sys.all_columns AS s2
)
SELECT t.CREATION_DATE, t.START_DATE, COUNT(*) AS Num_Minutes
FROM [MyTable] t
INNER JOIN Numbers n on n.Number <= DATEDIFF(minute, t.CREATION_DATE, t.START_DATE)
WHERE DATEPART(dw, DATEADD(minute, n.Number, t.CREATION_DATE)) NOT IN (7,1)
GROUP BY t.CREATION_DATE, t.START_DATE
但这可能会非常缓慢,具体取决于日期之间的距离。您可以通过使用其他各种方式来生成Numbers
表来获得一个更好地逼近实际数据需求的起点,从而对此进行改进。
答案 1 :(得分:0)
如果您不担心为假期做会计核算,则可以将其作为一个简单的数学问题来进行,而不必费力地查看理货表或进行任何计数。 下面的工作方式是从开始日期和结束日期参数中删除时间部分,并从中计算工作日的天数,然后将其乘以该数字的3660。从那里开始,如果开始日期是工作日,则减去开始日期分钟。 。如果结束日期是工作日,则会添加这些分钟。
DECLARE
@BegDate DATETIME = '2018-09-13 03:30:30',
@EndDate DATETIME = '2018-09-18 03:35:27';
SELECT
working_mins = bm.base_mins
- ((1 - (x.is_beg_sat + x.is_beg_sun)) * x.beg_mins) -- if the begin date is a week day, subtract the mins from midnight.
+ ((1 - (x.is_end_sat + x.is_end_sun)) * x.end_mins) -- if the end date is a week day add the mins from midnight.
--,*
FROM
( VALUES (
DATEADD(DAY, DATEDIFF(DAY, 0, @BegDate), 0),
DATEADD(DAY, DATEDIFF(DAY, 0, @EndDate), 0)
) ) d (beg_date, end_date)
CROSS APPLY ( VALUES (
DATEDIFF(MINUTE, d.beg_date, @BegDate),
DATEDIFF(MINUTE, d.end_date, @EndDate),
DATEDIFF(DAY, d.beg_date, d.end_date),
DATEDIFF(WEEK, d.beg_date, d.end_date) * 2,
1 - SIGN(DATEPART(WEEKDAY, d.beg_date) % 7),
1 - SIGN(DATEPART(WEEKDAY, d.end_date) % 7),
1 - SIGN((DATEPART(WEEKDAY, d.beg_date) + 7) % 8),
1 - SIGN((DATEPART(WEEKDAY, d.end_date) + 7) % 8)
) ) x (beg_mins, end_mins, total_days, weekend_days, is_beg_sat, is_end_sat, is_beg_sun, is_end_sun)
CROSS APPLY ( VALUES (1440 * (x.total_days - x.weekend_days + x.is_beg_sat - x.is_end_sat)) ) bm (base_mins);
答案 2 :(得分:0)
您可以看一下该解决方案,看看是否满足您的需求。基本上,我做了以下事情:
取StartDate
和EndDate
之间不是周末的整天数乘以2:
SELECT COUNT(*) * 24 * 60 FROM WholeDaysBetween WHERE wkday <= 5
从StartDate
(hours*60 + minutes
)开始记录
(24 * 60) - (DATEPART(HOUR, @StartDate) * 60) - (DATEPART(MINUTE, @StartDate))
从EndDate
(hours*60 + minutes
)开始记录
(DATEPART(HOUR, @EndDate) * 60) + (DATEPART(MINUTE, @EndDate))
为了获得两次之间的整天数,我使用了递归CTE:
WITH
WholeDaysBetween(dt, wkday) AS
(
SELECT DATEADD(DAY, 1, @StartDate), DATEPART(WEEKDAY, DATEADD(DAY, 1, @StartDate))
UNION ALL
SELECT DATEADD(DAY, 1, dt), DATEPART(WEEKDAY, DATEADD(DAY, 1, dt))
FROM WholeDaysBetween
WHERE dt < DATEADD(DAY, -1, @EndDate)
)
当然,要使其正常工作,您必须adjust your datefirst settings。
最终查询如下(我使用了与您的评论相同的示例数据):
set datefirst 1; -- day starts on Monday
declare @StartDate datetime = '2018-09-21 23:59:00';
declare @EndDate datetime = '2018-09-24 00:01:00';
WITH
WholeDaysBetween(dt, wkday) AS
(
SELECT DATEADD(DAY, 1, @StartDate), DATEPART(WEEKDAY, DATEADD(DAY, 1, @StartDate))
UNION ALL
SELECT DATEADD(DAY, 1, dt), DATEPART(WEEKDAY, DATEADD(DAY, 1, dt))
FROM WholeDaysBetween
WHERE dt < DATEADD(DAY, -1, @EndDate)
)
SELECT
-- whole weekdays between @StartDate and @EndDate,
-- multiplied by minutes per day
(
SELECT COUNT(*) * 24 * 60
FROM WholeDaysBetween
WHERE wkday <= 5
)
+
-- minutes from @StartDate date to end of @StartDate
-- as long as @StartDate isn't on weekend
(
SELECT
CASE
WHEN DATEPART(WEEKDAY, @StartDate) <= 5
THEN
(24 * 60) -
(DATEPART(HOUR, @StartDate) * 60) -
(DATEPART(MINUTE, @StartDate))
ELSE 0
END
)
+
-- minutes from start of @EndDate's date to @EndDate
-- as long as @EndDate isn't on weekend
(
SELECT
CASE
WHEN DATEPART(WEEKDAY, @EndDate) <= 5
THEN
(DATEPART(HOUR, @EndDate) * 60) +
(DATEPART(MINUTE, @EndDate))
ELSE 0
END
)