C#到T-SQL - 找到最近的工作日

时间:2017-09-18 16:55:27

标签: c# sql tsql datetime linq-to-sql

任何C#/ T-SQL专家都可以帮助我吗?我很难将这个C#代码转换为T-SQL。

这是C#代码:

        public (int distance, int absoluteDistance) MinimumDayOfWeekDistance(DayOfWeek dayOfWeekOne,
        DayOfWeek dayOfWeekTwo)
    {
        int forwardDaysDifference(int iteratorDayOfWeekOne, int iteratorDayOfWeekTwo) => iteratorDayOfWeekTwo -
                                                                                         iteratorDayOfWeekOne +
                                                                                         ((iteratorDayOfWeekOne >
                                                                                           iteratorDayOfWeekTwo)
                                                                                             ? 7
                                                                                             : 0);

        int forwardOneTwo = forwardDaysDifference((int) dayOfWeekOne, (int) dayOfWeekTwo);
        int forwardTwoOne = forwardDaysDifference((int) dayOfWeekTwo, (int) dayOfWeekOne);
        if (forwardOneTwo < forwardTwoOne)
        {
            return (forwardOneTwo, forwardOneTwo);
        }
        return (-forwardTwoOne, forwardTwoOne);
    }

    public int DaysToClosestDayOfWeek(DayOfWeek dayOfWeekOne, List<DayOfWeek> dayOfWeekList)
    {
        return dayOfWeekList.Select(dayOfWeek => MinimumDayOfWeekDistance(dayOfWeekOne, dayOfWeek))
            .Aggregate((x, y) => (x.absoluteDistance < y.absoluteDistance)
                ? x
                : ((x.absoluteDistance == y.absoluteDistance) ? ((x.distance > 0) ? x : y) : y)).distance;
    }

以下是实施:

int dayOfWeekDistance = this.DaysToClosestDayOfWeek(passedInDateParameter.DayOfWeek, myDayOfWeekList);
passedInDateParameter = passedInDateParameter.AddDays(dayOfWeekDistance);

