Як використовувати DISTINCT і ORDER BY в одному і тому самому операторі SELECT?


117

Після виконання наступного твердження:

SELECT  Category  FROM MonitoringJob ORDER BY CreationDate DESC

Я отримую з бази даних такі значення:

test3
test3
bildung
test4
test3
test2
test1

але я хочу, щоб копії видалено, як це:

bildung
test4
test3
test2
test1

Я спробував використовувати DISTINCT, але він не працює з ORDER BY в одному операторі. Будь ласка, допоможіть.

Важливо:

  1. Я спробував це:

    SELECT DISTINCT Category FROM MonitoringJob ORDER BY CreationDate DESC

    це не працює.

  2. Порядок CreationDate дуже важливий.


1
Як це не працює? Неправильний вихід?
Федеріан

Відповіді:


195

Проблема полягає в тому, що стовпці, які використовуються в ORDER BY, не вказані в DISTINCT. Для цього вам потрібно використати сукупну функцію для сортування та використання a GROUP BYдля створення DISTINCTроботи.

Спробуйте щось подібне:

SELECT DISTINCT Category, MAX(CreationDate) 
FROM MonitoringJob 
GROUP BY Category 
ORDER BY MAX(CreationDate) DESC, Category

99
Вам навіть не потрібне ключове слово DISTINCT, якщо ви групуєте за категоріями.
MatBailie

18

Розширені сортування ключових стовпців

Причина того, що те, що ви хочете зробити, не працює, полягає в логічному порядку операцій у SQL , який для вашого першого запиту (спрощений):

  • FROM MonitoringJob
  • SELECT Category, CreationDateтобто додайте так званий стовпець розширеного ключа
  • ORDER BY CreationDate DESC
  • SELECT Categoryтобто знову видаліть розширений стовпчик клавіш сортування з результату.

Отже, завдяки стандартній функції стовпчика розширеного сортування ключа SQL , цілком можливо замовити щось, що не зазначене в SELECTпункті, оскільки воно тимчасово додається до нього за кадром.

Отже, чому це не працює DISTINCT?

Якщо ми додамо DISTINCTоперацію, вона буде додана між SELECTта ORDER BY:

  • FROM MonitoringJob
  • SELECT Category, CreationDate
  • DISTINCT
  • ORDER BY CreationDate DESC
  • SELECT Category

Але тепер, з розширеним стовпчиком клавіш сортування CreationDate , семантика DISTINCTоперації була змінена, тому результат вже не буде таким. Це не те, чого ми хочемо, тому і стандарт SQL, і всі розумні бази даних забороняють це використання.

Обхідні шляхи

Його можна емулювати стандартним синтаксисом наступним чином

SELECT Category
FROM (
  SELECT Category, MAX(CreationDate) AS CreationDate
  FROM MonitoringJob
  GROUP BY Category
) t
ORDER BY CreationDate DESC

Або просто просто (в даному випадку), як показав також Prutswonder

SELECT Category, MAX(CreationDate) AS CreationDate
FROM MonitoringJob
GROUP BY Category
ORDER BY CreationDate DESC

Я детальніше розмістив тут повідомлення про SQL DISTINCT та ORDER BY .


1
Я думаю, ти помилився з тим, як DISTINCT ONпрацює, і майже впевнений, що це тут не допомагає. Вираз у дужках - це те, що використовується для визначення чіткості (умова групування). Якщо є різні категорії з однаковою, CreationDateто в результаті з’явиться лише одна! Оскільки мені було цікаво, чи, можливо, я помилявся якось, я також завантажив прикладну базу даних у своєму записі в блозі, щоб подвійно перевірити: DISTINCT ONзапит, який ви подали там, дав загальну кількість 1000 результатів (з великою кількістю дублікатів length), тоді як запит під ним дав лише 140 (унікальних) значень.
Інклінг

