SQL / mysql - Chọn phân biệt / ĐỘC ĐÁO nhưng trả về tất cả các cột?


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

Tôi đang cố gắng thực hiện câu lệnh sql sau đây nhưng tôi muốn nó trả về tất cả các cột là điều này có thể không? Cái gì đó như:

SELECT DISTINCT field1, * from table

12
Tại sao SELECT DISTINCT * FROM tablekhông làm việc cho bạn?
ypercubeᵀᴹ

19
Nếu bảng của bạn có PK, tất cả các hàng sẽ distincttheo định nghĩa. Nếu bạn đang cố gắng chỉ chọn DISTINCT field1nhưng bằng cách nào đó trả về tất cả các cột khác thì điều gì sẽ xảy ra đối với các cột có nhiều hơn một giá trị cho một field1giá trị cụ thể ? Bạn sẽ cần phải sử dụng GROUP BYvà một số loại tổng hợp trên các cột khác chẳng hạn.
Martin Smith

1
Nếu bạn muốn các hàng lặp lại và không chỉ các hàng riêng biệt, hãy xóa từ khóa riêng biệt.
Hyperboreus

2
Bạn có thể đưa ra một ví dụ về những gì bạn mong đợi kết quả trông như thế nào? Cho đến nay, tôi không thể hiểu bất kỳ ý nghĩa nào về truy vấn bạn muốn.
đệ quy

3
Dưới đây là câu trả lời của câu hỏi tương tự, trước tiên bạn cần lấy cột riêng biệt với id của họ và sau đó nối nó với bảng gốc. CHỌN DISTINCT trên một cột, trả lại nhiều cột khác
yadavr

Câu trả lời:


407

Bạn đang tìm kiếm một nhóm bằng cách:

select *
from table
group by field1

Mà đôi khi có thể được viết với một tuyên bố khác biệt:

select distinct on field1 *
from table

Tuy nhiên, trên hầu hết các nền tảng, cả hai nền tảng trên đều không hoạt động vì hành vi trên các cột khác không được chỉ định. (Công cụ đầu tiên hoạt động trong MySQL, nếu đó là những gì bạn đang sử dụng.)

Bạn có thể tìm nạp các trường riêng biệt và chọn một hàng tùy ý mỗi lần.

Trên một số nền tảng (ví dụ PostgreSQL, Oracle, T-SQL), điều này có thể được thực hiện trực tiếp bằng các hàm cửa sổ:

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

Trên những người khác (MySQL, SQLite), bạn sẽ cần phải viết các truy vấn con sẽ khiến bạn tham gia toàn bộ bảng với chính nó ( ví dụ ), vì vậy không nên.


10
Truy vấn sẽ không phân tích cú pháp cho tôi và báo lỗi : The ranking function "row_number" must have an ORDER BY clause. Chúng ta cần thêm thứ tự theo mệnh đề sau khi phân vùng theo trường1. Vì vậy, truy vấn chính xác sẽ là 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
Cảm ơn! Tôi đã ở trong cùng một vấn đề và giải pháp là GROUP BY
Joaquin Iurchuk

2
Ngoài ra trong Oracle (Oracle SQL Developer) bạn không thể chỉ định select *, row_number() over (partition by field1 order by field2) as row_number from table. Bạn phải sử dụng rõ ràng tên bảng / bí danh trong truy vấn chọnselect **table**.*, row_number() over (partition by field1 order by field2) as row_number from table
meta4

1
@jarlh: Có thể là ... hôm nay. Như bạn có thể nhận thấy, câu trả lời này đã gần 7 tuổi, một thời điểm không phải là trường hợp như tôi có thể hồi tưởng lại từ khi tôi còn hoạt động. Bạn có thể thử lại và / hoặc chỉnh sửa câu trả lời nếu bạn cảm thấy cần thiết.
Denis de Bernardy

2
select distinct on (field1) * from table; cũng hoạt động trong PostgreSQL
Chilianu Bogdan

61

