2009-06-11 3 views
8

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

Вот, [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 

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

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

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

Любые идеи?

+0

Можете ли вы дать представление о том, сколько записей в вашем столе? У меня есть некоторые идеи по этому поводу, но может быть не очень быстро, если есть тысячи/миллионы записей. –

+0

У вас есть первичные ключи на этой таблице? –

+0

~ 3000 записей, и никаких ПК, удивительно. Я добавил эту информацию к вопросу. – recursive

ответ

4

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

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 
+0

К сожалению, для этой таблицы нет уникального поля id. Да, я знаю. Это отстой. – recursive

+0

Я думаю, что это часть названия улицы. Кажется, у него нет ключей. –

+0

123, 234 и т. Д. Не являются идентификаторами, они являются частью уличного адреса. Задача, заданная, не включает поле идентификатора в таблице, что очень жаль, потому что это решение в противном случае. –

2
select distinct Name , street,city,state 
from table t1 where street = 
(select min(street) from table t2 where t2.name = t1.name) 
+0

Это не сработает: вы можете иметь один и тот же адрес улицы в нескольких городах. – Tadmas

+0

На самом деле есть примеры нескольких строк, которые содержат один и тот же адрес, поэтому в этих случаях я все равно получаю дубликаты. – recursive

+0

Тот же адрес для того же имени? если имя + адрес уникален, это будет работать, я думаю, – tekBlues

0

небольшая модификация на выше должна работать.

SELECT Name, Street, City, State 
FROM table t 
INNER JOIN (
    SELECT Name, MIN(Street) AS Street 
    FROM table m 
    GROUP BY Name 
) x 
    ON x.Name = t.Name AND x.Street = t.Street 

Теперь это не будет работать, если у вас есть по той же улице, но и другие части информации различны (например, с опечатками).

ИЛИ более полный хэш будет включать в себя все поля (но вы, вероятно, слишком много для производительности):

SELECT Name, Street, City, State 
FROM table t 
INNER JOIN (
    SELECT Name, MIN(Street + '|' + City + '|' + State) AS key 
    FROM table m 
    GROUP BY Name 
) x 
    ON x.Name = t.Name 
    AND x.key = Street + '|' + City + '|' + State 
+0

Я проверил это. К сожалению, есть несколько примеров записей, которые имеют одну и ту же улицу. – recursive

+0

Это должно быть имя и улица, чтобы там была проблема. Вы можете добавить больше столбцов, чтобы уменьшить вероятность столкновения, но в целом это не отличное решение проблемы. Если вообще возможно получить первичный ключ в записях, лучше. –

+0

Закрыть, кроме "table" x не имеет поля с именем Street. Вам понадобится нечто вроде «SELECT Name, MIN (Street) a» и «ON x.Name = t.Name AND x.a = t.Street» – mbeckish

0

Я не думаю, что вы можете сделать это, учитывая ваши ограничения. Вы можете выделить различные комбинации этих полей. Но если кто-то напишет Боба и Бобба с тем же адресом, у вас будет две записи. [GIGO] Вы правы, что любая группировка (без группировки по всем полям, эквивалентная DISTINCT) будет смешивать строки. Очень жаль, что у вас нет уникального идентификатора для каждого клиента.

Возможно, вы можете объединить запросы вместе, чтобы выбрать верхнюю часть 1 для каждого имени и объединить все вместе.

2

выберите Имя, улица, город, штат FROM ( выберите имя, улица, город, штат, ROW_NUMBER() OVER (PARTITION BY Name порядке по имени), как р-н из таблицы), как т ГДЕ гп = 1

+0

SQL Server 2000, no CTE – recursive

3

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

+0

Производительность для этого была бы очень плохой, поскольку вам придется использовать курсор для этого, если у вас нет кода для него. –

+0

Глядя на исходную таблицу, я не думаю, что производительность - это проблема. – Gratzy

+0

Сначала я должен был прочитать все ответы, потому что это то же самое, что и мое решение. Таблица не индексируется, и есть только 3000 записей, поэтому я не думаю, что курсор будет значительно медленнее, чем строго SQL-решение. Это также не влияет на меня как на запрос, который будет выполняться часто. –

0
SELECT name, 
     (SELECT TOP 1 street, city, state 
      FROM addresses b 
      WHERE a.name = b.name) 
    FROM addresses a 
GROUP BY name 
0
SELECT name, street, address, state 
FROM 
(SELECT name, street, address, state, 
    DENSE_RANK() OVER (PARTITION BY name ORDER BY street DESC) AS r 
FROM tbl) AS t 
WHERE r = 1; 
+0

SQL 2000 не поддерживает DENSE_RANK – recursive

1

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

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

Это не работает, потому что компоненты адреса не всегда увеличиваются или уменьшаются вместе. Например, 123 <234, но Пеория> Фарго. – recursive

+0

Хорошо, попробовал это с вашим набором данных и, действительно, кажется, моя логика была очень неправильной. Осталось только временное решение таблицы, хотя это отлично работает для меня. –

1

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

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 
+0

Я писал это решение в своей голове, когда читал предыдущие ответы. Это не гладкий или довольно ответ, но он должен работать. Другой вариант здесь - изменить подзапрос FROM на GROUP BY, чтобы улучшить производительность немного над DISTINCT. –

+0

Гарантировано ли это, что вы не смешиваете адреса? – recursive

+0

необходимо и ЗАКАЗАТЬ Я думаю, что это будет безопасно – Brimstedt

3

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

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) 

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

+1

++ 1. Это отличный ответ. Короткие и сладкие. Это не требует, чтобы вы перечисляли все имена полей по отдельности, и это полностью исключает проблему сопоставления полей с использованием NULL. Один коррелированный подзапрос, без агрегатов, без соединений. Для 3000 строк это: a) наименьшее кодирование, b) хорошая производительность и c) безупречные результаты. Красивый! –

1

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

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

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

-- 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 
0
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 
Смежные вопросы