我有两个表 - 国家/地区(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
但是,在我的实际场景中,我有更多的条件并加入其他表来确定哪个用户适用。通过使用此查询,我必须在两个地方具有这些条件 - 在实际选择数据的查询和计数子查询中。
有没有办法如何编写这样的查询,但没有在两个地方有这些其他条件(这可能不是很好的性能)?
答案 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}
:请将它们注入您需要的位置。