2017-06-16 81 views
2

我有两个表 - 国家(id,name)和用户(id,name,country_id)。每个用户都属于一个国家。我想从同一个随机国家中选择10个随机用户。但是,有些用户少于10个的国家/地区,因此我无法使用它们。我只需要选择那些至少有10个用户的国家。Oracle - 优化SQL查询

我可以写这样的事:

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 

然而,在我的实际情况,我有更多的条件,并加入到其他表以确定哪些用户是适用的。通过使用这个查询,我必须在两个地方有这些条件 - 在查询中实际选择数据并在count子查询中。

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

+0

你可以为'{MANY_OTHER_JOINS_AND_CONDITIONS}'创建一个视图... –

+0

嗨,我认为在这里优化的最佳方式是使用正确的连接,有时嵌套查询代价高昂。而不是嵌套查询使用左或内部联接取决于需要。 –

+0

@UsagiMiyamoto为了任何人长期维护这一点 - 不要在视图中隐藏复杂性 – Palcente

回答

5

您可以使用CTE作为用户标准,以避免重复逻辑并允许DB将该设置缓存一次(尽管根据我的经验,数据库并不像应该那样好,因此请检查您的执行计划)。

我更比甲骨文在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 

而且一步,这个查询是突然更容易阅读和维护。

+0

谢谢,这工作像一个魅力。更可读,更快,并返回正确的结果。感谢我向CTE介绍我,我从未接触过这个。我已经喜欢它了:) –

0

你可以创建一个视图 为

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

部份寻找到您的查询 你可以使用具有代替查询外的地方
的顺序似乎可以放置在内部查询内部
所以过滤器为一排

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 
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}:请注入他们回来,你需要他们。