这个想法是你有一个列表(C#侧的DayOfWeekList),并且该列表可以在一周中的任何一天填充。可能是周一和周二,可能是周二,周三,周五和周六等。

传入的DateTime参数与该列表进行比较,并根据列表中最接近的工作日进行更新。

例如,如果传入的DateTime参数是星期日,而我的DayOfWeek列表包含星期三和星期六,则该参数需要移回星期六,因为它在列表中最接近。

同样,如果我的列表包含星期日,星期一和星期六,并且传入的参数是星期四,则该参数必须移至星期六。

最后,如果参数与列表中的两个工作日相等(周三传入,周一和周五在列表中......或周日传入,周二和周五在列表中),则参数需要向前移动到下一个最接近的工作日(在第一种情况下,将是星期五,在第二种情况下是星期二)。

到目前为止,在SQL方面,我有一个VARCHAR变量,表示基于每个工作日的数字列表。例如,变量类似于&#34; 126&#34;如果星期日,星期一和星期五应该包含在变量中。

此外,在SQL方面,最好从列表中最接近的工作日返回输入参数的最小距离。

例如,如果我在星期四(或当前实施的方式为5)以及星期日,星期一和星期二传入列表(或1,2和3),则应该-2返回(因为星期二在列表中最接近星期四),这样我就可以从目标日期时间变量中减去2天(日期时间变量超出了这个问题的范围,仅供参考)。

简单地说,应该返回输入参数距离最接近的星期几的距离,它们可以是正数或负数,以便有效地计算应该添加或减去的天数。

到目前为止,实现在C#中运行良好,我只是很难将其转换为SQL,因为它不是我最好的语言。

对此的任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

我已经找到了答案。首先,SQL Fiddle

这是SQL:

create table test_Table (Days int);

declare @DaysList int
declare @x int
declare @Day int

set @DaysList =  36 
/*This is the variable that you mention in your original question. 
For example, 36 means Tuesday and Friday*/

set @Day = 1 
/*This is the day that you're checking against*/

set @x = 1
/*This is just our while loop variable*/

WHILE @x <= len(@DaysList)
  BEGIN 
    INSERT INTO test_Table
    SELECT substring(cast(@DaysList as nvarchar(7)),@x,1)
    SET @X = @X + 1 
  END 
/*The while loop is taking each digit in your @DaysList variable 
and putting it in its own row. 
This makes it so that we can compare our day to each day in the list
more easily.*/

;with cte as (SELECT case when abs(@Day-days) < 7-abs(@Day-days)
            then abs(@Day-days) 
            else 7-abs(@Day-days) end daysDiff
/*This field finds the number of days between the @Day value and the day
from @DaysList that we're comparing to. Basically, the smaller the number 
the better.*/
, days
/*This value is going to be the result of our query*/
, case when days-@Day between 0 and 8 
   then days-@Day
   when days+7-@day between 0 and 8
   then days+7-@Day 
   else -100 end a
/*This is doing a bunch of stuff to meet the requirement of finding the "next day" 
if the day is in the next week (for example, if @DaysList is Friday and Sunday (6 and 1)
and our @Day is Saturday (7), we want to show 1, not 6.*/ 
FROM #test_Table)

SELECT TOP 1 days FROM cte ORDER BY DaysDiff Asc, a
/*this just gives us our final result.*/

如果您希望将其作为SQL函数,则可以创建函数:

create function ClosestDayToDate(@Day int, @DaysList int)
RETURNS int AS BEGIN 

declare @x int
declare @result int
declare @test_table table (days int)
set @x = 1 

WHILE @x <= len(@DaysList)
  BEGIN 
    INSERT INTO @test_Table
    SELECT substring(cast(@DaysList as nvarchar(7)),@x,1)
    SET @X = @X + 1 
  END 

;with cte as (SELECT case when abs(@Day-days) < 7-abs(@Day-days)
            then abs(@Day-days) 
            else 7-abs(@Day-days) end daysDiff
, days
, case when days-@Day between 0 and 8 
   then days-@Day
   when days+7-@day between 0 and 8
   then days+7-@Day 
   else -100 end a
FROM @test_Table)

Select @Result = (SELECT TOP 1 days FROM cte ORDER BY DaysDiff Asc, a)

return @result
END

然后,您可以运行该功能:

SELECT dbo.ClosestDayToDate(1,72)

哪会返回2

如果您需要更多澄清,请告诉我。

编辑:我仍然非常喜欢这个功能,所以我继续编辑它以缩短它。我认为这是我能得到的最短时间而不是完全重新编写它的编写方式,但是:

create function ClosestDayToDate(@Day int, @DaysList int)
RETURNS int AS BEGIN 

declare @x int
declare @result int
declare @test_table table (days int)
set @x = 1 

WHILE @x <= len(@DaysList)
  BEGIN 
    INSERT INTO @test_Table
    SELECT substring(cast(@DaysList as nvarchar(7)),@x,1)
    SET @X = @X + 1 
  END 

;with cte as (SELECT days
, case when days-@Day between 0 and 8 
       then days-@Day
       when days+7-@day between 0 and 8
       then days+7-@Day 
       end a

FROM @test_Table)

Select @Result = (SELECT TOP 1 days FROM cte ORDER BY a)

return @result
END

这是函数的SQL Fiddle

编辑:根据您的要求,我已更新了该功能。现在它将显示投放的“日期”和“日期列表”中最近的日期之间的天数。如果日期在之前,它将显示负数。

仅供参考 - 我没有像以前的版本那样对此进行详尽的测试,但在我做过的测试中,它工作正常。

create function ClosestDayToDate(@Day int, @DaysList int)
RETURNS int AS BEGIN 

declare @x int
declare @result int
declare @test_table table (days int)
set @x = 1 

WHILE @x <= len(@DaysList)
  BEGIN 
    INSERT INTO @test_Table
    SELECT substring(cast(@DaysList as nvarchar(7)),@x,1)
    SET @X = @X + 1 
  END 

;with cte as (SELECT days
, case when abs(days-@Day) < days+7-@Day then days-@Day else days+7-@Day end b
, case when abs(days-@Day) < days+7-@Day then abs(days-@Day) else days+7-@Day end c         
FROM @test_Table)

Select @Result = (SELECT TOP 1 b FROM cte ORDER BY c, b desc)

return @result
END

SQL Fiddle.