2009-06-11 42 views
8

Tôi có một bảng trong SQL Server 2000 mà tôi đang cố gắng truy vấn theo một cách cụ thể. Cách tốt nhất để hiển thị điều này là với dữ liệu mẫu.Truy vấn SQL trả về một bản ghi duy nhất cho mỗi giá trị duy nhất trong một cột

Nầy, [Addresses]:

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

Đây thực sự là một ví dụ đơn giản về cấu trúc của bảng thực tế. Cấu trúc của bảng hoàn toàn nằm ngoài tầm kiểm soát của tôi. Tôi cần một truy vấn sẽ trả về một địa chỉ duy nhất cho mỗi tên. Không quan trọng địa chỉ nào, chỉ là chỉ có một địa chỉ. Kết quả có thể là thế này:

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

Tôi tìm thấy một câu hỏi tương tự here, nhưng không ai trong số các giải pháp cho công việc trong trường hợp của tôi, vì tôi không có quyền truy cập vào CROSS APPLY, và kêu gọi MIN() trên mỗi cột sẽ kết hợp địa chỉ khác nhau lại với nhau , và mặc dù tôi không quan tâm đến bản ghi nào được trả lại, nó phải là một hàng nguyên vẹn, không phải là một kết hợp của các hàng khác nhau.

Đề xuất thay đổi cấu trúc bảng sẽ không giúp tôi. Tôi đồng ý rằng bảng này là khủng khiếp, (nó tồi tệ hơn được hiển thị ở đây) nhưng đây là một phần của một cơ sở dữ liệu ERP lớn mà tôi không thể thay đổi.

Có khoảng 3000 bản ghi trong bảng này. Không có khóa chính.

Bất kỳ ý tưởng nào?

+0

Bạn có thể đưa ra ý tưởng về số lượng bản ghi trong bảng của mình không? Tôi có một số ý tưởng về việc này nhưng có thể không nhanh nếu có hàng nghìn triệu bản ghi. –

+0

Bạn có bất kỳ khóa chính nào trên bảng này không? –

+0

~ 3000 hồ sơ và không có PK, đủ ngạc nhiên. Tôi đã thêm thông tin này vào câu hỏi. – recursive

Trả lời

4

Vâng, điều này sẽ cung cấp cho bạn hiệu suất khá xấu, nhưng tôi nghĩ rằng nó sẽ làm việc

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

Thật không may, không có trường id duy nhất cho bảng này. Vâng tôi biết. Điều này hút. – recursive

+0

Tôi nghĩ rằng đó là một phần của tên đường phố. Nó dường như không có bất kỳ phím nào. –

+0

123, 234, v.v. không phải là ID, chúng là một phần của địa chỉ đường phố. Vấn đề như được đưa ra không bao gồm một trường ID trong bảng, đó là một điều đáng tiếc bởi vì giải pháp này là tuyệt vời nếu không. –

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

Điều đó sẽ không hoạt động: Bạn có thể có cùng địa chỉ đường phố ở nhiều thành phố. – Tadmas

+0

Thực tế, có nhiều ví dụ về nhiều hàng chứa cùng một địa chỉ, vì vậy trong những trường hợp đó, tôi vẫn nhận được bản sao. – recursive

+0

Cùng một địa chỉ cho cùng một tên? nếu tên + địa chỉ là duy nhất, điều này sẽ làm việc, tôi nghĩ rằng – tekBlues

0

Một sửa đổi chút ít trên trên nên làm việc.

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 

Điều này sẽ không hoạt động nếu bạn có cùng đường phố nhưng các thông tin khác khác nhau (ví dụ: với lỗi chính tả).

HOẶC băm hoàn chỉnh hơn sẽ bao gồm tất cả các lĩnh vực (nhưng bạn có thể có quá nhiều đối với hiệu suất):

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

Tôi đã kiểm tra điều này. Thật không may, có một số ví dụ về các bản ghi có cùng một đường phố. – recursive

+0

Nó sẽ phải là tên và đường phố để có một vấn đề. Bạn có thể thêm nhiều cột hơn để giảm khả năng va chạm, nhưng nhìn chung nó không phải là một giải pháp tuyệt vời cho vấn đề. Nếu có thể nhận được một khóa chính trên hồ sơ là tốt hơn. –

+0

Đóng, ngoại trừ "bảng" x không có trường có tên là Đường. Bạn sẽ cần một cái gì đó như "SELECT Name, MIN (Street) a" và "ON x.Name = t.Name AND x.a = t.Street" – mbeckish

0

