【问题标题】:How to return multiple values from stored procedure within tsql query如何从 tsql 查询中的存储过程返回多个值
【发布时间】:2014-12-23 10:13:35
【问题描述】:

我有一个包含四个datetime 列的约会表:

AppointmentStart
AppointmentEnd
Arrival
Departure

现在数据一团糟,有时到达和/或离开是空的,在未来,在过去,整个节目。 AppointmentEnd 甚至被发现在 AppointmentStart 之前。

我编写了一个存储过程来计算开始和结束时间,以及约会的长度。

ALTER PROCEDURE Calcdates @ApptStart DATETIME,
                          @ApptEnd   DATETIME,
                          @PatArrive DATETIME,
                          @PatDepart DATETIME
AS
    DECLARE @CalcStart DATETIME
    DECLARE @CalcEnd DATETIME
    DECLARE @CalcLen INT

    -- CALCULATED STARTDATETIME
    SET @CalcStart = CASE
                       WHEN @PatArrive IS NULL THEN @ApptStart
                       WHEN @PatArrive IS NOT NULL THEN
                         CASE
                           WHEN @PatArrive BETWEEN Dateadd(MINUTE, -60, @ApptStart) AND Dateadd(MINUTE, 480, @ApptStart) THEN @PatArrive
                           ELSE @ApptStart
                         END
                     END
    -- CALCUALTED ENDDATETIME
    SET @CalcEnd = CASE
                     WHEN @PatDepart IS NULL THEN
                       CASE
                         WHEN @ApptEnd BETWEEN @ApptStart AND Dateadd(MINUTE, 480, @ApptStart) THEN @ApptEnd
                         ELSE Dateadd(MINUTE, 30, @ApptStart)
                       END
                     WHEN @PatDepart IS NOT NULL THEN
                       CASE
                         WHEN @PatDepart BETWEEN @ApptStart AND Dateadd(MINUTE, 480, @ApptStart) THEN @PatDepart
                         ELSE
                           CASE
                             WHEN @ApptEnd BETWEEN @ApptStart AND Dateadd(MINUTE, 480, @ApptStart) THEN @ApptEnd
                             ELSE Dateadd(MINUTE, 30, @ApptStart)
                           END
                       END
                   END
    -- CALCULATED LENGTH
    SET @CalcLen = Datediff(MINUTE, @CalcStart, @CalcEnd) 

我现在需要做的是弄清楚如何在我的查询中包含这个返回值。

即这是我目前拥有的

SELECT PrimKey,
       Name,
       AppointmentStart,
       AppointmentEnd,
       PatArrive,
       PatDepart,
       Somehow have the results of the SP here
FROM   MyTable 

我想做的是在该查询中调用 SP,并为我提供每条记录的计算值。

有人知道怎么做吗?还是我走错了路?

【问题讨论】:

  • 如果将 SQL 语句放在存储过程中会发生什么?它肯定会根据需要返回recorset吗?如果不是,那是什么问题?
  • 对不起,也许我应该解释得更好。该查询是另一个执行 ETL 的 SP 的一部分。我刚刚更新了问题,希望更清楚

标签: sql sql-server tsql datetime stored-procedures


【解决方案1】:

您对CalcStartCalcEnd 的计算仅取决于行中的数据,这很好。只有CalcLen 的计算依赖于另一个计算。因此,您可以在 CTE 中内联计算前两个(即CalcStartCalcEnd),然后使用它们的计算值来获得CalcLen,这一切都在一次完成。

试试下面的。我所做的只是将您的最终 SELECT 语句替换为“以某种方式在此处获得 SP 的结果”,并将开始和结束日期计算为列而不是变量。该组合查询是 CTE 的基础,然后从中选择并现在包括 CalcStartCalcEnd 的计算值。那时我只是将第三个计算添加为另一列。

;WITH cte AS
(
   SELECT PrimKey,
          Name,
          AppointmentStart,
          AppointmentEnd,
          PatArrive,
          PatDepart,
          CASE
              WHEN PatArrive IS NULL THEN AppointmentStart
              WHEN PatArrive IS NOT NULL THEN
                   CASE
                       WHEN PatArrive BETWEEN Dateadd(MINUTE, -60, AppointmentStart)
                                AND Dateadd(MINUTE, 480, AppointmentStart)
                                     THEN PatArrive
                       ELSE AppointmentStart
                   END
              END AS [CalcStart],
          CASE
              WHEN PatDepart IS NULL THEN
                   CASE
                       WHEN AppointmentEnd BETWEEN AppointmentStart AND
                          Dateadd(MINUTE, 480, AppointmentStart) THEN AppointmentEnd
                       ELSE Dateadd(MINUTE, 30, AppointmentStart)
                   END
              WHEN PatDepart IS NOT NULL THEN
                   CASE
                       WHEN PatDepart BETWEEN AppointmentStart AND
                            Dateadd(MINUTE, 480, AppointmentStart) THEN PatDepart
                       ELSE
                           CASE
                               WHEN AppointmentEnd BETWEEN AppointmentStart
                                    AND Dateadd(MINUTE, 480, AppointmentStart)
                                    THEN AppointmentEnd
                               ELSE Dateadd(MINUTE, 30, AppointmentStart)
                           END
                       END
                   END AS [CalcEnd]
    FROM   MyTable
)
SELECT *, Datediff(MINUTE, [CalcStart], [CalcEnd]) AS [CalcLen]
FROM cte;

