【问题标题】:Split the given date into days将给定日期拆分为天
【发布时间】:2015-07-22 10:57:04
【问题描述】:

我正在编写一个存储过程。基本上我需要从用户那里获取年月信息并做一些事情。但我的问题是我没有检测到给定月份有多少天。让我举例说明;

用户按我说的给我年份和月份;

@year = 2015

@month = 07

所以我必须创建类似的行;

2015-07-01

2015-07-02

.....

2015-07-31

我的计划是将这些行一一添加到另一个临时表中。

我的 SP 的最后状态是;

ALTER PROCEDURE [dbo].[Usp_CreateStats]
(
 @Year VARCHAR (40),
 @Month VARCHAR (40)
 )
AS
BEGIN

DECLARE @StartTime VARCHAR (10)
DECLARE @EndTime VARCHAR (10)
SET @StartTime = '00:00:00'
SET @EndTime = '23:59:59'

DECLARE @Peoples TABLE (name VARCHAR(50))
INSERT INTO @Peoples (name) select distinct name from USERINFO

DECLARE @Dates TABLE (date VARCHAR(50))



END

我希望解释正确。

【问题讨论】:

标签: sql sql-server sql-server-2008


【解决方案1】:

我已将年份和月份变量更改为 int 数据类型, 将日期列的名称改为dates_date,并使用convert 和带有dateadd 函数的while 循环来填充@dates 表:

ALTER PROCEDURE [dbo].[Usp_CreateStats]
(
 @Year int,
 @Month int
 )
AS
BEGIN

DECLARE @StartTime VARCHAR (10),
        @EndTime VARCHAR (10),
        @Date date
SET @StartTime = '00:00:00'
SET @EndTime = '23:59:59'

SET @Date = CONVERT(date, right('0000' + cast(@year as char(4)), 4) + right('00' + cast(@month as char(2)), 2)+ '01', 112)

DECLARE @Peoples TABLE (name VARCHAR(50))
INSERT INTO @Peoples (name) select distinct name from USERINFO

DECLARE @Dates TABLE (dates_date date)

while month(@date) = @Month    
begin
    insert into @Dates (dates_date) values (@Date)
    set @Date = dateadd(day, 1, @date)
end

END

【讨论】:

  • 这里不需要WHILE 循环。
  • 我同意您的答案中的计数表是一个更优雅的解决方案。但是我没有足够的理货表经验,所以...
  • 没关系。但我建议你开始阅读它。我学过的最好的工具之一。
  • @ZoharPeled 是关于该主题的优秀文章。 sqlservercentral.com/articles/T-SQL/62867
【解决方案2】:

要根据@year@month 生成日期,您可以使用Tally Table

DECLARE @year   INT = 2015,
        @month  INT = 7

DECLARE @startDate DATE = DATEADD(MONTH, @month - 1, DATEADD(YEAR, @year - 1900, CAST('19000101' AS DATE)))

;WITH Tally (n) AS
(   -- 100 rows
    SELECT TOP(31) 
        ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
    FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
)
SELECT
    DATEADD(DAY, N-1, @startDate)
FROM Tally t
WHERE
    DATEADD(DAY, N-1, @startDate) < DATEADD(MONTH, 1, @startDate)

SQL Fiddle


注意:为变量使用适当的数据类型。在这种情况下,@month@year 应该是 INT

【讨论】:

    【解决方案3】:

    您可以使用递归 CTE。

    Declare @Year as varchar(5) = '2015'
    Declare @Month as varchar(5) = '05'
    
    Declare @DateOfMonth as DateTime =  @Month + '-01-' + @Year
    
    Declare @startdate datetime, @enddate datetime
    
    SET @startdate = DATEADD(s,0,DATEADD(mm, DATEDIFF(m,0,@DateOfMonth),0))
    SET @enddate = DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,@DateOfMonth)+1,0))
    SET @enddate = DATEADD(day,-1,@enddate)
    
    ;With DateSequence( Date ) as
    (
        Select @startdate as Date
            union all
        Select dateadd(day, 1, Date)
            from DateSequence
            where Date < @enddate
    )
    
    --select result
    Select * from DateSequence option (MaxRecursion 1000)
    

    【讨论】:

    • 我建议不要使用递归 CTE,因为这是一种隐藏的 RBAR。阅读 Jeff Moden 的 article 了解更多信息。
    【解决方案4】:
    DECLARE @Month int= 7 
    Declare @Year int = 2015
    Declare @MonthStart date = CAST(@Year  as varchar(4)) +  Right('00' +    Cast(@Month as varchar(2)), 2)  + '01'
    
    
    DECLARE @DaysInMonth int = (SELECT
                        datediff(day, dateadd(day, 1-day(@MonthStart),     @MonthStart),
                        dateadd(month, 1, dateadd(day, 1-day(@MonthStart), @MonthStart))))
    
    Declare @Dates TABLE (date date) 
    
    DECLARE @Counter int = 0 
    WHILE @Counter < @DaysInMonth 
    BEGIN
    INSERT INTO @Dates SELECT DATEADD(Day, @Counter,  @MonthStart)
    
    SET @Counter = @Counter + 1 
    END
    

    【讨论】:

    • 我在这里抽签有点慢。 Zohar Peled 的解决方案比我的更优雅。
    【解决方案5】:

    您可以使用的 CTE:

    DECLARE @year               INT = 2015,
            @month              INT = 7,
            @first_day_of_month DATETIME,
            @last_day_of_month  DATETIME 
    
    select @first_day_of_month = cast(cast(@year as varchar(4))+'/'+cast(@month as varchar(2))+'/'+'01' as date)
    select @last_day_of_month = dateadd(mm,datediff(mm,0,@first_day_of_month)+1,0)-1
    
    ;with mycte as 
    (
    select @first_day_of_month as src
    union all
    select dateadd(dd,1,src) from mycte
    where src < @last_day_of_month
    
    )select * from mycte
    

    【讨论】:

      【解决方案6】:

      使用递归 CTE,

      DECLARE @year  VARCHAR(4) = '2015',
              @month VARCHAR(2)= '02',
              @date  DATETIME
      
      SET @date= CONVERT(DATETIME, @year + '-' + @month + '-01');
      
      WITH cte
           AS (SELECT @date AS dt
               UNION ALL
               SELECT Dateadd(dd, 1, dt) AS dt
               FROM   cte
               WHERE  dt < Dateadd(dd, -1, Dateadd(mm, Datediff(mm, 0, @date) + 1, 0)))
      SELECT *
      FROM   cte 
      

      【讨论】:

        【解决方案7】:

        这是另一个(更快的)计数解决方案:

        DECLARE @year   INT = 2015,
                @month  INT = 7
        
        DECLARE @start datetime = dateadd(m, (@year- 1900) * 12 + 7, -31)
        
        SELECT TOP(datediff(d, @start, dateadd(m, 1, @start))) 
          dateadd(d, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) - 1, @start)
        FROM 
          (VALUES(0),(0),(0),(0),(0),(0)) a(n)
           CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0)) b(n)
        

        【讨论】:

          【解决方案8】:
          convert(varchar,OrderDate,111)='2015-07-01'
          

          【讨论】:

          • 你能解释一下这是如何回答这个问题的吗? OP 希望根据年份和月份获取日期
          猜你喜欢
          • 2022-10-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-08
          相关资源
          最近更新 更多