Як вибрати лише перші рядки для кожного унікального значення стовпця


96

Скажімо, у мене є таблиця адрес клієнтів:

CName           |   AddressLine
-------------------------------
John Smith      | 123 Nowheresville
Jane Doe        | 456 Evergreen Terrace
John Smith      | 999 Somewhereelse
Joe Bloggs      | 1 Second Ave

У таблиці такий клієнт, як Джон Сміт, може мати кілька адрес. Мені потрібен запит вибору для цієї таблиці, щоб повернути лише перший знайдений рядок, де є дублікати в 'CName'. Для цієї таблиці вона повинна повертати всі рядки, крім 3-ї (або 1-ї - будь-яка з цих двох адрес в порядку, але можна повернути лише одну). Чи є ключове слово, яке я можу додати до запиту SELECT для фільтрування на основі того, чи сервер вже бачив значення стовпця раніше?

Відповіді:


125

Дуже проста відповідь, якщо ви скажете, що вам байдуже, яка адреса використовується.

SELECT
    CName, MIN(AddressLine)
FROM
    MyTable
GROUP BY
    CName

Якщо ви хочете перший, скажімо, "вставлений" стовпець, то це інший запит

SELECT
    M.CName, M.AddressLine,
FROM
    (
    SELECT
        CName, MIN(Inserted) AS First
    FROM
        MyTable
    GROUP BY
        CName
    ) foo
    JOIN
    MyTable M ON foo.CName = M.CName AND foo.First = M.Inserted

Хоча, можливо, це не передбачається використовувати таким чином при виборі 10 стовпців. Також здається, що він не може прийняти стовпець бітового типу.
nuit9

1
@ nuit9: звичайно, це не буде працювати з бітами та 10 стовпцями. Жоден із цих фактів не є вашим запитанням. Ви б використали 2-ю техніку або техніку Бен Тула. Я відповів на те, що ви запитали конкретно, із вказівками на те, як вирішити загальніше.
gbn

Перша частина РОБИТИ з кількома стовпцями, хоча не зі стовпцями бітового типу. Я протестував це на сервері MS SQL 2016.
netfed

24

У SQL 2k5 + ви можете зробити щось на зразок:

;with cte as (
  select CName, AddressLine,
  rank() over (partition by CName order by AddressLine) as [r]
  from MyTable
)
select CName, AddressLine
from cte
where [r] = 1

5
Поясніть, будь ласка, що означає ранг, розділ та [r]
Роберто

10

Ви можете використовувати, row_number()щоб отримати номер рядка. Він використовує overкоманду - partition byпункт визначає, коли перезапускати нумерацію, і order byвибирає, на що замовити номер рядка. Навіть якщо ви додали order byв кінець запиту, це збереже порядок у overкоманді при нумерації.

select *
from mytable
where row_number() over(partition by Name order by AddressLine) = 1

6
У postgresql функції вікна заборонені в реченні WHERE
ekanna

3
Це також не дозволено для MS-SQL.
Mixxiphoid

1
ROW_NUMBER()не працює в Whereпункті і в Teradata
Pirate X

6

Ви можете використовувати row_numer() over(partition by ...)синтаксис приблизно так:

select * from
(
select *
, ROW_NUMBER() OVER(PARTITION BY CName ORDER BY AddressLine) AS row
from myTable
) as a
where row = 1

Це робить те, що він створює стовпець із назвою row, який є лічильником, який збільшується щоразу, коли бачить те саме CName, і індексує ці випадки за AddressLine. Накладаючи where row = 1, можна вибрати, CNameчий AddressLineприходить першим за алфавітом. Якби це order byбуло desc, тоді він вибирав би, CNameчий AddressLineприходить останнім за алфавітом.


1

Це дасть вам один рядок кожного повторюваного рядка. Він також надасть вам стовпці бітового типу, і він працює принаймні на MS Sql Server.

(select cname, address 
from (
  select cname,address, rn=row_number() over (partition by cname order by cname) 
  from customeraddresses  
) x 
where rn = 1) order by cname

Якщо замість цього ви хочете знайти всі дублікати, просто змініть rn = 1 на rn> 1. Сподіваюся, це допоможе

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.