SQL / mysql: ¿selecciona distinto / ÚNICO pero devuelve todas las columnas?

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

Estoy tratando de lograr la siguiente instrucción sql pero quiero que devuelva todas las columnas, ¿es esto posible? Algo como:

SELECT DISTINCT field1, * from table
aryaxt
fuente
12
¿Por qué SELECT DISTINCT * FROM tableno funciona para ti?
ypercubeᵀᴹ
19
Si su tabla tiene un PK, todas las filas deben ser distinctpor definición. Si está tratando de seleccionar, DISTINCT field1pero de alguna manera devolver todas las demás columnas, ¿qué debería suceder para aquellas columnas que tienen más de un valor para un field1valor en particular ? Tendría que usar GROUP BYy algún tipo de agregación en las otras columnas, por ejemplo.
Martin Smith
1
Si desea filas repetidas y no solo filas distintas, elimine la palabra clave distinta.
Hyperboreus
2
¿Podría dar un ejemplo de cómo espera que se vean los resultados? Hasta ahora, no puedo entender su consulta deseada.
recursivo
3
Aquí está la respuesta de una pregunta similar, primero debe obtener la columna distinta con sus identificadores y luego unirla con la tabla original. SELECCIONE DISTINCT en una columna, devuelva varias otras columnas
yadavr

Respuestas:

407

Estás buscando un grupo por:

select *
from table
group by field1

Que ocasionalmente se puede escribir con una declaración distinta en:

select distinct on field1 *
from table

Sin embargo, en la mayoría de las plataformas, ninguno de los anteriores funcionará porque el comportamiento en las otras columnas no está especificado. (El primero funciona en MySQL, si eso es lo que está usando).

Puede buscar los distintos campos y seguir eligiendo una sola fila arbitraria cada vez.

En algunas plataformas (por ejemplo, PostgreSQL, Oracle, T-SQL), esto se puede hacer directamente usando funciones de ventana:

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

En otros (MySQL, SQLite), necesitará escribir subconsultas que lo harán unir toda la tabla consigo mismo ( ejemplo ), por lo que no es recomendable.

Denis de Bernardy
fuente
10
La consulta no se analizará para mí y da un error: The ranking function "row_number" must have an ORDER BY clause. Necesitamos agregar orden por cláusula después de la partición por campo1. Entonces la consulta correcta será select * from ( select *, row_number() over (partition by field1 order by orderbyFieldName) as row_number from table ) as rows where row_number = 1
Ankur-m
1
¡Gracias! Estaba en el mismo problema y la solución fue el GROUP BY
Joaquin Iurchuk
2
También en Oracle (Oracle SQL Developer) no puede especificar select *, row_number() over (partition by field1 order by field2) as row_number from table. select **table**.*, row_number() over (partition by field1 order by field2) as row_number from table
Debe
1
@jarlh: Podría ser ... hoy. Como puede observar, esta respuesta tiene casi 7 años, un momento en el que ese no era el caso en la medida en que puedo recordar desde que estaba activo. Puede volver a etiquetar y / o editar la respuesta si lo considera necesario.
Denis de Bernardy
2
select distinct on (field1) * from table; funciona también en PostgreSQL
Chilianu Bogdan
61

De la redacción de su pregunta, entiendo que desea seleccionar los valores distintos para un campo determinado y que cada valor tenga todos los demás valores de columna en la misma fila. La mayoría de los DBMS no permitirán esto con ni DISTINCTtampoco GROUP BY, porque el resultado no está determinado.

Piénselo de esta manera: si field1ocurre más de una vez, qué valor de field2aparecerá en la lista (dado que tiene el mismo valor field1en dos filas pero dos valores distintos de field2en esas dos filas).

Sin embargo, puede usar funciones agregadas (explícitamente para cada campo que desea que se muestren) y usar un en GROUP BYlugar de DISTINCT:

SELECT field1, MAX(field2), COUNT(field3), SUM(field4), .... FROM table GROUP BY field1
Costi Ciudatu
fuente
44
+1 para esta solución. Así que podemos hacer SELECT field1, MIN(field2), MIN(field3), MIN(field4), .... FROM table GROUP BY field1, y los campos 2, 3, 4, no están obligados a ser enteros (u otros dígitos), también pueden ser campos de caracteres
acechar el
Estaba funcionando bien hasta que me quedé atrapado en una columna booleana. Los valores de columna MIN (Dinámico) se modifican a falso incluso si era verdadero. Cualquier otra función agregada disponible para abordar boolean - signonsridhar hace 6 minutos. Suma (dinámica) cambió falso a 1
signonsridhar
1
Gran sugerencia, me llevó a mi solución, que creo que es más universal: ¡eche un vistazo!
Garrett Simpson
@signonsridhar convierte tu booleano en int y usa sum; egsum(cast(COL as int)) > 0
Drew
26

