从左外连接获取不同的行

时间:2009-04-25 13:58:28

标签: sql sql-server sql-server-2005 linq-to-sql

我正在构建一个动态生成sql的应用程序来搜索特定Table的行(这是主域类,如Employee)。

Table1,Table2和Table1Table2Map共有三个表。 Table1与Table2有多对多的关系,并通过Table1Table2Map表进行映射。但由于Table1是我的主要表格,因此这种关系几乎就像一对多。

我的应用程序生成一个sql,它基本上给出了一个包含所有这些表中行的结果集。 select子句和连接不会更改,而where子句是基于用户交互生成的。在任何情况下,我都不希望在我的结果集中重复使用Table1,因为它是结果显示的主表。现在,生成的查询是这样的:

select distinct Table1.Id as Id, Table1.Name, Table2.Description from Table1
left outer join Table1Table2Map on (Table1Table2Map.Table1Id = Table1.Id)
left outer join Table2 on (Table2.Id = Table1Table2Map.Table2Id)

为简单起见,我排除了where子句。问题是Table2中Table1中有多行,即使我已经说明了Table1的不同.Id结果集有重复的Table1行,因为它必须选择Table2中的所有匹配行。

要详细说明,请考虑对于Table1中Id = 1的行,Table1Table2Map(1,1)和(1,2)中有两行将Table1映射到Table2中的两行,其中ID为1,2。提到的查询为这种情况返回重复的行。现在我希望查询只返回Id 1一次的Table1行。这是因为Table2中只有一行类似于Table1中相应条目的活动值(此信息在Mapping表中)。 有没有办法可以避免获得Table1的重复行。

我认为我试图解决问题的方式存在一些基本问题,但我无法弄清楚它是什么。提前谢谢。

6 个答案:

答案 0 :(得分:24)

尝试:

left outer join (select distinct YOUR_COLUMNS_HERE ...) SUBQUERY_ALIAS on ...

换句话说,不要直接加入表,加入一个限制你加入的行的子查询。

答案 1 :(得分:12)

您可以在GROUP BY上使用Table1.Id,这将消除额外的行。您无需担心加入方的任何机制。

我在一个巨大的查询中提出了这个解决方案,而且这个解决方案对查询时间影响不大。

注意:我在被问到3年后才回答这个问题,但这可能对我相信的人有所帮助。

答案 2 :(得分:6)

您可以将左连接重写为外部适用,以便您可以使用前1和顺序,如下所示:

select Table1.Id as Id, Table1.Name, Table2.Description 
from Table1
outer apply (
   select top 1 *
   from Table1Table2Map
   where (Table1Table2Map.Table1Id = Table1.Id) and Table1Table2Map.IsActive = 1
   order by somethingCol 
) t1t2
outer apply (
   select top 1 *
   from Table2
   where (Table2.Id = Table1Table2Map.Table2Id)
) t2;

请注意,没有“top”或“order by”的外部应用完全等同于左外部连接,它只是为您提供更多控制。 (交叉申请相当于内部联接)。

你也可以使用row_number()函数做类似的事情:

 select * from (
      select distinct Table1.Id as Id, Table1.Name, Table2.Description,
        rowNum = row_number() over ( partition by table1.id order by something )
      from Table1
      left outer join Table1Table2Map on (Table1Table2Map.Table1Id = Table1.Id)
      left outer join Table2 on (Table2.Id = Table1Table2Map.Table2Id)
 ) x
 where rowNum = 1;

如果IsActive标志可以将其他表格缩小到一行,那么大部分内容都不适用,但它们可能会对您有用。

答案 3 :(得分:3)

详细说明一点:您说Table1中每行的Table2中只有一个“活动”行。该行是否未标记为活动,以便您可以将其放在where子句中?或者在用户提供的动态条件中有一些魔力可以确定什么是活动的,什么不是。

如果您不需要从Table2中选择任何内容,解决方案相对简单,因为您可以使用EXISTS函数,但由于您已将TAble2.Description放在该子句中,我将假设情况并非如此。

基本上是什么将Table2中的相关行与不相关的行分开?它是一个活跃的旗帜还是一个动态的条件?第一排?这就是你应该删除重复项的方法。

DISTINCT clauses tend to be overused。这可能不是这里的情况,但听起来你有可能试图用DISTINCT来破解你想要的结果,而不是解决真正的问题,这是一个相当普遍的问题。

答案 4 :(得分:2)

您必须在您的联接中包含活动子句(并且不需要区分):

select Table1.Id as Id, Table1.Name, Table2.Description from Table1
left outer join Table1Table2Map on (Table1Table2Map.Table1Id = Table1.Id) and Table1Table2Map.IsActive = 1
left outer join Table2 on (Table2.Id = Table1Table2Map.Table2Id)

答案 5 :(得分:1)

如果要在table2中显示多行,则会显示来自table1的重复数据。如果你想在table2上使用聚合函数(IE Max,Min),这将消除table1中的重复行,但也会隐藏table2中的一些数据。

有关其他说明,请参阅我对问题#70161的回答