Oracle - 优化SQL查询

时间:2017-06-16 14:14:54

标签: sql oracle

我有两个表 - 国家/地区(ID,名称)和用户(id,name,country_id)。每个用户属于一个国家。我想从同一个随机国家/地区中选择10个随机用户。但是,有些国家的用户数不足10个,因此我无法使用它们。我只需要选择那些拥有至少10位用户的国家/地区。

我可以这样写:

SELECT * FROM(
   SELECT * 
   FROM users u
   {MANY_OTHER_JOINS_AND_CONDITIONS}
   WHERE u.country_id = 
  (
    SELECT * 
    FROM
    (
      SELECT c.id 
      FROM countries c
      JOIN 
      (
        SELECT users.country_id, COUNT(*) as cnt 
        FROM users 
        {MANY_OTHER_JOINS_AND_CONDITIONS}
        GROUP BY users.country_id
      ) X ON X.country_id = c.id 
      WHERE X.cnt >= 10 
      ORDER BY DBMS_RANDOM.RANDOM
    ) Y
    WHERE ROWNUM = 1
  ) 
  ORDER BY DBMS_RANDOM.RANDOM
) Z WHERE ROWNUM < 10

但是,在我的实际场景中,我有更多的条件并加入其他表来确定哪个用户适用。通过使用此查询,我必须在两个地方具有这些条件 - 在实际选择数据的查询和计数子查询中。

有没有办法如何编写这样的查询,但没有在两个地方有这些其他条件(这可能不是很好的性能)?

3 个答案:

答案 0 :(得分:5)

您可以将CTE用作用户标准,以避免重复逻辑并允许DB缓存该设置一次(尽管根据我的经验,数据库不是那么好,所以检查执行情况计划)。

我比Oracle更像是一个Sql Server人员,语法略有不同,所以这可能需要一些调整,但试试这个:

WITH SafeUsers (ID, Name, country_id) As
(
   --criteria for users only has to specified here
   SELECT ID, Name, country_id
   FROM users
   WHERE ... 
),
RandomCountry (ID) As 
( 
    SELECT ID 
    FROM (
        SELECT u.country_id AS ID
        FROM SafeUsers u -- but we reference it HERE
        GROUP BY u.country_id
        HAVING COUNT(u.Id) >= 10
        ORDER BY DBMS_RANDOM.RANDOM
    ) c
    WHERE ROWNUM = 1
)
SELECT u.* 
FROM (
    SELECT s.*        
    FROM SafeUsers s -- and HERE
    INNER JOIN RandomCountry r ON s.country_id = r.ID
    ORDER BY DBMS_RANDOM.RANDOM
) u
WHERE ROWNUM <= 10

通过删除嵌套并为每个中间步骤引入名称,此查询突然 更容易阅读和维护。

答案 1 :(得分:0)

你可以创建一个视图 为了

  create view user_with_many_cond as 
  SELECT * 
  FROM users u
  {MANY_OTHER_JOINS_AND_CONDITIONS}

期待您的查询 你可以使用而不是在查询之外的地方
order by似乎可以放在内部查询中 所以过滤一行

  SELECT * FROM(
     SELECT * 
     FROM user_with_many_cond u
     WHERE u.country_id = 
    (
        SELECT c.id 
        FROM countries c
        JOIN 
        (
          SELECT users.country_id, COUNT(*) as cnt 
          FROM user_with_many_cond
          GROUP BY users.country_id
          HAVING cnt >=10
          ORDER BY DBMS_RANDOM.RANDOM
        ) X ON X.country_id = c.id 
        WHERE ROWNUM = 1
    ) 
    ORDER BY DBMS_RANDOM.RANDOM
  ) Z WHERE ROWNUM < 10

答案 2 :(得分:0)

要获得超过10位用户的国家/地区:

  SELECT users.country_id
          , row_number() over (order by dbms_random.value()) as rn 
  FROM users 
  GROUP BY users.country_id having count(*) > 10

使用此选项作为子查询来选择国家/地区并抓住一些用户:

with ctry as (
  SELECT users.country_id
          , row_number() over (order by dbms_random.value()) as ctry_rn 
  FROM users 
  GROUP BY users.country_id having count(*) > 10
)
  , usr as (
      select user_id
             ,  row_number() over (order by dbms_random.value()) as usr_rn 
      from ctry
      join users 
           on users.country_id = ctry.country_id
      where ctry.ctry_rn = 1
) 
select users.*
from usr
     join users 
     on users.user_id = usr.user_id
where usr.usr_rn <= 10
/  

此示例忽略了您的{MANY_OTHER_JOINS_AND_CONDITIONS}:请将它们注入您需要的位置。