@Inkling: Дякуємо за ваш час. ОП явно хоче, щоб "дублікати" були вилучені. Дивіться формулювання ОП "але я хочу, щоб копії були видалені, як це" . Ви, мабуть, помилилися, копіюючи запити з моєї публікації в блозі. Є два запити, той, який використовує DISTINCT(ні ON) і той, який використовує DISTINCT ON. Зверніть увагу, що остання не видаляє дублікати довжин, а дублюючі заголовки. Я думаю, що моя відповідь тут абсолютно правильна.
Лукаш Едер

1
Моя думка, що ваші DISTINCT ONумови видаляють дублікати, використовуючи неправильну умову. У своєму записі в блозі DISTINCT ONзапит дійсно видаляє повторювані заголовки , однак DISTINCTзапит над ним та запит під ним (що ви стверджуєте, що це "синтаксичний цукор") обидва видаляють дублікати довжин , оскільки це, мабуть, вся мета. Тут застосовується те саме: ОП хоче видалити повторювані категорії , а не дублювати CreationDates, як це DISTINCT ONробить запит. Якщо ти все ще не віриш мені, протестуй на собі.
Інклінг

6

Якщо вихід MAX (CreationDate) не потрібен - як у прикладі оригінального запитання - єдиною відповіддю є друге твердження відповіді Прашанта Гупти:

SELECT [Category] FROM [MonitoringJob] 
GROUP BY [Category] ORDER BY MAX([CreationDate]) DESC

Пояснення: ви не можете використовувати пункт ORDER BY у функції inline, тому висловлювання у відповіді Prutswonder в цьому випадку не використовується, ви не можете поставити навколо нього зовнішній вибір і відкинути частину MAX (CreationDate).


2

Просто використовуйте цей код, якщо ви хочете значення у стовпцях [Категорія] та [CreationDate]

SELECT [Category], MAX([CreationDate]) FROM [MonitoringJob] 
             GROUP BY [Category] ORDER BY MAX([CreationDate]) DESC

Або використовуйте цей код, якщо ви хочете лише значення стовпця [Категорія].

SELECT [Category] FROM [MonitoringJob] 
GROUP BY [Category] ORDER BY MAX([CreationDate]) DESC

Ви будете мати всі чіткі записи, що хочете.


ці дужки [] абсолютно заплутані ... чи справжній синтаксис SQL?
m13r

1
Дужки призначені для уникнення ключових слів, таких як Порядок, подія тощо. Отже, якщо у вас є (наприклад) стовпець, що називається, Eventви можете писати [Event]замість того, Eventщоб зупиняти помилку розбору SQL.
Бен Максфілд

1

2) Порядок від CreationDate дуже важливий

Початкові результати вказували, що "test3" мав кілька результатів ...

Дуже просто починати використовувати MAX весь час для видалення дублікатів у групі By ... і забути чи ігнорувати, що є основним питанням ...

ОП, мабуть, зрозумів, що використання MAX дає йому останнє "створене", а використання MIN дасть перше "створене" ...


3
Насправді це не відповідає на питання, це здається, що це коментар щодо використання інших користувачів MAX, а не щось, що є окремим як відповідь на питання.
DaveyDaveDave

0
if object_id ('tempdb..#tempreport') is not null
begin  
drop table #tempreport
end 
create table #tempreport (
Category  nvarchar(510),
CreationDate smallint )
insert into #tempreport 
select distinct Category from MonitoringJob (nolock) 
select * from #tempreport  ORDER BY CreationDate DESC

0

За допомогою підзапиту він повинен працювати:

    SELECT distinct(Category) from MonitoringJob  where Category in(select Category from MonitoringJob order by CreationDate desc);

Уммм ... я не думаю, що так буде. Зовнішній вибір не сортується.
Хоссам Ель-Дін

це не буде працювати, я тут, бо це не працює
Амірреза

-1

Виразні будуть сортувати записи у порядку зростання. Якщо ви хочете сортувати в порядку замовлення, використовуйте:

