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

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 для фильтрации на основе того, видел ли сервер значение столбца раньше?

nuit9
источник

Ответы:

126

Очень простой ответ, если вы скажете, что вам все равно, какой адрес используется.

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
gbn
источник
Хотя это может не предназначаться для использования таким образом при выборе 10 столбцов. Также кажется, что он не может принимать столбец битового типа.
nuit9
1
@ nuit9: конечно, он не работает с битами и 10 столбцами. В вашем вопросе нет ни одного из этих фактов. Вы бы использовали вторую технику или технику Бена Тала. Я ответил на ваш конкретный вопрос, указав, как решать в более общем плане.
gbn
Первая часть ДОЛЖНА работать с несколькими столбцами, но не с битовыми столбцами. Я тестировал это на MS SQL Server 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
Пожалуйста, объясните, что делают rank, partition и [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 02 авг.16,
1
ROW_NUMBER()не работает и в Whereстатье в Терадате
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. Надеюсь, это поможет.

netfed
источник