web-dev-qa-db-fra.com

SQL / mysql - Sélectionnez distinct / UNIQUE mais renvoie toutes les colonnes?

SELECT DISTINCT field1, field2, field3, ......   FROM table

J'essaie d'accomplir l'instruction SQL suivante, mais je souhaite qu'elle renvoie toutes les colonnes. Est-ce possible? Quelque chose comme:

SELECT DISTINCT field1, * from table
337
aryaxt

Vous recherchez un groupe par:

select *
from table
group by field1

Ce qui peut parfois être écrit avec une déclaration distincte:

select distinct on field1 *
from table

Sur la plupart des plates-formes, cependant, aucune des solutions ci-dessus ne fonctionnera, car le comportement des autres colonnes n'est pas spécifié. (Le premier fonctionne sous MySQL, si c'est ce que vous utilisez.)

Vous pouvez aller chercher les champs distincts et vous en tenir à choisir une seule ligne arbitraire à chaque fois.

Sur certaines plates-formes (telles que PostgreSQL, Oracle, T-SQL), cela peut être fait directement à l'aide de fonctions de fenêtre:

select *
from (
   select *,
          row_number() over (partition by field1 order by field2) as row_number
   from table
   ) as rows
where row_number = 1

Sur d’autres (MySQL, SQLite), vous devrez écrire des sous-requêtes qui vous obligeront à joindre la table entière à elle-même ( exemple ), ce qui n’est pas recommandé.

373

D'après le libellé de votre question, je comprends que vous souhaitez sélectionner les valeurs distinctes pour un champ donné et que pour chacune d'elles, toutes les autres valeurs de colonne de la même ligne soient répertoriées. La plupart des SGBD ne le permettent pas avec ni DISTINCT ni GROUP BY, car le résultat n'est pas déterminé.

Pensez-y comme ceci: si votre field1 apparaît plusieurs fois, quelle valeur de field2 sera listée (étant donné que vous avez la même valeur pour field1 sur deux lignes mais deux valeurs distinctes de field2 dans ces deux lignes).

Vous pouvez cependant utiliser des fonctions d'agrégat (explicitement pour chaque champ à afficher) et utiliser un GROUP BY au lieu de DISTINCT:

SELECT field1, MAX(field2), COUNT(field3), SUM(field4), .... FROM table GROUP BY field1
55
Costi Ciudatu

Si j'ai bien compris votre problème, c'est semblable à celui que je viens d'avoir. Vous voulez pouvoir limiter l'utilisabilité de DISTINCT à un champ spécifié, plutôt que de l'appliquer à toutes les données.

Si vous utilisez GROUP BY sans une fonction d'agrégat, quel que soit le champ, GROUP BY sera votre fichier DISTINCT.

Si vous faites votre requête:

SELECT * from table GROUP BY field1;

Il montrera tous vos résultats basés sur une seule instance de field1.

Par exemple, si vous avez une table avec nom, adresse et ville. Une seule personne a plusieurs adresses enregistrées, mais vous voulez juste une seule adresse pour la personne, vous pouvez interroger comme suit:

SELECT * FROM persons GROUP BY name;

Le résultat sera qu'une seule instance de ce nom apparaîtra avec son adresse et que l'autre sera omise de la table résultante. Attention: si vos champs ont des valeurs atomiques telles que firstName, lastName que vous souhaitez grouper par les deux.

SELECT * FROM persons GROUP BY lastName, firstName;

parce que si deux personnes ont le même nom de famille et que vous ne regroupez que par nom, l'une de ces personnes sera omise des résultats. Vous devez garder ces choses en considération. J'espère que cela t'aides.

20
rocklandcitizen
SELECT  c2.field1 ,
        field2
FROM    (SELECT DISTINCT
                field1
         FROM   dbo.TABLE AS C
        ) AS c1
        JOIN dbo.TABLE AS c2 ON c1.field1 = c2.field1
13
Stormy

C'est une très bonne question. J'ai déjà lu quelques réponses utiles, mais je peux probablement ajouter une explication plus précise.

Réduire le nombre de résultats de la requête avec une instruction GROUP BY est simple si vous ne demandez pas d'informations supplémentaires. Supposons que vous ayez le tableau suivant '' emplacements ''.

--country-- --city--
 France      Lyon
 Poland      Krakow
 France      Paris
 France      Marseille
 Italy       Milano

Maintenant la requête

SELECT country FROM locations
GROUP BY country

aura pour résultat:

--country--
 France
 Poland
 Italy

Cependant, la requête suivante

SELECT country, city FROM locations
GROUP BY country

