web-dev-qa-db-fra.com

MAX () et MAX () OVER PARTITION BY génère l'erreur 3504 dans la requête Teradata

J'essaie de produire un tableau de résultats avec la dernière date de cours achevée pour chaque code de cours, ainsi que le dernier code de cours terminé pour l'ensemble des employés. Ci-dessous ma requête:

SELECT employee_number,
       MAX(course_completion_date) 
           OVER (PARTITION BY course_code) AS max_course_date,
       MAX(course_completion_date) AS max_date
FROM employee_course_completion
WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
GROUP BY employee_number

Cette requête génère l'erreur suivante:

3504 : Selected non-aggregate values must be part of the associated group

Si je supprime la ligne MAX () OVER (PARTITION BY ...), la requête s'exécute correctement. J'ai donc isolé le problème sur cette ligne, mais après une recherche dans ces forums et sur Internet, je ne peux pas voir ce que j'ai. je fais mal. Quelqu'un peut-il aider?

8
dneaster3

Comme Ponies le dit dans un commentaire, vous ne pouvez pas mélanger OLAP fonctions avec des fonctions d'agrégat.

Il est peut-être plus facile d'obtenir la dernière date d'achèvement pour chaque employé et de la lier à un jeu de données contenant la dernière date d'achèvement pour chacun des trois cours ciblés. 

C'est une idée non testée qui devrait, espérons-le, vous mettre dans la bonne voie:

  SELECT employee_number,
         course_code,
         MAX(course_completion_date) AS max_date,
         lcc.LAST_COURSE_COMPLETED
    FROM employee_course_completion ecc
         LEFT JOIN (
             SELECT employee_number,
                    MAX(course_completion_date) AS LAST_COURSE_COMPLETED
               FROM employee_course_completion
              WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
         ) lcc
         ON lcc.employee_number = ecc.employee_number
   WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
GROUP BY employee_number, course_code, lcc.LAST_COURSE_COMPLETED
5
bernie

Je sais que c'est une très vieille question, mais quelqu'un d'autre m'a posé une question similaire.

Je n'ai pas TeraData, mais vous ne pouvez pas faire ce qui suit?

SELECT employee_number,
       course_code,
       MAX(course_completion_date)                                     AS max_course_date,
       MAX(course_completion_date) OVER (PARTITION BY employee_number) AS max_date
FROM employee_course_completion
WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
GROUP BY employee_number, course_code

Le GROUP BY garantit désormais une ligne par cours et par employé. Cela signifie que vous avez simplement besoin d'une MAX() hétéro pour obtenir le max_course_date.

Avant, votre GROUP BY donnait seulement une ligne par employé et la MAX() OVER() essayait de donner plusieurs résultats pour cette ligne (un par cours) .

Au lieu de cela, vous avez maintenant besoin de la clause OVER() pour obtenir la MAX() pour l’employé dans son ensemble. Ceci est maintenant légitime car chaque ligne individuelle ne reçoit qu'une seule réponse (car elle provient d'un super-ensemble, pas d'un sous-ensemble). De même, pour la même raison, la clause OVER() fait maintenant référence à une valeur scalaire valide, telle que définie par la clause GROUP BY; employee_number.


Peut-être un moyen bref de dire cela serait qu’une aggregate avec une clause OVER() doit être un super-ensemble du GROUP BY, pas un sous-ensemble.

Créez votre requête avec un GROUP BY au niveau qui représente les lignes souhaitées, puis spécifiez les clauses OVER() si vous souhaitez agréger à un niveau supérieur.

1
MatBailie

Logiquement, les fonctions OLAP sont calculées après GROUP BY/HAVING. Vous ne pouvez donc accéder qu'aux colonnes de GROUP BY ou aux colonnes dotées d'une fonction d'agrégat. Suivre semble étrange, mais c'est du SQL standard:

SELECT employee_number,
       MAX(MAX(course_completion_date)) 
           OVER (PARTITION BY course_code) AS max_course_date,
       MAX(course_completion_date) AS max_date
FROM employee_course_completion
WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
GROUP BY employee_number, course_code

Et comme Teradata permet de réutiliser un alias, cela fonctionne aussi:

SELECT employee_number,
       MAX(max_date) 
           OVER (PARTITION BY course_code) AS max_course_date,
       MAX(course_completion_date) AS max_date
FROM employee_course_completion
WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
GROUP BY employee_number, course_code
1
dnoeth

Je pense que cela fonctionnera même si c'était il y a toujours.

SELECT employee_number, Row_Number()  
   OVER (PARTITION BY course_code ORDER BY course_completion_date DESC ) as rownum
FROM employee_course_completion
WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
   AND rownum = 1

Si vous souhaitez obtenir le dernier identifiant si la date est la même, vous pouvez l'utiliser en supposant que votre clé primaire est un identifiant.

SELECT employee_number, Row_Number()  
   OVER (PARTITION BY course_code ORDER BY course_completion_date DESC, Id Desc) as rownum    FROM employee_course_completion
WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
   AND rownum = 1
0
jwize