Từ cụm từ của câu hỏi của bạn, tôi hiểu rằng bạn muốn chọn các giá trị riêng biệt cho một trường nhất định và cho mỗi giá trị như vậy để có tất cả các giá trị cột khác trong cùng một hàng được liệt kê. Hầu hết các DBMS sẽ không cho phép điều này DISTINCTcũng như không GROUP BY, vì kết quả không được xác định.

Hãy nghĩ về nó như thế này: nếu bạn field1xuất hiện nhiều hơn một lần, giá trị nào field2sẽ được liệt kê (với điều kiện bạn có cùng giá trị field1trong hai hàng nhưng hai giá trị khác biệt field2trong hai hàng đó).

Tuy nhiên, bạn có thể sử dụng các hàm tổng hợp (rõ ràng cho mọi trường mà bạn muốn được hiển thị) và sử dụng GROUP BYthay vì DISTINCT:

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

4
+1 cho giải pháp này. Vì vậy, chúng ta có thể làm SELECT field1, MIN(field2), MIN(field3), MIN(field4), .... FROM table GROUP BY field1và trường2, 3, 4 ,,, không bắt buộc phải là số nguyên (hoặc các chữ số khác), chúng cũng có thể là các trường char
dõi

Đã làm việc tốt cho đến khi tôi bị mắc kẹt tại một cột boolean. Các giá trị cột MIN (Động) được sửa đổi thành sai ngay cả khi nó là đúng .. Bất kỳ hàm tổng hợp nào khác có sẵn để xử lý boolean - Signonsridhar 6 phút trước. Tổng (động) thay đổi sai thành 1
Signonsridhar

1
Đề nghị tuyệt vời, dẫn tôi đến giải pháp của tôi mà tôi nghĩ là phổ quát hơn - hãy xem!
Garrett Simpson

@signonsridhar bỏ boolean của bạn thành int và sử dụng tổng; ví dụsum(cast(COL as int)) > 0
Drew

26

Nếu tôi hiểu vấn đề của bạn một cách chính xác, nó tương tự như vấn đề tôi vừa gặp phải. Bạn muốn có thể giới hạn khả năng sử dụng DISTINCT trong một trường được chỉ định, thay vì áp dụng nó cho tất cả dữ liệu.

Nếu bạn sử dụng GROUP BY mà không có chức năng tổng hợp, trường nào bạn NHÓM THEO sẽ là DISTINCT của bạn được nộp.

Nếu bạn thực hiện truy vấn của mình:

SELECT * from table GROUP BY field1;

Nó sẽ hiển thị tất cả các kết quả của bạn dựa trên một trường hợp duy nhất của trường1.

Ví dụ: nếu bạn có một bảng có tên, địa chỉ và thành phố. Một người có nhiều địa chỉ được ghi lại, nhưng bạn chỉ muốn một địa chỉ cho người đó, bạn có thể truy vấn như sau:

SELECT * FROM persons GROUP BY name;

Kết quả sẽ là chỉ một trường hợp của tên đó sẽ xuất hiện cùng với địa chỉ của nó và một trường hợp khác sẽ bị bỏ qua khỏi bảng kết quả. Thận trọng: nếu các tệp của bạn có các giá trị nguyên tử như FirstName, LastName bạn muốn nhóm theo cả hai.

SELECT * FROM persons GROUP BY lastName, firstName;

bởi vì nếu hai người có cùng họ và bạn chỉ nhóm theo họ, thì một trong những người đó sẽ bị loại khỏi kết quả. Bạn cần phải xem xét những điều đó. Hi vọng điêu nay co ich.


Như đã đề cập trong câu trả lời được chấp nhận, sẽ hoạt động cho hầu hết các phiên bản SQL - chỉ dành cho MYSQL
Garrett Simpson

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

Tại sao có C aliaskhi nó có thể làm việc mà không có nó? trong dòngFROM dbo.TABLE AS C
Talha

