【问题标题】:Microsoft SQL Server 2005 Function, passing list of start and end timesMicrosoft SQL Server 2005 函数,传递开始和结束时间列表
【发布时间】:2010-04-14 16:02:44
【问题描述】:

我希望将动态数量的一个开始/结束时间对作为输入参数传递给函数。然后,该函数将在 select 语句中使用列表,而不是只使用一个开始时间和一个结束时间。

CREATE FUNCTION [dbo].[GetData]
(
    @StartTime datetime,
    @EndTime datetime
)
RETURNS int
AS
BEGIN
    SELECT @EndTime = CASE WHEN @EndTime > CURRENT_TIMESTAMP THEN CURRENT_TIMESTAMP ELSE @EndTime END

    DECLARE @TempStates TABLE
        (StartTime datetime NOT NULL
        , EndTime datetime NOT NULL
        , StateIdentity int NOT NULL
        )

    INSERT INTO @TempStates
    SELECT StartTime
        , EndTime
        , StateIdentity
    FROM State
    WHERE StartTime <= @EndTime AND EndTime >= @StartTime

    RETURN 0
END

【问题讨论】:

    标签: sql-server sql-server-2005 user-defined-functions stored-functions


    【解决方案1】:

    您需要一种方法来拆分和处理 TSQL 中的字符串,有很多方法可以做到这一点。本文涵盖了几乎所有方法的优缺点:

    "Arrays and Lists in SQL Server 2005 and Beyond, When Table Value Parameters Do Not Cut it" by Erland Sommarskog

    您需要创建一个拆分函数。这就是拆分函数的使用方式:

    SELECT
        *
        FROM YourTable                               y
        INNER JOIN dbo.yourSplitFunction(@Parameter) s ON y.ID=s.Value
    

    I prefer the number table approach to split a string in TSQL 但是在 SQL Server 中有多种拆分字符串的方法,请参阅上一个链接,其中解释了每种方法的优点和缺点。

    要使 Numbers Table 方法起作用,您需要设置一个时间表,这将创建一个包含 1 到 10,000 行的表 Numbers

    SELECT TOP 10000 IDENTITY(int,1,1) AS Number
        INTO Numbers
        FROM sys.objects s1
        CROSS JOIN sys.objects s2
    ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
    

    一旦设置了 Numbers 表,创建这个拆分函数:

    CREATE FUNCTION [dbo].[FN_ListToTableRows]
    (
         @SplitOn  char(1)      --REQUIRED, the character to split the @List string on
        ,@List     varchar(8000)--REQUIRED, the list to split apart
    )
    RETURNS TABLE
    AS
    RETURN 
    (
        ----------------
        --SINGLE QUERY-- --this will return empty rows, and row numbers
        ----------------
        SELECT
            ROW_NUMBER() OVER(ORDER BY number) AS RowNumber
                ,LTRIM(RTRIM(SUBSTRING(ListValue, number+1, CHARINDEX(@SplitOn, ListValue, number+1)-number - 1))) AS ListValue
            FROM (
                     SELECT @SplitOn + @List + @SplitOn AS ListValue
                 ) AS InnerQuery
                INNER JOIN Numbers n ON n.Number < LEN(InnerQuery.ListValue)
            WHERE SUBSTRING(ListValue, number, 1) = @SplitOn
    );
    GO 
    

    测试分裂:

    SELECT
        RowNumber, CONVERT(datetime,ListValue) AS ListValue 
        FROM  dbo.FN_ListToTableRows(',','1/1/2010 12:45am,,2/2/2010 1:23pm,3/3/2010 12:45')
    

    输出:

    RowNumber            ListValue
    -------------------- -----------------------
    1                    2010-01-01 00:45:00.000
    2                    1900-01-01 00:00:00.000
    3                    2010-02-02 13:23:00.000
    4                    2010-03-03 12:45:00.000
    
    (4 row(s) affected)
    

    注意输入字符串中的缺失值:

    '1/1/2010 12:45am,,2/2/2010 1:23pm,3/3/2010 12:45'
                     ^^
    

    在函数的结果集中创建了一个空字符串值,将其 CONVERT 更改为 1900-01-01 00:00:00.000,您可以使用 CASE 语句以不同的方式处理这些值。

    然后创建你的函数。这是基于问题中的代码。我不确定它做了什么,因为它返回一个int,它始终为零,并且对查询没有任何作用。但它来自 OPs 功能,所以它必须是他们正在做的事情的简单形式:

    CREATE FUNCTION [dbo].[GetData]
    (
        @StartTime varchar(8000),  --CSV string of dates: '1/1/2010 12:45am,,2/2/2010 1:23pm,3/3/2010 12:45'
        @EndTime varchar(8000)     --CSV string of dates: '1/1/2010 12:45am,,2/2/2010 1:23pm,3/3/2010 12:45'
    )
    RETURNS int
    AS
    BEGIN
        DECLARE @TempStates TABLE
            (StartTime datetime NOT NULL
            , EndTime datetime NOT NULL
            , StateIdentity int NOT NULL
            )
    
        INSERT INTO @TempStates
        SELECT s.StartTime
            , s.EndTime
            , s.StateIdentity
        FROM State s
            CROSS JOIN (SELECT
                            a1.RowNumber
                                ,CONVERT(datetime,a1.ListValue) AS StartTime
                                ,CASE
                                    WHEN a2.ListValue > GETDATE() THEN GETDATE()
                                    ELSE CONVERT(datetime,a2.ListValue)
                                END AS EndTime
                            FROM dbo.FN_ListToTableRows(',',@StartTime)          a1
                                INNER JOIN dbo.FN_ListToTableRows(',',@EndTime)  a2 ON a1.RowNumber=a2.RowNumber
                       ) dt
        WHERE s.StartTime <= dt.EndTime AND s.EndTime >= dt.StartTime
        RETURN 0
    END
    GO
    

    【讨论】:

      【解决方案2】:

      阅读 Erland Sommarskog 在Arrays and Lists in SQL Server 2005 and Beyond - When TVPs Do Not Cut it 上的出色(而且很长!)文章。我敢打赌,这不仅能涵盖您的问题 :-)

      【讨论】:

        【解决方案3】:

        我可能会使用 OpenXML。如果您想了解它的工作原理,我写了a blog article

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-30
          • 1970-01-01
          • 1970-01-01
          • 2013-11-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多