Si entendí tu problema correctamente, es similar a uno que acabo de tener. Desea poder limitar la usabilidad de DISTINCT a un campo específico, en lugar de aplicarlo a todos los datos.

Si usa GROUP BY sin una función agregada, cualquier campo que GROUP BY será su DISTINCT archivado.

Si haces tu consulta:

SELECT * from table GROUP BY field1;

Mostrará todos sus resultados basados ​​en una sola instancia de field1.

Por ejemplo, si tiene una tabla con nombre, dirección y ciudad. Una sola persona tiene múltiples direcciones registradas, pero solo desea una sola dirección para la persona, puede consultar de la siguiente manera:

SELECT * FROM persons GROUP BY name;

El resultado será que solo una instancia de ese nombre aparecerá con su dirección, y la otra se omitirá de la tabla resultante. Precaución: si sus fileds tienen valores atómicos como firstName, lastName que desea agrupar por ambos.

SELECT * FROM persons GROUP BY lastName, firstName;

porque si dos personas tienen el mismo apellido y usted solo agrupa por apellido, una de esas personas será omitida de los resultados. Necesitas tener esas cosas en consideración. Espero que esto ayude.

rocklandcitizen
fuente
Como se menciona en la respuesta aceptada, funcionaría para la mayoría de las encarnaciones de SQL, solo para MYSQL
Garrett Simpson, el
15
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
Tormentoso
fuente
¿Por qué hay C aliascuando puede funcionar sin él? en líneaFROM dbo.TABLE AS C
Talha
2
Creo que esto se debe a mi uso de RedGate SQLPrompt. Tal como lo configuré, siempre agrega alias, incluso si es innecesario. Está ahí "por si acaso"
Tormentoso
Esto me pareció prometedor, pero aun así recuperó todas las filas, no el campo distinto1. :(
Michael Fever
13

Esa es una muy buena pregunta. Ya he leído algunas respuestas útiles aquí, pero probablemente pueda agregar una explicación más precisa.

Reducir el número de resultados de la consulta con una instrucción GROUP BY es fácil siempre que no consulte información adicional. Supongamos que tiene la siguiente tabla 'ubicaciones'.

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

Ahora la consulta

SELECT country FROM locations
GROUP BY country

resultará en:

--country--
 France
 Poland
 Italy

Sin embargo, la siguiente consulta

SELECT country, city FROM locations
GROUP BY country

... arroja un error en MS SQL, porque ¿cómo podría saber su computadora cuál de las tres ciudades francesas "Lyon", "París" o "Marsella" desea leer en el campo a la derecha de "Francia"?

Para corregir la segunda consulta, debe agregar esta información. Una forma de hacerlo es utilizar las funciones MAX () o MIN (), seleccionando el valor más grande o más pequeño entre todos los candidatos. MAX () y MIN () no solo son aplicables a valores numéricos, sino que también comparan el orden alfabético de los valores de cadena.

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

resultará en:

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

o:

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

resultará en:

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

Estas funciones son una buena solución siempre que esté bien con la selección de su valor de los extremos del orden alfabético (o numérico). Pero, ¿y si este no es el caso? Supongamos que necesita un valor con cierta característica, por ejemplo, comenzando con la letra 'M'. Ahora las cosas se complican.

La única solución que pude encontrar hasta ahora es poner toda su consulta en una subconsulta y construir la columna adicional fuera de ella a mano:

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

resultará en:

--country-- --city--
 France      Marseille
 Poland      NULL
 Italy       Milano
Ulf Sanne
fuente
5

Gran pregunta @aryaxt: ¡puede decir que fue una gran pregunta porque la hizo hace 5 años y hoy me topé con ella tratando de encontrar la respuesta!

Solo intenté editar la respuesta aceptada para incluir esto, pero en caso de que mi edición no se haga en:

Si su tabla no era tan grande, y suponiendo que su clave principal fuera un entero de incremento automático, podría hacer algo como esto:

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
Garrett Simpson
fuente
5

Tratar

SELECT table.* FROM table 
WHERE otherField = 'otherValue'
GROUP BY table.fieldWantedToBeDistinct
limit x
Pedro Ramos
fuente
3

Puedes hacerlo con una WITHcláusula.

Por ejemplo:

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

Esto también le permite seleccionar solo las filas seleccionadas en la WITHconsulta de cláusulas.

usuario2225399
fuente
2

Para SQL Server, puede usar dense_rank y las funciones adicionales de ventanas para obtener todas las filas Y columnas con valores duplicados en columnas específicas. Aquí hay un ejemplo...

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

Esto está tomando un recuento de filas para cada combinación distinta de col1, col2 y col3.

dotjoe
fuente
demasiado complicado y específico para una implementación de SQL
Garrett Simpson
1
select min(table.id), table.column1
from table 
group by table.column1
KadoJ
fuente
¡Esto funcionó para mí! Vale la pena señalar que, si está utilizando fetch_array (), deberá llamar a cada fila a través de una etiqueta de índice en lugar de llamar implícitamente el nombre de la fila. No hay suficientes caracteres en esto para que yo escriba el ejemplo que tengo: ¡X lo siento!
Brandon Printiss
0
SELECT *
FROM tblname
GROUP BY duplicate_values
ORDER BY ex.VISITED_ON DESC
LIMIT 0 , 30

en ORDER BYacabo de poner un ejemplo aquí, también puede agregar el campo ID en este

SagarPPanchal
fuente
Como se menciona en la respuesta aceptada, funcionaría para la mayoría de las encarnaciones de SQL, solo para MYSQL
Garrett Simpson, el
0

Encontré esto en otro lugar aquí, pero esta es una solución simple que funciona:

 WITH cte AS /* Declaring a new table named 'cte' to be a clone of your table */
 (SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY val1 DESC) AS rn
 FROM MyTable /* Selecting only unique values based on the "id" field */
 )
 SELECT * /* Here you can specify several columns to retrieve */
 FROM cte
 WHERE rn = 1
