2009-09-08 12 views
0

Ok, tout d'abord, j'ai vu this thread. Mais aucune des solutions n'est très satisfaisante. La réponse nommée ressemble à NULLs la casser, et la réponse la mieux cotée semble désagréable à maintenir. Je me demandais quelque chose comme ce qui suit:Meilleure façon de calculer Max/Min de N colonnes dans SQL Server

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 

Les principaux problèmes que je vois sont que s'il n'y a que trois champs à comparer, vous auriez encore de spécifier NULL pour les 2 autres, et si vous vouliez l'étendre à six comparaisons viendrait à briser l'usage existant. S'il s'agissait d'une procédure stockée paramétrée, vous pouviez spécifier un paramètre par défaut pour chaque paramètre, et l'ajout de nouveaux paramètres ne romprait pas les références existantes. La même méthode pourrait également être étendue à d'autres types de données ou à des choses comme Min ou Avg. Y at-il un inconvénient majeur à cela que je ne repère pas? Notez que cette fonction fonctionne si certaines, toutes ou aucune des valeurs qui lui sont passées sont des valeurs nulles ou des doublons.

+0

Une downvote soudaine et inexpliquée 2,5 ans plus tard? – MartW

Répondre

0

je passerais les dates en XML (vous pouvez utiliser varchar/etc, et convertir le type de données xml aussi):

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 

qui répondrait aux question de gérer autant de possibilités, et je ne serais pas la peine de mettre des nulls dans le xml.

J'utiliserais un paramètre distinct pour spécifier le type de datet plutôt que de personnaliser le code de support xml & à chaque fois, mais vous pourriez avoir besoin d'utiliser du SQL dynamique pour que cela fonctionne.

+0

Je n'ai pas testé cela, mais il semble le plus proche de ce que j'avais en tête (ce qui s'apparentait à la façon dont Coalesce possède un nombre de paramètres non fixé) – MartW

+0

@Code: D'après ceci: http://stackoverflow.com/ questions/1342907/open-xml-insert-dans-temp-table-sql-2005/1343096 # 1343096, devrait fonctionner correctement :) –

+0

Avertissement: Ceci est une requête SQL Server 2005+. –

2

Vous pouvez résoudre la question null avec la fonction ISNULL:

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 

Mais il ne fonctionne qu'avec les fonctions MAX.

Voici une autre suggestion : http://www.sommarskog.se/arrays-in-sql-2005.html

Ils suggèrent valeurs séparées par des virgules dans une forme de chaîne.

La fonction prend autant de paramètres que vous le souhaitez et ressemble à ceci:

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 

Et Vocation:

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

En fait, je pense que ma fonction n'est pas dérangée par les valeurs nulles. – MartW

1

Pourquoi ne pas simplement:

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   

Ce shoud prendre soin du problème nul sans introduire de nouvelle valeur s

0

Une meilleure option consiste à restructurer les données pour prendre en charge les colonnes min/max/moy, car c'est ce que SQL est le meilleur.

Dans SQL Server 2005, vous pouvez utiliser l'opérateur UNPIVOT pour effectuer la transformation.

Pas toujours approprié pour tous les problèmes, mais peut rendre les choses plus faciles si vous pouvez l'utiliser.

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

0

Si vous devez le faire sur une seule ligne, peu importe comment vous le ferez (tout serait assez rapide).

Pour sélectionner Min/Max/valeur moyenne de plusieurs colonnes par ligne, solution avec UNPIVOT devrait être beaucoup plus rapide que UDF

0

une autre possibilité est de créer un type de tableau personnalisé, comme celui-ci:

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

puis,

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

Bien sûr, vous pouvez échanger "int" avec votre type requis.

Questions connexes