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

发布于 2024-07-23 07:02:42 字数 1147 浏览 4 评论 0原文

我在 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条记录。 没有主键。

有任何想法吗?

I have a table in SQL Server 2000 that I am trying to query in a specific way. The best way to show this is with example data.

Behold, [Addresses]:

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

This is actually a simplified example of the structure of the actual table. The structure of the table is completely beyond my control. I need a query that will return a single address per name. It doesn't matter which address, just that there is only one. The result could be this:

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

I found a similar question here, but none of the solutions given work in my case because I do not have access to CROSS APPLY, and calling MIN() on each column will mix different addresses together, and although I don't care which record is returned, it must be one intact row, not a mix of different rows.

Recommendations to change the table structure will not help me. I agree that this table is terrible, (it's worse than shown here) but this is part of a major ERP database that I can not change.

There are about 3000 records in this table. There is no primary key.

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

春庭雪 2024-07-30 07:02:43

选择名称、街道、城市、州 FROM(
选择名称、街道、城市、州、
ROW_NUMBER() OVER(PARTITION BY 名称 ORDER BY 名称) AS rn
从表)AS t
其中 rn=1

select Name , street,city,state FROM(
select Name , street,city,state,
ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Name) AS rn
from table) AS t
WHERE rn=1

世俗缘 2024-07-30 07:02:43

这太丑陋了,但听起来你的困境也很丑陋......所以这里......

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

This is ugly as hell, but it sounds like your predicament is ugly, too... so here goes...

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
软糯酥胸 2024-07-30 07:02:43

临时表解决方案如下

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

A temporary table solution would be as follows

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
小嗷兮 2024-07-30 07:02:43

我认为这是基于游标的解决方案的一个很好的候选者。 我已经很久没有使用游标了,所以我不会尝试编写 T-SQL,但想法如下:

  1. 创建与 Addresses 具有相同架构的临时表
  2. 将不同的名称选择到游标中
  3. 循环遍历游标,从 Addresses 中选择前 1 个每个不同名称的临时表
  4. 返回从临时表中选择

I think this is a good candidate for a cursor based solution. It's been so long since I've used a cursor that I won't attempt to write the T-SQL but here's the idea:

  1. Create temp table with same schema as Addresses
  2. Select distinct Names into cursor
  3. Loop through cursor selecting top 1 from Addresses into temp table for each distinct Name
  4. Return select from temp table
芸娘子的小脾气 2024-07-30 07:02:43

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

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

I don't think that you can do that, given your constraints. You can pull out distinct combinations of those fields. But if someone spelled Bob and Bobb with the same address you'd end up with two records. [GIGO] You are correct that any grouping (short of grouping on all of the fields-equivalent to DISTINCT) will mix rows. It's too bad that you don't have a unique identifier for each customer.

You might be able to nest queries together in such as way as to select the top 1 for each name and join all of those together.

十年不长 2024-07-30 07:02:43

对上面的内容稍作修改就可以了。

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

A slight modification on the above should work.

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

Now this won't work if you have the same street but the other pieces of information are different (e.g. with typos).

OR a more complete hash would include all the fields (but you likely have too many for performance):

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
你好,陌生人 2024-07-30 07:02:43
SELECT name,
       ( SELECT TOP 1 street, city, state
           FROM addresses b
          WHERE a.name = b.name )
  FROM addresses a
 GROUP BY name
SELECT name,
       ( SELECT TOP 1 street, city, state
           FROM addresses b
          WHERE a.name = b.name )
  FROM addresses a
 GROUP BY name
末が日狂欢 2024-07-30 07:02:43

还有另一种方式:

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

And still another way:

-- 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
何以畏孤独 2024-07-30 07:02:43
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
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
浮华 2024-07-30 07:02:43
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; 
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; 
浅忆 2024-07-30 07:02:42

好吧,这会给你带来非常糟糕的性能,但我认为它会起作用

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

Well, this will give you pretty bad performance, but I think it'll work

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
徒留西风 2024-07-30 07:02:42

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

Use a temp table or table variable and select a distinct list of names into that. Use that structure then to select the top 1 of each record in the original table for each distinct name.

惯饮孤独 2024-07-30 07:02:42

如果可以使用临时表:

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)

对于更大的表,不建议使用此解决方案。

If you can use a temp table:

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)

This solution would not be advisable for much larger tables.

橘虞初梦 2024-07-30 07:02:42
select distinct Name , street,city,state
from table t1 where street =  
(select min(street) from table t2 where t2.name = t1.name)
select distinct Name , street,city,state
from table t1 where street =  
(select min(street) from table t2 where t2.name = t1.name)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文