SQL返回重复结果

8
为什么下面的SQL查询会返回重复的结果?我只想要3行结果。我猜测我的连接有误。限制应该可以从查询连接中说明。如果需要额外信息,请询问。
SELECT 
    [addresstype].name As [Type], 
    [address].city As [City], address.statecode As [State], 
    [address].postalcode As [Zip], 
    [address].addressid As [Id] 
FROM  
    [address]
    LEFT OUTER JOIN [contact_address] ON [address].addressid = [contact_address].addressid 
    LEFT OUTER JOIN [addresstype] ON [addresstype].addresstypeid = [contact_address].addresstypeid 
    LEFT OUTER JOIN [clientcontact] ON dbo.contact_address.contactid = [clientcontact].contactid 
WHERE  
    [contact_address].contactid = 12538 
ORDER BY 
    [address].name, [address].statecode, [address].city  

结果:

在此输入图片描述

======================

更多信息

看起来我有多个客户端。 我使用这个连接不是为了这个查询,而是依赖于这个查询的另一个查询。 这是在.NET代码中构建的自定义规则引擎。 另一个查询需要这个clientcontact连接,因为从UNION查询构建了一个临时表。如果那样的话,我就不需要这个表(clientcontact)来加入该连接。 我会获得多行,因为在clientcontact表中有多个客户端ID。 换句话说,这个联系人在所有这些客户端工作。 但是,我想放入一个WHERE子句,以便获取3行,但我不能修改JOINS。 以上是共享的。 如何做到这一点?……请原谅我的RIGHT JOIN..这不应该改变任何东西。 不要让它困扰您。 :-)

新查询如下:

SELECT 
    dbo.clientcontact.clientcontactid ,
    dbo.clientcontact.clientid ,
    dbo.clientcontact.contactid
    --[addresstype].name As [Type], 
    --[address].city As [City], address.statecode As [State], 
    --[address].postalcode As [Zip], 
    --[address].addressid As [Id] 
FROM  
    [address]
    LEFT OUTER JOIN [contact_address] ON [address].addressid = [contact_address].addressid 
    LEFT OUTER JOIN [addresstype] ON [addresstype].addresstypeid = [contact_address].addresstypeid 
    right JOIN [clientcontact] ON dbo.contact_address.contactid = [clientcontact].contactid 
WHERE  
    [contact_address].contactid = 12538 
ORDER BY 
    [address].name, [address].statecode, [address].city  

输入图像描述

=================

更多更新

有些人对于我为什么不能删除clientcontact join感到困惑。这是因为我们的.NET规则引擎中的另一个查询正在使用相同的查询。请参见下面UNION查询的第二个查询。如果通过保留该连接无法从中获取3行,那么我猜那就是答案。然后我需要将它们分开。

SELECT 
    client_addressexternal.address_table_type As [Address Record Type], 
    addresstype.name As [Type], 
    CASE WHEN client_addressexternal.address_table_type = 'CLIENT Address' THEN '<a href="/ClientServices/ManageClients/ClientDetails/ClientAddresses.aspx?Id=' + CONVERT(VARCHAR,client_addressexternal.addressid) + '&ClientId=' + CONVERT(VARCHAR,client_addressexternal.client_id) + '&SourceClientId=14103">' + address.name + '</a>' + '<br /><b>Client Name:</b> ' + client_addressexternal.client_full_name ELSE client_addressexternal.contact_full_name END As [Address Name], 
    dbo.limssubstring(dbo.LIMSNullString(address1) + '<br />' + dbo.LIMSNullString(address2), 84) As [Address], 
    address.city As [City], address.statecode As [State], 
    address.postalcode As [Zip], 
    CASE client.clientid WHEN 14103 THEN '' ELSE client.name END As [From Parent Client], 
    address.addressid As [Id] 
