SQL查询为列中的每个唯一值返回一条记录

时间:2009-06-11 18:52:37

标签: sql tsql sql-server-2000

我在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

我发现了一个类似的问题here,但由于我无法访问CROSS APPLY,并且在每列上调用MIN()会混合使用虽然我不关心返回哪条记录,但它必须是一条完整的行,而不是不同行的混合。

更改表格结构的建议对我没有帮助。我同意这个表格很糟糕(这比这里显示的更糟糕),但这是我无法改变的主要ERP数据库的一部分。

此表中有大约3000条记录。没有主键。

有什么想法吗?

14 个答案:

答案 0 :(得分: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

答案 1 :(得分:3)

使用临时表或表变量,并在其中选择不同的名称列表。然后使用该结构为原始表中的每个记录选择每个不同名称的前1个。

答案 2 :(得分: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)

对于更大的表格,此解决方案不可取。

答案 3 :(得分:2)

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

答案 4 :(得分:2)

选择姓名,街道,城市,州FROM( 选择姓名,街道,城市,州, ROW_NUMBER()OVER(按名称分区按名称排序)ASn 来自表)AS t 在哪里rn = 1

答案 5 :(得分: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

答案 6 :(得分: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

答案 7 :(得分:1)

我认为这是基于游标的解决方案的理想选择。我使用游标已经很久了,我不会尝试编写T-SQL,但这就是想法:

  1. 使用与地址相同的模式创建临时表
  2. 选择不同的名称到光标
  3. 循环光标从地址中选择前1到每个不同名称的临时表
  4. 从临时表中返回选择

答案 8 :(得分: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

如果您拥有相同的街道但其他信息不同(例如使用拼写错误),现在这将无效。

OR更完整的哈希将包括所有字段(但您可能有太多的性能):

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

答案 9 :(得分:0)

考虑到你的限制,我认为你不能这样做。您可以提取这些字段的不同组合。但如果有人用同一地址拼写Bob和Bobb,你最终会得到两条记录。 [GIGO]你是正确的,任何分组(在所有字段上分组 - 相当于DISTINCT)都会混合行。您没有为每个客户提供唯一标识符太糟糕了。

您可以将查询嵌套在一起,以便为每个名称选择前1并将所有这些加在一起。

答案 10 :(得分:0)

SELECT name,
       ( SELECT TOP 1 street, city, state
           FROM addresses b
          WHERE a.name = b.name )
  FROM addresses a
 GROUP BY name

答案 11 :(得分: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; 

答案 12 :(得分: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

答案 13 :(得分: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