SELECT DISTINCT Category
FROM MonitoringJob
ORDER BY Category DESC

Якщо ви хочете сортувати записи на основі поля CreationDate, це поле повинно бути у виписці Вибір:

SELECT DISTINCT Category, creationDate
FROM MonitoringJob
ORDER BY CreationDate DESC

12
Це виконає, але не дасть того, що потрібно ОП. ОП бажає різних категорій, а не виразних комбінацій категорій та CreateDate. Цей код може отримати кілька екземплярів однієї категорії, кожен з яких має різні значення CreationDate.
MatBailie

-1

Ви можете використовувати CTE:

WITH DistinctMonitoringJob AS (
    SELECT DISTINCT Category Distinct_Category FROM MonitoringJob 
)

SELECT Distinct_Category 
FROM DistinctMonitoringJob 
ORDER BY Distinct_Category DESC

-3

Спробуйте далі, але це не корисно для величезних даних ...

SELECT DISTINCT Cat FROM (
  SELECT Category as Cat FROM MonitoringJob ORDER BY CreationDate DESC
);

4
"Заява ORDER BY недійсна у представленнях, вбудованих функціях, похідних таблицях, підзапитах та загальних виразах таблиць, якщо також не вказано TOP або FOR XML."
TechplexEngineer

Це не працює, оскільки ви не вказали стовпець CreationDate в порядку замовлення.
Мауро Білотті

1
@TechplexEngineer Ваш коментар невірний. Використання ORDER BYв підзапитах абсолютно вірно. І хтось навіть підкреслив ваш неправильний коментар.
Раціл Хілан

Я намагаюся це зробити і маю таку ж помилку з @TechplexEngineer. Я використовую спеціальне замовлення у випадку, коли.
Еге Байрак

-4

Це можна зробити за допомогою внутрішнього запиту

$query = "SELECT * 
            FROM (SELECT Category  
                FROM currency_rates                 
                ORDER BY id DESC) as rows               
            GROUP BY currency";

-5
SELECT DISTINCT Category FROM MonitoringJob ORDER BY Category ASC

2
мені це потрібно відсортовано за датою створення !! це дуже важливо
rr

Тож чи неможливо додати колонку, яку ви хочете замовити самостійно? Ваш приклад показав записи, упорядковані за алфавітом. Якщо вам потрібно замовити за створеною датою, просто додайте його. Це справді не так складно.
Фурікане

8
-1: ОП намагалася це зробити, але не вийшло, тому що це неможливо, і ви, мабуть, проігнорували цей факт, коли опікували ОП. Справа в тому, що оператор DISTINCT зіставить кілька записів з однаковим значенням категорії, кожен з яких може мати різні дати створення. Таким чином, логічно неможливо при використанні DISTINCT. Це підштовхує необхідну логіку до GROUP BY замість DISTINCT, дозволяючи агрегату (MAX) на дату створення.
MatBailie

Насправді, якщо ви уважніше подивіться на те, що зробила ОП, яка абсолютно неправильно розробила SQL - я не допустив жодної помилки, і отриманий результат відповідає тому, що він запитував. Я не буду турбуватися до -1, просто читайте наступного разу, перш ніж виправляти людей. Дякую.
Фурікане

8
Ви прямо пропонуєте додати поле CreationDate, навіть кажучи, що "це дійсно не так складно". Це призводить до неправильного формування SQL. Ви отримали -1 за покровительство в рамках ОП, даючи поради, які повертають ОП до заяви, яку він спочатку опублікував, і не помітили суперечки між DISTINCT та замовленням полю, що не знаходиться в DISTINCT. Крім того, 'b' надходить до 't', а '1' надходить до '4', тому результати, надані ОП, категорично не в алфавітному порядку. Чи можу я запропонувати вашу власну пораду: читайте (уважніше) наступного разу.
MatBailie
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.