FROM  
address 
    JOIN (

        SELECT client_address.clientid, client_address.addressid, client_address.addresstypeid, depth, 'CLIENT Address' AS 'address_table_type', '' as 'contact_full_name', client.name as 'client_full_name', client_address.clientid as 'client_id', '' as 'contact_id'
        FROM dbo.fnClientRelatives(14103, 0, 1, 0) relatives
        inner join client_address on client_address.clientid = relatives.clientid
        LEFT OUTER JOIN client ON relatives.clientid = dbo.client.clientid

        UNION

        SELECT clientcontact.clientid, contact_address.addressid, contact_address.addresstypeid, 999 [depth], 'CONTACT Address' AS 'address_table_type', address.name + '<br /><b>Contact Name:</b> ' + LTRIM(RTRIM(ISNULL(contact.firstname, '') + ISNULL(' ' + contact.middleinitial + ' ', ' ') + ISNULL(contact.lastname, ''))), '' as 'client_full_name', clientcontact.clientid as 'client_id', clientcontact.contactid as 'contact_id'
        from clientcontact 
        inner join contact_address ON contact_address.contactid=clientcontact.contactid and clientcontact.clientid=14103
        LEFT OUTER JOIN [contact] ON [clientcontact].contactid = [contact].contactid
        LEFT OUTER JOIN [address] ON contact_address.addressid = address.addressid

    ) AS client_addressexternal ON client_addressexternal.addressid = address.addressid 
    JOIN client ON client.clientid = client_addressexternal.clientid 
    JOIN addresstype on addresstype.addresstypeid = client_addressexternal.addresstypeid 
 ORDER BY 
    depth,address.statecode, address.city, address.name     

如果你非常感兴趣,这里是函数:

GO
/****** Object:  UserDefinedFunction [dbo].[fnClientRelatives]    Script Date: 07/29/2011 12:48:24 ******/
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER ON
GO
--your basic recursive tree searcher.
--childrennotparents = 1 means you'll get children. = 0 means you'll get parents
--@recursive = 1 means it finds all children, grandchildren, etc... or whatever
-- The depth is the base level to start incrementing each level, if set to zero, the @clientid will also be part of the results
ALTER  FUNCTION [dbo].[fnClientRelatives]
(
    @clientId INT,
    @childrenNotParents BIT,
    @recursive bit,
    @depth int
)
RETURNS @clientids TABLE (clientid INT primary key clustered, depth int)
AS
begin

-- Add the parent client id if the depth is zero
if @depth = 0
begin
    INSERT INTO @clientids VALUES (@clientid, @depth)
end
set @depth = @depth + 1

IF @childrenNotParents = 1  
begin
    DECLARE clientids CURSOR FOR  
        SELECT clientid
        FROM client
        where parentclientid = @clientId
END--/if childrennotparents
ELSE--if not childrennotparents  
BEGIN  
    DECLARE clientids CURSOR FOR  
        SELECT parentclientid
        FROM client
        where clientid = @clientid
END--/if not childrennotparents

OPEN clientids
DECLARE @nextClientID INT
FETCH clientids INTO @nextClientID
--@nextClientID may be null if we're loading parents, and the
--current client has null for a parent id.
WHILE @@FETCH_STATUS = 0 AND @nextClientID IS NOT NULL
BEGIN  
    INSERT INTO @clientids
    VALUES (@nextclientid, @depth)

    IF @recursive = 1  
    BEGIN  
        INSERT INTO @clientids  
            SELECT * FROM dbo.fnClientRelatives(@nextclientid, @childrenNotParents, @recursive, @depth)    
    END--IF @recursive = 1    
FETCH clientids INTO @nextclientid  
END--WHILE @@FETCH_STATUS = 0  

CLOSE clientids  
DEALLOCATE clientids  

RETURN   
END--/IssueRelatives

地址数据库图示:

enter image description here


1
DISTINCT 可能会纠正结果,但查询中仍存在一些理解上的缺失。如果您可以提供表格,那么帮助会稍微容易一些。 - Sam DeHaan
6个回答

7
更多信息会更有帮助,但根据您提供的内容,我认为您在 clientcontact 表中有多个记录。
在您的选择语句中添加 DISTINCT 关键字或删除不必要的连接(您没有使用来自 clientcontact 表的任何内容)。
注意:很多人使用 DISTINCT 关键字来掩盖编写不良查询。虽然 DISTINCT 会给您期望的结果,但它并不能真正解决您的问题 - 它只是掩盖了它。在考虑使用 DISTINCT 前,请确保您理解为什么会出现重复记录。
编辑:
如果您无法删除连接(仍然不确定我为什么要这样做),并且 DISTINCT 不起作用(仍然不确定我为什么要这样做),那么添加一个 GROUP BY
GROUP BY [addresstype].name,
         [address].city,
         [adress].statecode,
         [address].postalcode,
         [address].addressid 