Michael Fever
fuente
Funciona para MSSQL
Michael Fever
-1

Agregue GROUP BY al campo en el que desea verificar si hay duplicados en su consulta.

SELECT field1, field2, field3, ......   FROM table GROUP BY field1

el campo 1 se verificará para excluir registros duplicados

o puede consultar como

SELECT *  FROM table GROUP BY field1

los registros duplicados del campo 1 se excluyen de SELECCIONAR

iCodeCrew
fuente
1
La cláusula GROUP BY debe coincidir con los campos seleccionados. de lo contrario arrojará un error comofiled2 must appear in the GROUP BY clause or be used in an aggregate function
Viuu -a
-2

Simplemente incluya todos sus campos en la cláusula GROUP BY.

wayneh
fuente
3
Para que esta sea una buena respuesta, debe incluir un poco más de detalles sobre lo que quiere decir.
Robbert
-2

Se puede hacer por consulta interna

$query = "SELECT * 
            FROM (SELECT field
                FROM table
                ORDER BY id DESC) as rows               
            GROUP BY field";
Zaheer Babar
fuente
2
Esto no responde a la pregunta, el OP estaba tratando de obtener todos los datos de la tabla, pero elimina las filas que contienen duplicados de un solo campo
Garrett Simpson
-3
SELECT * from table where field in (SELECT distinct field from table)
Andrés
fuente
77
Eso no hará el trabajo. Ha seleccionado la columna distinta en la subconsulta, pero la cláusula where obtiene todas esas columnas con ese valor. Por lo tanto, la consulta es tan buena como escribir 'select * from table' a menos que la columna 'field' sea una columna única, en cuyo caso no se requiere el distintivo en esa columna.
Ankur-m
-3

SELECT DISTINCT FIELD1, FIELD2, FIELD3 FROM TABLE1 funciona si los valores de las tres columnas son únicos en la tabla.

Si, por ejemplo, tiene varios valores idénticos para el nombre, pero el apellido y otra información en las columnas seleccionadas es diferente, el registro se incluirá en el conjunto de resultados.

Doris Gammenthaler
fuente
2
Esto no responde a la pregunta, el OP estaba tratando de obtener todos los datos de la tabla, pero elimina las filas que contienen duplicados de un solo campo
Garrett Simpson
-3

Sugeriría usar

SELECT  * from table where field1 in 
(
  select distinct field1 from table
)

de esta manera, si tiene el mismo valor en el campo 1 en varias filas, se devolverán todos los registros.

Ioannis K
fuente
1
No es diferente con SELECT * FROM table;. Aún más es lento.
Shin Kim el
Por favor, intente su respuesta primero.
Sherif