在oracle中选择前N个不同的行而不进行内部选择

时间:2011-01-11 08:25:51

标签: sql oracle join distinct

我有类似以下结构:Table1 - >表2的关系是1:m

我需要执行类似下一个的查询:

select Table1.id from Table1 left outer join Table2 on (Table1.id1 = Table2.id2) where Table2.name like '%a%' and rownum < 11

即。我希望表1中的前10个ID满足表2中的条件。问题是我要使用distinct,但在rownum&lt;之后应用distinct子句。 11',结果可能是例如5条记录,即使它们的数量超过10条。

明显的解决方案是使用以下内容:

select id from ( select Table1.id from Table1 left outer join Table2 on (Table1.id1 = Table2.id2) where Table2.name like '%a%' ) where rownum < 11

但我害怕这种查询的表现。如果Table1包含大约300k记录,而Table2包含大约700k记录,那么这样的查询是不是真的很慢?

是否有其他查询,但没有内部选择?不幸的是,我想避免使用内部选择。

3 个答案:

答案 0 :(得分:3)

  

不幸的是,我想避免使用内心   选择

在TABLE2上使用WHERE子句,您将select过滤到INNER JOIN(即,因为Table2.name IS null&lt;&gt; Table2.name,如'%a%',您将只获得连接的结果彼此是INNER。另外,没有基于函数的索引的%a%将导致每次迭代都进行全表扫描。

但@lweller完全正确,要正确执行查询,您需要使用子查询。请记住,没有ORDER BY你不能保证你的前X记录的顺序(它可能总是'显示'值符合主键或诸如此类的东西,但不能保证。

WITH TABLE1 AS(SELECT 1 ID FROM DUAL 
               UNION ALL
               SELECT 2 ID FROM DUAL 
               UNION ALL
               SELECT 3 ID FROM DUAL 
               UNION ALL
               SELECT 4 ID FROM DUAL 
               UNION ALL
               SELECT 5 ID FROM DUAL) ,
     TABLE2 AS(SELECT 1 ID, 'AAA' NAME FROM DUAL
               UNION ALL
               SELECT 2 ID, 'ABB' NAME FROM DUAL
               UNION ALL
               SELECT 3 ID, 'ACC' NAME FROM DUAL
               UNION ALL
               SELECT 4 ID, 'ADD' NAME FROM DUAL
               UNION ALL
               SELECT 1 ID, 'BBB' NAME FROM DUAL
               ) ,
     sortable as( --here is the subquery
         SELECT
            Table1.ID ,
            ROW_NUMBER( ) OVER (ORDER BY Table2.NAME NULLS LAST) ROWOverName , --this wil handle the sort
            table2.name
           from
            Table1
            LEFT OUTER JOIN  --this left join it moot, pull the WHERE table2.name into the join to have it LEFT join as expected
            Table2
             on
            (
                Table1.id = Table2.id
            )
          WHERE
          Table2.NAME LIKE '%A%')
    SELECT * 
      FROM sortable
     WHERE ROWOverName <= 2; 

- 您可以删除ROW_NUMBER()分析函数并替换最终查询(如您最初指示的那样)

SELECT * 
  FROM sortable
 WHERE 
       ROWNUM <= 2
 ORDER BY sortable.NAME  --make sure to put in an order by!
 ;

答案 1 :(得分:2)

这里根本不需要DISTINCT,子查询也没有什么不好。

SELECT  id
FROM    Table1
WHERE   id IN
        (
        SELECT  id
        FROM    Table2
        WHERE   name LIKE '%a%'
        )
        AND rownum < 11

请注意,订单无法保证。为了保证订单,您必须使用嵌套查询:

SELECT  id
FROM    (
        SELECT  id
        FROM    Table1
        WHERE   id IN
                (
                SELECT  id
                FROM    Table2
                WHERE   name LIKE '%a%'
                )
        ORDER BY
                id -- or whatever else
        )
WHERE   rownum < 11

没有嵌套查询(或CTE),就无法做到这一点。

答案 2 :(得分:1)

对我来说,没有理由害怕表现。我认为子选择是解决问题的最佳方法。如果您不想相信我,请查看您的查询的解释计划,您会发现它的行为并不像您想象的那么糟糕。