需要在仅返回一行的外连接中创建表达式

问题描述:

我创建了一个非常复杂的动态sql,它必须为每个用户返回一行,但现在我必须加入一对多表。我做了一个外连接来确保我至少返回了一行(并且可以检查null以查看该表是否有数据),但是我必须确保从外连接部分只返回一行,如果有多个此用户的第二个表中的行。 到目前为止,我想出了这一点:(SYBASE)需要在仅返回一行的外连接中创建表达式

select a.user_id 
from table1 a, table2 b 
where a.user_id = b.user_id 
and a.sub_id = (select min(c.sub_id) 
       from table2 c 
       where b.sub_id = c.sub_id) 

子查询发现在一个最小值到许多表中特定用户。

这可行,但我担心表1和表2变得非常大时,做相关的子查询时会出现问题。 有没有更好的方法?我试图想出一种方法来加入,但我没有看到它。 也说“哪里rowcount = 1”或“顶部1”没有帮助我,因为我不想解决上述查询,我​​将上述添加到一个已经很复杂的查询。

+0

该查询写入后将不起作用。 “从table2 c中选择min(c.sub_id),其中b.sub_id = c.sub_id”将始终返回b.sub_id,使外部where子句:“a.user_id = b.user_id和a.sub_id = b.sub_id” – 2008-10-27 14:47:19

+0

这里:a.user_id = b.user_id 用于说a.user_id * = b.user_id – stu 2008-10-27 15:20:55

+0

是的,查询是borked。它总是会返回b.sub_id。我认为詹姆斯是正确的,除非你正在尝试做一些不同的事情...... – Shawn 2008-10-27 15:25:02

也许你的例子过于简化了,但我会通过使用一组:

 
SELECT 
    a.user_id 
FROM 
    table1 a 
    LEFT OUTER JOIN table2 b ON (a.user_id = b.user_id) 
GROUP BY 
    a.user_id 

我担心唯一的其他方式将使用嵌套查询:

此查询和你之间的区别例子是一个'子表'只生成一次,但在你的例子中,你为table1中的每一行生成一个'子表'(但可能取决于编译器,所以你可能想用查询分析器来检查性能)。

 
SELECT 
    a.user_id, 
    b.sub_id 
FROM 
    table1 a 
    LEFT OUTER JOIN (
     SELECT 
     user_id, 
     min(sub_id) as sub_id, 
     FROM 
     table2 
     GROUP BY 
     user_id 
    ) b ON (a.user_id = b.user_id) 

此外,如果你的查询变得相当复杂,我会使用临时表来简化代码,它可能花费的处理时间多一点,反而会使你的查询更容易维护。

一个临时表例子是:

 
SELECT 
    user_id 
INTO 
    #table1 
FROM 
    table1 
WHERE 
    ..... 

SELECT 
    a.user_id, 
    min(b.sub_id) as sub_id, 
INTO 
    #table2 
FROM 
    #table1 a 
    INNER JOIN table2 b ON (a.user_id = b.user_id) 
GROUP BY 
    a.user_id 

SELECT 
    a.*, 
    b.sub_id 
from 
    #table1 a 
    LEFT OUTER JOIN #table2 b ON (a.user_id = b.user_id) 
+0

我无法将整个查询转换为聚合查询。 – stu 2008-10-27 13:56:49

如何:

select a.user_id 
from table1 a 
where exists (select null from table2 b 
       where a.user_id = b.user_id 
      ) 

在MySQL中,你可以确保任何查询返回至多X行使用

select * 
from foo 
where bar = 1 
limit X; 

不幸,我相当肯定这是一个MySQL特定的SQL扩展。但是,Google搜索“mysql sybase限制”等内容可能会成为Sybase的等价物。

首先,我相信你试图写为您的示例查询是:

select a.user_id 
from table1 a, table2 b 
where a.user_id = b.user_id 
and b.sub_id = (select min(c.sub_id) 
       from table2 c 
       where b.user_id = c.user_id) 

除非你想要一个外连接(我觉得有人编辑了甲骨文语法)。

select a.user_id 
from table1 a 
left outer join table2 b on a.user_id = b.user_id 
where b.sub_id = (select min(c.sub_id) 
       from table2 c 
       where b.user_id = c.user_id) 

几个简单的要点:

  1. 你需要有明确的业务规则。如果查询返回多行,那么您需要考虑原因(除了“它是1:多关系 - 为什么它是1:多关系?”)。你应该想出商业解决方案,而不是仅仅使用“min”,因为它给你1行。业务解决方案可能只是“采取第一个”,在这种情况下,min可能是答案,但您需要确保这是一个有意识的决定。
  2. 你应该真的尝试为连接使用ANSI语法。不仅仅是因为它是标准的,而且因为你拥有的语法并不是真的在做你认为它正在做的事情(它不是外连接),有些事情根本不可能用你的语法来完成。

假设您最终使用MIN解决方案,下面是一个没有子查询的可能解决方案。你应该用各种其他解决方案来测试它,以确保它们在结果上是相同的,并且看看哪个效果最好。

SELECT 
    a.user_id, b.* 
FROM 
    dbo.Table_1 a 
LEFT OUTER JOIN dbo.Table_2 b ON b.user_id = a.user_id AND b.sub_id = a.sub_id 
LEFT OUTER JOIN dbo.Table_2 c ON c.user_id = a.user_id AND c.sub_id < b.sub_id 
WHERE 
    c.user_id IS NULL 

你需要测试这个,看看它是否真的给你想要什么,你可能需要调整它,但其基本思想是利用第二左外连接,以确保没有行存在一个比在第一个LEFT OUTER JOIN中找到的更低的sub_id(如果有的话)。您可以根据最终的业务规则调整第二个LEFT OUTER JOIN中的条件。

那么,你已经有了一个可行的查询。如果您担心速度,你可以

  • 添加一个字段,表2,其 识别哪个sub_id是 “第一个”或

  • 跟踪表2的主键的表1,或另一张表