从oracle

时间:2017-06-20 09:03:20

标签: oracle select random greatest-n-per-group

我正在创建一个视图,需要为每个客户只选择一个随机行。类似的东西:

select c.name, p.number
from customers c, phone_numbers p
where p.customer_id = c.id

如果此查询返回:

NAME      NUMBER
--------- ------
Customer1 1
Customer1 2
Customer1 3
Customer2 4
Customer2 5
Customer3 6

我需要它像:

NAME      NUMBER
--------- ------
Customer1 1
Customer2 4
Customer3 6

Rownum不会工作,因为它只会从所有6条记录中选择第一条记录,而我需要每个客户的第一条记录。我需要的解决方案不会对性能产生太大影响,因为选择数据的查询非常复杂,这只是解释我需要的一个例子。提前谢谢。

3 个答案:

答案 0 :(得分:1)

使用ROW_NUMBER()分析函数:

SELECT name,
       number
FROM   (
  SELECT c.name,
         p.number,
         ROW_NUMBER() OVER ( PARTITION BY c.id ORDER BY DBMS_RANDOM.VALUE ) AS rn
  FROM   customers c
         INNER JOIN phone_numbers p
         ON ( p.customer_id = c.id )
)
WHERE  rn = 1

答案 1 :(得分:1)

您可以使用group by子句只返回一个电话号码:

select c.name, MAX(p.number) as phone
from customers c, phone_numbers p
where p.customer_id = c.id
group by c.name

答案 2 :(得分:1)

您还可以将最小或最大聚合函数与the keep dense_rank syntax

一起使用
select c.name,
  min(p.number) keep (dense_rank last order by dbms_random.value) as number
from customers c
join phone_numbers p on p.customer_id = c.id
group by c.id, c.name
order by c.name;

number不是有效的列或别名,因为它是一个保留字,所以当然请使用您自己的真实姓名。)

如果电话号码需要是任意的而不是实际随机的,您可以通过其他方式订购:

select c.name,
  min(p.number) keep (dense_rank last order by null) as number
from customers c
join phone_numbers p on p.customer_id = c.id
group by c.id, c.name
order by c.name;

您可能每次都会为每个客户返回相同的号码,但并非总是如此,数据/统计/计划更改会影响您看到的内容。看起来你似乎并不在乎。但是,使用普通聚合对你来说也可能同样有效,就像在@答案中一样。

如果您从该随机行中获取大量列,而不仅仅是电话号码,则MTO的子查询可能更简单;对于一个或两个值,我发现这更清楚了。