选择前10个,然后连接表格。而不是从连接的表格中选择前10个。

4
我继承了一个存储过程,它跨越了八个表进行连接操作,其中一些表包含数十万行数据,然后从该连接结果中选择前十条记录。
我在存储过程开始时拥有足够的信息以从单个表中选择这十条记录,然后对这十条记录执行连接操作,而不是在成千上万的中间行上执行连接操作。
我如何选择这些前十行,并仅在这十行上执行连接操作,而不是在整张表的所有千万行上执行连接操作?

4
通常查询优化器会自动处理这个问题。检查执行计划 - 你可能只是在这里遇到了一个完全不成问题的情况。 - TomTom
查询返回用户最近操作的十条记录。问题浮出水面是因为那些操作了最多记录的用户遇到了最严重的问题。我承认可能我对问题的看法不正确,但这绝对不是一个无关紧要的问题。 - Frosty840
是的,比如说... 都是独立开发者吗? - TomTom
只是确认一下,问题确实是在连接数千行之后才选择了最近的十个条目。现在,在子查询中早期选择了前十个(由Marco解决),然后执行的连接数量就少得多了。 - Frosty840
3个回答

9

我应该尝试:

SELECT * FROM
    (SELECT TOP 10 * FROM your_table
     ORDER BY your_condition) p
INNER JOIN second_table t
    ON p.field = t.field

1
  1. 如果您使用内部连接,优化器可能无法执行前10个操作,因为它无法确定内部连接是否会在后面排除行。如果选择了主表中的10行,然后由于连接而最终只返回7行,则这将是一个错误。使用Marco的重写可能会提高性能,因为您明确声明在连接之前限制行是安全的。
  2. 如果您的查询足够复杂,查询计划优化器可能会耗尽时间找到一个好的计划。它只有几百毫秒的时间,即使有几个连接,它也可能有数千种不同的方式来执行查询(不同的连接顺序等)。如果是这种情况,您将受益于首先将前10行存储在临时表中,然后像这样使用:

    select top 10 *
    into #MainResults
    from MyTable
    order by your_condition;
    
    
    select *
    from #MainResults r
    join othertable t
      on t.whatever = r.whatever;
    

    我见过这种第二种方法产生巨大差异的情况。


1
嗯,我几乎总是有相反的经验。将临时表移入内联视图、CTE或普通连接中比使用临时表更好。 - Conrad Frix
1
这完全取决于规模的大小。如果行数很少(在这种情况下为10),可能值得做。但如果它们开始增加,那么它肯定不值得做。 - John Gibb

0
你还可以使用CTE来定义前X个,然后使用它。
例如,这个data.se query只限制在前40个标签。
with top40 as (
  select top 40 t.id, t.tagname
  from tags t, posttags pt
  where pt.tagid = t.id
  group by t.tagname, t.id
  order by count(pt.postid) desc
),

myanswers as(
  select p.parentid, p.score
  from posts p
  where
    p.owneruserid = ##UserID## and
    p.communityowneddate is null
)

select t40.tagname as 'Tag', sum(p1.score) as 'Score',
case when sum(p1.score) >= 15 then ':-)' else ':-(' end as 'Status'
from top40 t40, myanswers p1, posttags pt1
where
  pt1.postid = p1.parentid and
  pt1.tagid = t40.id
group by t40.tagname
order by sum(p1.score) desc

1
这与Marco的答案相当;CTE只是嵌套子查询更优雅的语法。 (有一些例外,但在这种情况下是正确的)。 - John Gibb

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