我正在构建一个动态生成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的重复行。
我认为我试图解决问题的方式存在一些基本问题,但我无法弄清楚它是什么。提前谢谢。
答案 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的回答