仅供将来参考,还可以选择创建一个内联 TVF,它将 4 列作为输入参数,CTE 只是为了得到 [CalcStart][CalcEnd],并吐出这些值加上计算[CalcLen].

CREATE FUNCTION CalculateDates (@AppointmentStart DATETIME,
                                @AppointmentEnd DATETIME,
                                @PatArrive DATETIME,
                                @PatDepart DATETIME)
RETURNS TABLE
AS RETURN
WITH cte AS
(
    SELECT 
         CASE
             WHEN @PatArrive IS NULL THEN @AppointmentStart
             WHEN @PatArrive IS NOT NULL THEN
                 CASE
                     WHEN @PatArrive BETWEEN DATEADD(MINUTE, -60, @AppointmentStart)
                             AND DATEADD(MINUTE, 480, @AppointmentStart)
                                      THEN @PatArrive
                     ELSE @AppointmentStart
                 END
             END AS [CalcStart],
         CASE
             WHEN @PatDepart IS NULL THEN
                 CASE
                     WHEN @AppointmentEnd BETWEEN @AppointmentStart AND
                        DATEADD(MINUTE, 480, @AppointmentStart) THEN @AppointmentEnd
                     ELSE DATEADD(MINUTE, 30, @AppointmentStart)
                 END
             WHEN @PatDepart IS NOT NULL THEN
                 CASE
                     WHEN @PatDepart BETWEEN @AppointmentStart AND
                         DATEADD(MINUTE, 480, @AppointmentStart) THEN @PatDepart
                     ELSE
                         CASE
                             WHEN @AppointmentEnd BETWEEN @AppointmentStart
                                 AND DATEADD(MINUTE, 480, @AppointmentStart)
                                 THEN @AppointmentEnd
                             ELSE DATEADD(MINUTE, 30, @AppointmentStart)
                         END
                     END
                 END AS [CalcEnd]
)
SELECT [CalcStart], [CalcEnd], DATEDIFF(MINUTE, [CalcStart], [CalcEnd]) AS [CalcLen]
FROM cte;
GO

函数的用法如下:

SELECT mt.PrimKey,
       mt.Name,
       mt.AppointmentStart,
       mt.AppointmentEnd,
       mt.PatArrive,
       mt.PatDepart,
       dates.[CalcStart],
       dates.[CalcEnd],
       dates.[CalcLen]
FROM MyTable mt
CROSS APPLY CalculateDates(mt.AppointmentStart,
                           mt.AppointmentEnd,
                           mt.PatArrive,
                           mt.PatDepart) dates;

但是,如果您不打算在多个地方使用这些计算,则没有必要在第一个示例中显示的内联 CTE 上使用该函数。还要记住,内联 TVF 比多行 TVF 更有效(您可以执行多个步骤、插入表变量并在最后返回该变量的样式),因此需要在内部重用 CTE 概念函数。

【讨论】:

  • 太棒了,谢谢。我以前从未遇到过 TVF,我应该开始研究它。
【解决方案2】:

创建一个表值函数并在 select 语句中使用它(未测试)。

CREATE FUNCTION Calcdates (@ApptStart DATETIME,
                           @ApptEnd   DATETIME,
                           @PatArrive DATETIME,
                           @PatDepart DATETIME)
RETURNS @Calcdates TABLE (
  CalcStart DATETIME,
  CalcEnd   DATETIME,
  CalcLen   INT )
AS
  BEGIN
      DECLARE @CalcStart DATETIME
      DECLARE @CalcEnd DATETIME
      DECLARE @CalcLen INT

      SET @CalcStart = CASE
                         WHEN @PatArrive IS NULL THEN @ApptStart
                         WHEN @PatArrive IS NOT NULL THEN
                           CASE
                             WHEN @PatArrive BETWEEN Dateadd(MINUTE, -60, @ApptStart) AND Dateadd(MINUTE, 480, @ApptStart) THEN @PatArrive
                             ELSE @ApptStart
                           END
                       END
      -- CALCUALTED ENDDATETIME
      SET @CalcEnd = CASE
                       WHEN @PatDepart IS NULL THEN
                         CASE
                           WHEN @ApptEnd BETWEEN @ApptStart AND Dateadd(MINUTE, 480, @ApptStart) THEN @ApptEnd
                           ELSE Dateadd(MINUTE, 30, @ApptStart)
                         END
                       WHEN @PatDepart IS NOT NULL THEN
                         CASE
                           WHEN @PatDepart BETWEEN @ApptStart AND Dateadd(MINUTE, 480, @ApptStart) THEN @PatDepart
                           ELSE
                             CASE
                               WHEN @ApptEnd BETWEEN @ApptStart AND Dateadd(MINUTE, 480, @ApptStart) THEN @ApptEnd
                               ELSE Dateadd(MINUTE, 30, @ApptStart)
                             END
                         END
                     END
      -- CALCULATED LENGTH
      SET @CalcLen = Datediff(MINUTE, @CalcStart, @CalcEnd)

      INSERT INTO @Calcdates
      VALUES      (@CalcStart,@CalcEnd,@CalcLen)

      RETURN;
  END;

SELECT PrimKey,
       Name,
       AppointmentStart,
       AppointmentEnd,
       PatArrive,
       PatDepart,
       T.CalcStart,
       T.CalcEnd,
       T.CalcLen
FROM   MyTable
       CROSS apply Calcdates(AppointmentStart, AppointmentEnd, PatArrive, PatDepart) as T

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-25
    • 2012-12-24
    • 1970-01-01
    • 2012-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多