Tôi không nghĩ rằng bạn có thể làm điều đó, vì khó khăn của bạn. Bạn có thể kéo ra các kết hợp riêng biệt của các trường đó. Nhưng nếu ai đó đánh vần Bob và Bobb với cùng một địa chỉ bạn sẽ kết thúc với hai bản ghi. [GIGO] Bạn chính xác rằng bất kỳ nhóm nào (thiếu nhóm trên tất cả các trường tương đương với DISTINCT) sẽ trộn các hàng. Thật tệ khi bạn không có số nhận dạng duy nhất cho từng khách hàng.

Bạn có thể lồng ghép các truy vấn với nhau theo cách như để chọn đầu 1 cho mỗi tên và kết hợp tất cả những thứ đó với nhau.

2

chọn Name, đường phố, thành phố, tiểu bang TỪ ( chọn Name, đường phố, thành phố, tiểu bang, ROW_NUMBER() OVER (PARTITION BY Tên ORDER BY Name) AS rn từ bảng) AS t ĐÂU rn = 1

+0

SQL Server 2000, không CTE – recursive

3

Sử dụng bảng tạm thời hoặc biến bảng và chọn danh sách tên riêng biệt vào đó. Sử dụng cấu trúc đó sau đó chọn đầu 1 của mỗi bản ghi trong bảng gốc cho mỗi tên riêng biệt.

+0

Hiệu suất cho việc này sẽ rất kém vì bạn sẽ phải sử dụng con trỏ cho điều này, trừ khi bạn có mã cho nó. –

+0

Nhìn vào bảng nguồn tôi không nghĩ hiệu suất là vấn đề. – Gratzy

+0

Tôi nên đọc tất cả các câu trả lời trước vì điều này giống như giải pháp của tôi. Bảng không được lập chỉ mục và chỉ có 3000 bản ghi vì vậy tôi không nghĩ con trỏ sẽ chậm hơn đáng kể so với giải pháp SQL đúng. Điều này cũng không tấn công tôi như một truy vấn sẽ được chạy thường xuyên. –

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 không hỗ trợ DENSE_RANK – recursive

1

Một giải pháp bảng tạm thời sẽ như sau

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

Điều này không hoạt động vì các thành phần địa chỉ không phải lúc nào cũng tăng hoặc giảm cùng nhau. Ví dụ: 123 <234, nhưng Peoria> Fargo. – recursive

+0

Ok, đã thử với tập dữ liệu của bạn và thực sự, dường như logic của tôi rất sai. Đã để lại chỉ là giải pháp bảng tạm thời mặc dù đó hoạt động tốt cho tôi. –

1

Đây là xấu xí như địa ngục, nhưng có vẻ như tình trạng khó khăn của bạn là xấu xí, quá ... vì vậy đây đi ...

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

Tôi đã viết giải pháp này trong đầu khi đọc qua các câu trả lời trước. Nó không phải là một câu trả lời khéo léo hay đẹp nhưng nó sẽ hoạt động. Một tùy chọn khác ở đây sẽ là thay đổi truy vấn con FROM thành GROUP BY để cải thiện hiệu suất một chút so với DISTINCT. –

+0

Điều này có được đảm bảo không trộn các địa chỉ không? – recursive

+0

nhu cầu và ORDER BY Tôi nghĩ rằng để được an toàn – Brimstedt

3

Nếu bạn có thể sử dụng một bảng temp:

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) 

Giải pháp này sẽ không được khuyến khích cho các bảng lớn hơn nhiều.

+1

++ 1. Đây là một câu trả lời tuyệt vời. Ngắn và ngọt. Nó không yêu cầu bạn phải liệt kê tất cả các tên trường riêng lẻ, và nó hoàn toàn tránh được vấn đề so sánh trường rỗng. Một truy vấn con tương quan, không có tổng hợp, không có tham gia. Đối với 3000 hàng, đây là a) mã hóa ít nhất, b) hiệu suất tốt và c) kết quả chống lừa đảo. Đẹp! –

1

Tôi nghĩ đây là một ứng cử viên tốt cho giải pháp dựa trên con trỏ. Đã quá lâu kể từ khi tôi đã sử dụng một con trỏ mà tôi sẽ không cố gắng để viết T-SQL nhưng đây là ý tưởng:

  1. Tạo bảng tạm thời với cùng một sơ đồ như Addresses
  2. Chọn Danh bạ riêng biệt vào con trỏ
  3. Vòng qua con trỏ chọn top 1 từ Địa chỉ vào bảng temp cho mỗi Tên biệt
  4. Return chọn từ bảng temp
0

Và vẫn còn một cách khác:

-- 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 
Các vấn đề liên quan