82 votes

Sélectionner une colonne dans SQL sans la grouper

J'ai essayé de trouver des informations sur la façon de sélectionner une colonne non agrégée qui n'est pas contenue dans l'instruction Group By en SQL, mais rien de ce que j'ai trouvé jusqu'à présent ne semble répondre à ma question. J'ai une table avec trois colonnes que je veux extraire. L'une est une date de création, l'autre est un ID qui regroupe les enregistrements par un ID de réclamation particulier, et la dernière est le PK. Je veux trouver l'enregistrement qui a la date de création maximale dans chaque groupe d'ID de réclamation. Je sélectionne le MAX(date de création) et l'ID du sinistre (cpe.fmgcms_cpeclaimid), et je regroupe par l'ID du sinistre. Mais j'ai besoin du PK de ces enregistrements (cpe.fmgcms_claimid), et si j'essaie de l'ajouter à ma clause de sélection, j'obtiens une erreur. Et je ne peux pas l'ajouter à ma clause group by parce qu'alors, cela annulerait le regroupement que j'avais prévu. Est-ce que quelqu'un connaît une solution à ce problème ? Voici un exemple de mon code :

Select MAX(cpe.createdon) As MaxDate, cpe.fmgcms_cpeclaimid 
from Filteredfmgcms_claimpaymentestimate cpe
where cpe.createdon < 'reportstartdate'
group by cpe.fmgcms_cpeclaimid

Voici le résultat que j'aimerais obtenir :

Select MAX(cpe.createdon) As MaxDate, cpe.fmgcms_cpeclaimid, cpe.fmgcms_claimid 
from Filteredfmgcms_claimpaymentestimate cpe
where cpe.createdon < 'reportstartdate'
group by cpe.fmgcms_cpeclaimid

57voto

Nicholas Carey Points 24614

Les colonnes de l'ensemble des résultats d'un select requête avec group by doit l'être :

  • une expression utilisée comme l'un des éléments du group by ou ...
  • une fonction agrégée, ou ...
  • une valeur littérale

Il n'est donc pas possible de faire ce que l'on veut en une seule et simple requête. La première chose à faire est d'énoncer votre problème de manière claire, quelque chose comme :

Je veux trouver le date de création la plus récente dans chaque groupe de ma table des sinistres

Données

create table dbo.some_claims_table
(
  claim_id     int      not null ,
  group_id     int      not null ,
  date_created datetime not null ,

  constraint some_table_PK primary key ( claim_id                ) ,
  constraint some_table_AK01 unique    ( group_id , claim_id     ) ,
  constraint some_Table_AK02 unique    ( group_id , date_created ) ,

)

La première chose à faire est d'identifier la date de création la plus récente pour chaque groupe :

select group_id ,
       date_created = max( date_created )
from dbo.claims_table
group by group_id

Cela vous donne les critères de sélection dont vous avez besoin (1 ligne par groupe, avec 2 colonnes : group_id et la date de création) pour remplir la première partie de l'exigence (sélectionner la ligne individuelle de chaque groupe). Il doit s'agir d'une table virtuelle dans votre version finale. select demande :

select *
from dbo.claims_table t
join ( select group_id ,
       date_created = max( date_created )
       from dbo.claims_table
       group by group_id
      ) x on x.group_id     = t.group_id
         and x.date_created = t.date_created

Si le tableau n'est pas unique par date_created à l'intérieur group_id (AK02), vous pouvez obtenir des lignes en double pour un groupe donné.

28voto

RedFilter Points 84190

Vous pouvez le faire avec PARTITION y RANK :

select * from
(
    select MyPK, fmgcms_cpeclaimid, createdon,  
        Rank() over (Partition BY fmgcms_cpeclaimid order by createdon DESC) as Rank
    from Filteredfmgcms_claimpaymentestimate 
    where createdon < 'reportstartdate' 
) tmp
where Rank = 1

10voto

MatBailie Points 37610

La réponse directe est que vous ne pouvez pas. Vous doit sélectionnez un agrégat ou un élément par lequel vous effectuez un regroupement.

Il vous faut donc une autre approche.

1). Reprenez votre requête actuelle et joignez-y les données de base.

SELECT
  cpe.*
FROM
  Filteredfmgcms_claimpaymentestimate cpe
INNER JOIN
  (yourQuery) AS lookup
    ON  lookup.MaxData           = cpe.createdOn
    AND lookup.fmgcms_cpeclaimid = cpe.fmgcms_cpeclaimid

2). Utilisez un CTE pour tout faire en une seule fois...

WITH
  sequenced_data AS
(
  SELECT
    *,
    ROW_NUMBER() OVER (PARITION BY fmgcms_cpeclaimid ORDER BY CreatedOn DESC) AS sequence_id
  FROM
    Filteredfmgcms_claimpaymentestimate
  WHERE
    createdon < 'reportstartdate'
)
SELECT
  *
FROM
  sequenced_data
WHERE
  sequence_id = 1

REMARQUE : L'utilisation de ROW_NUMBER() ne garantit qu'un seul enregistrement par fmgcms_cpeclaimid . Même si plusieurs enregistrements sont liés avec exactement la même createdon valeur. Si vous pouvez avoir des liens, et que vous voulez que tous les enregistrements aient la même createdon valeur, utilisation RANK() au lieu de cela.

5voto

bluefeet Points 105508

Vous pouvez join la table sur elle-même pour obtenir le PK :

Select cpe1.PK, cpe2.MaxDate, cpe1.fmgcms_cpeclaimid 
from Filteredfmgcms_claimpaymentestimate cpe1
INNER JOIN
(
    select MAX(createdon) As MaxDate, fmgcms_cpeclaimid 
    from Filteredfmgcms_claimpaymentestimate
    group by fmgcms_cpeclaimid
) cpe2
    on cpe1.fmgcms_cpeclaimid = cpe2.fmgcms_cpeclaimid
    and cpe1.createdon = cpe2.MaxDate
where cpe1.createdon < 'reportstartdate'

2voto

Osher Zakin Points 21

Ce que j'aime faire, c'est envelopper les colonnes d'addition dans une fonction d'agrégation, comme par exemple max() . Cela fonctionne très bien lorsque vous ne vous attendez pas à des valeurs dupliquées.

Select MAX(cpe.createdon) As MaxDate, cpe.fmgcms_cpeclaimid, MAX(cpe.fmgcms_claimid) As fmgcms_claimid 
from Filteredfmgcms_claimpaymentestimate cpe
where cpe.createdon < 'reportstartdate'
group by cpe.fmgcms_cpeclaimid

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X