... génère une erreur dans MS SQL, car comment votre ordinateur peut-il savoir laquelle des trois villes françaises "Lyon", "Paris" ou "Marseille" vous souhaitez lire dans le champ situé à droite de "France"?

Afin de corriger la deuxième requête, vous devez ajouter cette information. Une façon de faire est d'utiliser les fonctions MAX () ou MIN (), en sélectionnant la valeur la plus grande ou la plus petite parmi tous les candidats. MAX () et MIN () s'appliquent non seulement aux valeurs numériques, mais comparent également l'ordre alphabétique des valeurs de chaîne.

SELECT country, MAX(city) FROM locations
GROUP BY country

aura pour résultat:

--country-- --city--
 France      Paris
 Poland      Krakow
 Italy       Milano

ou:

SELECT country, MIN(city) FROM locations
GROUP BY country

aura pour résultat:

--country-- --city--
 France      Lyon
 Poland      Krakow
 Italy       Milano

Ces fonctions sont une bonne solution tant que vous pouvez sélectionner votre valeur à l’une ou l’autre des extrémités de l’ordre alphabétique (ou numérique). Mais si ce n'est pas le cas? Supposons qu'il vous faut une valeur avec une certaine caractéristique, par exemple. en commençant par la lettre 'M'. Maintenant, les choses se compliquent.

La seule solution que j'ai pu trouver jusqu'à présent est de placer votre requête entière dans une sous-requête et de construire la colonne supplémentaire à l'extérieur de celle-ci:

SELECT
     countrylist.*,
     (SELECT TOP 1 city
     FROM locations
     WHERE
          country = countrylist.country
          AND city like 'M%'
     )
FROM
(SELECT country FROM locations
GROUP BY country) countrylist

aura pour résultat:

--country-- --city--
 France      Marseille
 Poland      NULL
 Italy       Milano
7
Ulf Sanne

Excellente question @aryaxt - vous pouvez dire que c’était une excellente question parce que vous l’aviez posée il ya 5 ans et que je suis tombé sur elle aujourd’hui en essayant de trouver la réponse!

Je viens d'essayer de modifier la réponse acceptée pour inclure ceci, mais au cas où ma modification ne le ferait pas:

Si votre table n'était pas si grande, et en supposant que votre clé primaire était un entier auto-incrémenté, vous pourriez faire quelque chose comme ça:

SELECT 
  table.*
FROM table
--be able to take out dupes later
LEFT JOIN (
  SELECT field, MAX(id) as id
  FROM table
  GROUP BY field
) as noDupes on noDupes.id = table.id
WHERE
  //this will result in only the last instance being seen
  noDupes.id is not NULL
3
Garrett Simpson

Vous pouvez le faire avec une clause WITH.

Par exemple:

WITH c AS (SELECT DISTINCT a, b, c FROM tableName)
SELECT * FROM tableName r, c WHERE c.rowid=r.rowid AND c.a=r.a AND c.b=r.b AND c.c=r.c

Cela vous permet également de sélectionner uniquement les lignes sélectionnées dans la requête de clauses WITH.

2
user2225399

Pour SQL Server, vous pouvez utiliser les fonctions dense_rank et Windows supplémentaires pour obtenir toutes les lignes ET les colonnes avec des valeurs dupliquées sur des colonnes spécifiées. Voici un exemple...

with t as (
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r1' union all
    select col1 = 'c', col2 = 'b', col3 = 'a', other = 'r2' union all
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r3' union all
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r4' union all
    select col1 = 'c', col2 = 'b', col3 = 'a', other = 'r5' union all
    select col1 = 'a', col2 = 'a', col3 = 'a', other = 'r6'
), tdr as (
    select 
        *, 
        total_dr_rows = count(*) over(partition by dr)
    from (
        select 
            *, 
            dr = dense_rank() over(order by col1, col2, col3),
            dr_rn = row_number() over(partition by col1, col2, col3 order by other)
        from 
            t
    ) x
)

select * from tdr where total_dr_rows > 1

Cela prend un nombre de lignes pour chaque combinaison distincte de col1, col2 et col3.

1
dotjoe

Essayer

SELECT table.* FROM table 
WHERE otherField = 'otherValue'
GROUP BY table.fieldWantedToBeDistinct
limit x
1
Pedro Ramos
SELECT *
FROM tblname
GROUP BY duplicate_values
ORDER BY ex.VISITED_ON DESC
LIMIT 0 , 30

dans ORDER BY je viens de mettre exemple ici, vous pouvez également ajouter un champ ID dans cette

0
SagarPPanchal