【问题标题】:Best way to calculate Max/Min of N columns in SQL Server在 SQL Server 中计算 N 列的 Max/Min 的最佳方法
【发布时间】:2017-09-01 23:17:32
【问题描述】:

好的,首先我看到了this thread。但没有一个解决方案非常令人满意。指定的答案看起来像 NULL 会破坏它,而评分最高的答案看起来很难维护。 所以我想知道类似以下的事情:

CREATE FUNCTION GetMaxDates
(
    @dte1 datetime,
    @dte2 datetime,
    @dte3 datetime,
    @dte4 datetime,
    @dte5 datetime
)
RETURNS datetime
AS
BEGIN
    RETURN (SELECT Max(TheDate)
        FROM
        (
            SELECT @dte1 AS TheDate
            UNION ALL
            SELECT @dte2 AS TheDate
            UNION ALL
            SELECT @dte3 AS TheDate
            UNION ALL
            SELECT @dte4 AS TheDate
            UNION ALL
            SELECT @dte5 AS TheDate) AS Dates
        )
END
GO

我看到的主要问题是,如果只有 3 个要比较的字段,您仍然必须为其他 2 个指定 NULL,如果您想将其扩展到 6 个比较,它将破坏现有的使用。如果它是一个参数化的存储过程,您可以为每个参数指定一个默认值,并且添加新参数不会破坏现有的引用。同样的方法显然也可以扩展到其他数据类型或诸如 Min 或 Avg 之类的东西。这有一些我没有发现的主要缺点吗?请注意,无论传递给它的部分、全部或全部值是空值还是重复值,此函数都有效。

【问题讨论】:

  • 2.5 年后突然出现无法解释的否决票?

标签: sql sql-server tsql


【解决方案1】:

您可以使用 ISNULL 函数解决 null 问题:

SELECT ISNULL(@dte1,0) AS TheDate
UNION ALL
SELECT ISNULL(@dte2,0) AS TheDate
UNION ALL
SELECT ISNULL(@dte3,0) AS TheDate
UNION ALL
SELECT ISNULL(@dte4,0) AS TheDate
UNION ALL
SELECT ISNULL(@dte5,0) AS TheDate) AS Dates

但它只适用于 MAX 函数。

这是另一个建议http://www.sommarskog.se/arrays-in-sql-2005.html

他们建议以字符串形式使用逗号分隔值。

该函数采用任意数量的参数,如下所示:

CREATE FUNCTION GetMaxDate
(
 @p_dates VARCHAR(MAX)
)
RETURNS DATETIME
AS
BEGIN
DECLARE @pos INT, @nextpos INT, @date_tmp DATETIME, @max_date DATETIME, @valuelen INT

SELECT @pos = 0, @nextpos = 1
SELECT @max_date = CONVERT(DATETIME,0)


WHILE @nextpos > 0
BEGIN
   SELECT @nextpos = charindex(',', @p_dates, @pos + 1)
   SELECT @valuelen = CASE WHEN @nextpos > 0
      THEN @nextpos
      ELSE len(@p_dates) + 1
      END - @pos - 1
   SELECT @date_tmp = CONVERT(DATETIME, substring(@p_dates, @pos + 1, @valuelen))

    IF @date_tmp > @max_date
SET @max_date = @date_tmp

SELECT @pos = @nextpos
END

RETURN @max_date
END

然后调用:

DECLARE @dt1 DATETIME
DECLARE @dt2 DATETIME
DECLARE @dt3 DATETIME
DECLARE @dt_string VARCHAR(MAX)

SET @dt1 = DATEADD(HOUR,3,GETDATE())
SET @dt2 = DATEADD(HOUR,-3,GETDATE())
SET @dt3 = DATEADD(HOUR,5,GETDATE())

SET @dt_string = CONVERT(VARCHAR(50),@dt1,21)+','+CONVERT(VARCHAR(50),@dt2,21)+','+CONVERT(VARCHAR(50),@dt3,21)
SELECT dbo.GetMaxDate(@dt_string)

【讨论】:

  • 其实我觉得我的函数不会被空值打扰。
【解决方案2】:

为什么不只是:

SELECT Max(TheDate)        
FROM        
(
    SELECT @dte1 AS TheDate WHERE @dte1 IS NOT NULL
    UNION ALL
    SELECT @dte2 AS TheDate WHERE @dte2 IS NOT NULL
    UNION ALL
    SELECT @dte3 AS TheDate WHERE @dte3 IS NOT NULL
    UNION ALL
    SELECT @dte4 AS TheDate WHERE @dte4 IS NOT NULL
    UNION ALL
    SELECT @dte5 AS TheDate WHERE @dte5 IS NOT NULL) AS Dates        

应该在不引入任何新值的情况下解决 null 问题

【讨论】:

    【解决方案3】:

    我会在 XML 中传递日期(您可以使用 varchar/etc,也可以转换为 xml 数据类型):

    DECLARE @output DateTime
    DECLARE @test XML
        SET @test = '<VALUES><VALUE>1</VALUE><VALUE>2</VALUE></VALUES>'
    
    DECLARE @docHandle int 
    EXEC sp_xml_preparedocument @docHandle OUTPUT, @doc 
    
    SET @output = SELECT MAX(TheDate)
                    FROM (SELECT t.value('./VALUE[1]','DateTime') AS 'TheDate'
                            FROM OPENXML(@docHandle, '//VALUES', 1) t)
    
    EXEC sp_xml_removedocument @docHandle
    
    RETURN @output
    

    这将解决处理尽可能多的可能性的问题,并且我不会费心在 xml 中放置空值。

    我会使用单独的参数来指定日期类型,而不是每次都自定义 xml 和支持代码,但您可能需要使用动态 SQL 才能使其工作。

    【讨论】:

    • 我没有测试过这个,但它似乎最接近我的想法(这类似于 Coalesce 的参数数量不固定)
    • @Code:据此:stackoverflow.com/questions/1342907/…,应该可以正常工作:)
    • 警告:这是一个 SQL Server 2005+ 查询。
    【解决方案4】:

    更好的选择是重组数据以支持基于列的 min/max/avg,因为这是 SQL 最擅长的。

    在 SQL Server 2005 中,您可以使用 UNPIVOT 运算符来执行转换。

    并不总是适用于所有问题,但如果您可以使用它,可以让事情变得更容易。

    见:
    http://msdn.microsoft.com/en-us/library/ms177410.aspx http://blogs.msdn.com/craigfr/archive/2007/07/17/the-unpivot-operator.aspx

    【讨论】:

      【解决方案5】:

      如果你只需要在一行上做,你怎么做都没有关系(一切都足够快)。

      对于每行选择几列的 Min/Max/Avg 值,使用 UNPIVOT 的解决方案应该比 UDF 快得多

      【讨论】:

        【解决方案6】:

        另一种可能性是创建自定义表类型,如下所示:

        CREATE TYPE [Maps].[TblListInt] AS TABLE(   [ID] [INT] NOT NULL )
        

        那么,

        CREATE FUNCTION dbo.GetMax(@ids maps.TblListInt READONLY) RETURNS INT
        BEGIN
        RETURN (select max(id) from @ids)
        END
        

        当然,您可以将“int”替换为您需要的类型。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-11-14
          • 2015-09-26
          • 1970-01-01
          • 2018-03-04
          • 2014-03-13
          • 2023-03-19
          相关资源
          最近更新 更多