Запрос SQL для возврата одной записи для каждого уникального значения в столбце - PullRequest
8 голосов
/ 11 июня 2009

У меня есть таблица в SQL Server 2000, которую я пытаюсь запросить особым образом. Лучший способ показать это на примере данных.

Вот, [Addresses]:

Name         Street                 City          State
--------------------------------------------------------
Bob          123 Fake Street        Peoria        IL
Bob          234 Other Street       Fargo         ND
Jim          345 Main Street        St Louis      MO

На самом деле это упрощенный пример структуры фактической таблицы. Структура таблицы полностью вне моего контроля. Мне нужен запрос, который будет возвращать один адрес на имя. Неважно, какой адрес, просто есть только один. Результат может быть таким:

Name         Street                 City          State
--------------------------------------------------------
Bob          123 Fake Street        Peoria        IL
Jim          345 Main Street        St Louis      MO

Я нашел похожий вопрос здесь , но ни одно из приведенных решений не работает в моем случае, потому что у меня нет доступа к CROSS APPLY, а вызов MIN() для каждого столбца будет смешивать разные адреса вместе и хотя мне все равно, какая запись возвращается, это должна быть одна целая строка, а не смесь разных строк.

Рекомендации по изменению структуры таблицы мне не помогут. Я согласен, что эта таблица ужасна (она хуже, чем показано здесь), но это часть основной базы данных ERP, которую я не могу изменить.

В этой таблице около 3000 записей. Первичного ключа нет.

Есть идеи?

Ответы [ 14 ]

4 голосов
/ 11 июня 2009

Ну, это даст вам довольно плохую производительность, но я думаю, что это будет работать

SELECT t.Name, t.Street, t.City, t.State
FROM table t 
INNER JOIN (
     SELECT m.Name, MIN(m.Street + ';' + m.City  + ';' + m.State) AS comb
     FROM table m
     GROUP BY m.Name
) x
   ON  x.Name = t.Name
   AND x.comb = t.Street + ';' + t.City  + ';' + t.State
3 голосов
/ 11 июня 2009

Если вы можете использовать временную таблицу:

select * -- Create and populate temp table 
into #Addresses
from Addresses 

alter table #Addresses add PK int identity(1, 1) primary key

select Name, Street, City, State 
-- Explicitly name columns here to not return the PK
from #Addresses A
where not exists 
    (select *
    from #Addresses B
    where B.Name = A.Name
    and A.PK > B.PK)

Это решение не рекомендуется для гораздо больших таблиц.

3 голосов
/ 11 июня 2009

Используйте временную таблицу или табличную переменную и выберите для нее отдельный список имен. Затем используйте эту структуру, чтобы выбрать первые 1 записи в исходной таблице для каждого отдельного имени.

2 голосов
/ 11 июня 2009

выберите Название, улица, город, штат ОТ ( выберите Имя, улица, город, штат, ROW_NUMBER () OVER (PARTITION BY Name ORDER BY Name) AS из таблицы) КАК т ГДЕ рН = 1

2 голосов
/ 11 июня 2009
select distinct Name , street,city,state
from table t1 where street =  
(select min(street) from table t2 where t2.name = t1.name)
1 голос
/ 12 июня 2009

Я думаю, что это хороший кандидат для решения на основе курсора. Прошло так много времени с тех пор, как я использовал курсор, что я не буду пытаться писать T-SQL, но вот идея:

  1. Создать временную таблицу с той же схемой, что и адреса
  2. Выберите отдельные имена в курсоре
  3. Прокручивая курсор, выбирая верхнюю 1 из Адреса во временную таблицу для каждого отдельного Имени
  4. Возврат выберите из временной таблицы
1 голос
/ 11 июня 2009

Это чертовски уродливо, но, похоже, ваше затруднительное положение тоже ужасно ... так что вот так ...

select  name,
    (select top 1 street from [Addresses] a1 where a1.name = a0.name) as street,
    (select top 1 city from [Addresses] a2 where a2.name = a0.name) as city,
    (select top 1 state from [Addresses] a3 where a3.name = a0.name) as state
from    (select distinct name from [Addresses]) as a0
1 голос
/ 11 июня 2009

Временное табличное решение будет выглядеть следующим образом

CREATE Table #Addresses
(
    MyId int IDENTITY(1,1),
    [Name] NVARCHAR(50),
    Street NVARCHAR(50),
    City NVARCHAR(50),
    State NVARCHAR(50)
)

INSERT INTO #Addresses ([Name], Street, City, State) SELECT [Name], Street, City, State FROM Addresses

SELECT
    Addresses1.[Name],
    Addresses1.Street,
    Addresses1.City,
    Addresses1.State
FROM
    #Addresses Addresses1
WHERE
    Addresses1.MyId =
(
    SELECT
        MIN(MyId)
    FROM
        #Addresses Addresses2
    WHERE
        Addresses2.[Name] = Addresses1.[Name]
)

DROP TABLE #Addresses
0 голосов
/ 11 июня 2012
select c.*, b.* from companies c left outer join 
(SELECT *,
    ROW_NUMBER()
        OVER(PARTITION BY FKID ORDER BY PKId) AS Seq
 FROM Contacts) b on b.FKID = c.PKID and b.Seq = 1
0 голосов
/ 12 июня 2009

И еще один способ:

-- build a sample table  
DECLARE @T TABLE (Name VARCHAR(50),Street VARCHAR(50),City VARCHAR(50),State VARCHAR(50))  
INSERT INTO @T   
SELECT 'Bob','123 Fake Street','Peoria','IL' UNION  
SELECT 'Bob','234 Other Street','Fargo','ND' UNION  
SELECT 'Jim','345 Main Street','St Louis','MO' UNION  
SELECT 'Fred','234 Other Street','Fargo','ND'  

-- here is all you do to get the unique record  
SELECT * FROM @T a WHERE (SELECT COUNT(*) FROM @T b WHERE a.Name = b.name and a.street <= b.street) = 1
...