2
Tôi tin rằng điều này là do tôi sử dụng RedGate SQLPrompt. Cách tôi đã cấu hình, nó luôn thêm bí danh - ngay cả khi không cần thiết. Đó là "chỉ trong trường hợp"
Bão tố

Điều này có vẻ hứa hẹn với tôi nhưng nó vẫn mang lại tất cả các hàng, không phải là trường khác biệt1. :(
Michael Fever

13

Đó là một câu hỏi thực sự tốt. Tôi đã đọc một số câu trả lời hữu ích ở đây rồi, nhưng có lẽ tôi có thể thêm một lời giải thích chính xác hơn.

Việc giảm số lượng kết quả truy vấn bằng câu lệnh GROUP BY thật dễ dàng miễn là bạn không truy vấn thêm thông tin. Giả sử bạn có bảng 'vị trí' sau đây.

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

Bây giờ truy vấn

SELECT country FROM locations
GROUP BY country

sẽ cho kết quả:

--country--
 France
 Poland
 Italy

Tuy nhiên, truy vấn sau đây

SELECT country, city FROM locations
GROUP BY country

... ném lỗi trong MS SQL, vì làm thế nào máy tính của bạn có thể biết thành phố nào trong ba thành phố của Pháp "Lyon", "Paris" hoặc "Marseille" mà bạn muốn đọc trên cánh đồng bên phải "Pháp"?

Để sửa truy vấn thứ hai, bạn phải thêm thông tin này. Một cách để làm điều này là sử dụng các hàm MAX () hoặc MIN (), chọn giá trị lớn nhất hoặc nhỏ nhất trong số tất cả các ứng cử viên. MAX () và MIN () không chỉ áp dụng cho các giá trị số, mà còn so sánh thứ tự chữ cái của các giá trị chuỗi.

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

sẽ cho kết quả:

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

hoặc là:

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

sẽ cho kết quả:

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

Các hàm này là một giải pháp tốt miễn là bạn ổn với việc chọn giá trị của mình từ hai đầu của thứ tự chữ cái (hoặc số). Nhưng nếu đây không phải là trường hợp? Giả sử rằng bạn cần một giá trị với một đặc tính nhất định, ví dụ: bắt đầu bằng chữ 'M'. Bây giờ mọi thứ trở nên phức tạp.

Giải pháp duy nhất tôi có thể tìm thấy cho đến nay là đưa toàn bộ truy vấn của bạn vào truy vấn con và xây dựng cột bổ sung bên ngoài nó bằng tay:

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

sẽ cho kết quả:

--country-- --city--
 France      Marseille
 Poland      NULL
 Italy       Milano

5

Câu hỏi tuyệt vời @aryaxt - bạn có thể nói đó là một câu hỏi tuyệt vời bởi vì bạn đã hỏi nó 5 năm trước và tôi tình cờ thấy nó ngày hôm nay cố gắng tìm câu trả lời!

Tôi chỉ cố gắng chỉnh sửa câu trả lời được chấp nhận để bao gồm câu trả lời này, nhưng trong trường hợp bản chỉnh sửa của tôi không đưa ra câu trả lời:

Nếu bảng của bạn không lớn và giả sử khóa chính của bạn là số nguyên tăng tự động, bạn có thể làm một cái gì đó như thế này:

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

5

Thử

SELECT table.* FROM table 
WHERE otherField = 'otherValue'
GROUP BY table.fieldWantedToBeDistinct
limit x

3

Bạn có thể làm điều đó với một WITH điều khoản.

Ví dụ:

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

Điều này cũng cho phép bạn chỉ chọn các hàng được chọn trong WITHtruy vấn mệnh đề.


2

Đối với SQL Server, bạn có thể sử dụng các hàm dense_rank và các cửa sổ bổ sung để nhận tất cả các hàng VÀ cột có giá trị trùng lặp trên các cột được chỉ định. Đây là một ví dụ...

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

Đây là cách tính hàng cho mỗi kết hợp riêng biệt của col1, col2 và col3.


quá phức tạp và cụ thể đối với một triển khai SQL
Garrett Simpson

1
select min(table.id), table.column1
from table 
group by table.column1

Điều này làm việc cho tôi !! Điều đáng chú ý là, nếu bạn đang sử dụng fetch_array () thì bạn sẽ cần gọi mỗi hàng thông qua một nhãn chỉ mục thay vì gọi ngầm tên hàng. Không có đủ ký tự trong này để tôi viết ra ví dụ tôi có: X xin lỗi !!
Brandon Printiss

0
SELECT *
FROM tblname
GROUP BY duplicate_values
ORDER BY ex.VISITED_ON DESC
LIMIT 0 , 30

trong ORDER BYtôi vừa nêu ví dụ ở đây, bạn cũng có thể thêm trường ID vào đây


Như đã đề cập trong câu trả lời được chấp nhận, sẽ hoạt động đối với hầu hết các phiên bản SQL - chỉ dành cho MYSQL
Garrett Simpson

0

Tìm thấy điều này ở nơi khác ở đây nhưng đây là một giải pháp đơn giản hoạt động:

 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

Hoạt động cho MSSQL
Michael Fever

-1

Thêm NHÓM THEO vào trường bạn muốn kiểm tra trùng lặp truy vấn của bạn có thể trông giống như

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

trường1 sẽ được kiểm tra để loại trừ các bản ghi trùng lặp

hoặc bạn có thể truy vấn như

SELECT *  FROM table GROUP BY field1

các bản ghi trùng lặp của trường1 được loại trừ khỏi CHỌN


1
Mệnh đề GROUP BY phải khớp với các trường đã chọn. nếu không nó sẽ ném lỗi nhưfiled2 must appear in the GROUP BY clause or be used in an aggregate function
Viuu -a

-2

Chỉ cần bao gồm tất cả các lĩnh vực của bạn trong mệnh đề GROUP BY.


3
Để làm cho câu trả lời này tốt, bạn nên bao gồm một chút chi tiết về ý của bạn.
Robbert

-2

Nó có thể được thực hiện bằng truy vấn bên trong

$query = "SELECT * 
            FROM (SELECT field
                FROM table
                ORDER BY id DESC) as rows               
            GROUP BY field";

2
Điều này không trả lời câu hỏi, OP đã cố gắng lấy tất cả dữ liệu của bảng nhưng loại bỏ các hàng có chứa các bản sao của một trường duy nhất
Garrett Simpson

-3
SELECT * from table where field in (SELECT distinct field from table)

7
Điều đó sẽ không làm công việc. Bạn đã chọn cột riêng biệt trong truy vấn con nhưng mệnh đề where nhận được tất cả các cột có giá trị đó. Vì vậy, truy vấn cũng tốt như viết 'select * từ bảng' trừ khi cột 'trường' là một cột duy nhất trong trường hợp khác biệt trên cột đó là không bắt buộc.
Ankur-m

-3

CHỌN DISTINCT FIELD1, FIELD2, FIELD3 TỪ TABLE1 hoạt động nếu các giá trị của cả ba cột là duy nhất trong bảng.

Ví dụ: nếu bạn có nhiều giá trị giống hệt nhau cho tên đầu tiên, nhưng tên cuối cùng và thông tin khác trong các cột được chọn là khác nhau, bản ghi sẽ được bao gồm trong tập kết quả.


2
Điều này không trả lời câu hỏi, OP đã cố gắng lấy tất cả dữ liệu của bảng nhưng loại bỏ các hàng có chứa các bản sao của một trường duy nhất
Garrett Simpson

-3

Tôi sẽ đề nghị sử dụng

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

bằng cách này nếu bạn có cùng giá trị trong trường1 trên nhiều hàng, tất cả các bản ghi sẽ được trả về.


1
Nó không khác với SELECT * FROM table;. Thậm chí nhiều hơn là chậm.
Shin Kim

Xin vui lòng, hãy thử câu trả lời của bạn đầu tiên.
Sherif
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.