我提供了更多细节。我仍然想要3行,但正如我在更新后的问题底部的注释中提到的那样,我无法删除那个join。我用尽了所有的方法..现在是星期五了。Hill先生,你有什么建议吗? - Entree
1
在所有列上使用GROUP BY与使用DISTINCT在道德上/逻辑上/物理上是相同的。在这种情况下,我会选择DISTINCT,纯粹是因为它更短,而且不会让维护者思考为什么在没有聚合的情况下使用了分组。 - Damien_The_Unbeliever
1
@Damien,我理解并同意。在我的原始答案中建议使用DISTINCT。由于我不完全了解什么问题阻止了OP使用我的一些建议(如我的编辑中所述),因此我正在尝试呈现所有选项。 - James Hill
Group By 在初始查询中没有做任何不同的事情。我需要出去一下.. 我会在2小时后继续。 - Entree
嘿,我会多次点赞你的,因为你似乎是唯一指出DISTINCT只是解决问题的临时方法,而不是解决方案的人。 - Damien_The_Unbeliever
我给每个人点了赞。当我从我的约会回来后,我将继续测试和尝试不同的方法。 - Entree

5

你需要使用 SELECT DISTINCT,因为关系型数据库基于多重集合(尽管关系数据模型是基于集合的)。


5
在您的SELECT语句后添加DISTINCT。
SELECT DISTINCT
    [addresstype].name As [Type], 
    [address].city As [City], address.statecode As [State], 
    [address].postalcode As [Zip], 
    [address].addressid As [Id],
    [address].name
FROM  
    [address]
    LEFT OUTER JOIN [contact_address] ON [address].addressid = [contact_address].addressid 
    LEFT OUTER JOIN [addresstype] ON [addresstype].addresstypeid = [contact_address].addresstypeid 
    LEFT OUTER JOIN [clientcontact] ON dbo.contact_address.contactid = [clientcontact].contactid 
WHERE  
    [contact_address].contactid = 12538 
ORDER BY 
    [address].name, [address].statecode, [address].city  

在SELECT列表中添加[address].name,以解决您遇到的ORDER BY错误


错误消息 145,级别 15,状态 1,行 1 如果指定了 SELECT DISTINCT,则 ORDER BY 项必须出现在选择列表中。 - Entree
@Mr. MacGyver:您的选择列表中没有“[address].name”列。 - user194076
你需要将[address].name添加到你的选择列表中。 - Taryn

5
您可以使用Select distinct,但是您需要将[address].name列添加到选择列表中,就像这样(或者您可以从orderby子句中删除[address].name列):
SELECT DISTINCT
[address].name as [Address],
    [addresstype].name As [Type], 
    [address].city As [City], address.statecode As [State], 
    [address].postalcode As [Zip], 
    [address].addressid As [Id] 
FROM  
    [address]
    LEFT OUTER JOIN [contact_address] ON [address].addressid = [contact_address].addressid 
    LEFT OUTER JOIN [addresstype] ON [addresstype].addresstypeid = [contact_address].addresstypeid 
    LEFT OUTER JOIN [clientcontact] ON dbo.contact_address.contactid = [clientcontact].contactid 
WHERE  
    [contact_address].contactid = 12538 
ORDER BY 
    [address].name, [address].statecode, [address].city

它应该能够工作,但你可能需要重写你的查询。为此,你能否提供你的数据库表映射,以便我们提供帮助?


5

添加where子句可能不会得到您所期望的结果。

您描述的情况限制了您处理连接的能力,但按组是我能想到的唯一可行的选项。

GROUP BY ([addresstype].name,[address].city,
           address.statecode,[address].postalcode,[address].addressid)

只有在使用聚合操作(如sum()或avg())时才有效。 - das_weezul
据我所知,聚合函数并不是必需的。如果他需要后续使用此查询的直接结果来访问非均匀数据,则它会表现出奇怪的行为,但我们正在 GROUP BY 的数据不需要进行聚合。 - Sam DeHaan

4
了解为什么从查询中获得多个重复行是使用SQL时必须学习的关键技能之一,这也是我建议使用SELECT *而不是使用列列表的少数几个地方之一。一旦您查看了整个(宽)结果集,希望您可以确定整个结果集包含差异的位置(即使只包含5列的投影结果集看起来相同)。只有通过检查这些差异,您才能确定如何更新原始查询:通过向WHERE子句添加条件,向JOIN的一个ON子句添加条件,或引入一个可以减少结果集的新JOIN。

我想这个答案比其他答案更好,因为我解决它的方法是在规则引擎中添加一个扩展来排除共享连接。本质上,我排除了上面的clientcontact连接,因为我们只关心地址数据,而不是客户联系人(这些地址的联系人)所工作的客户。我想我可以通过在clientcontact表上添加WHERE子句或该连接的ON子句来实现另一种方式。我只是选择完全避免使用那个表。此外,使用DISTINCT或GROUP BY永远无法解决这个问题。 - Entree

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接