昨天业务提了个需求,一下子把我给难住了,想了两小时没想出来(为毛奇葩的需求老是提到俺们这边,俺真心不是万能的啊),只好求助万能的itpub,大牛给了些不错的思路。现总结如下
需求简化后的版本如下
有这么一张表如下:
订单号 商品号 付款时间 用户
1 A 11.27 甲
1 B 11.27 甲
2 B 12.2 乙
3 C 12.3 甲
订单号 商品号 付款时间 用户
1 A 11.27 甲
1 B 11.27 甲
2 B 12.2 乙
3 C 12.3 甲
订单商品表,订单号一样的,付款时间、用户也保证一样。
现在要求找出那些在一个月内(30天)买过三种不同商品的客户。一个月是指连续的一个月,不是指自然月。
比如这个例子中,甲27号买了2个商品,3号又买了1个商品,所以他在连续一个月内买了三个不同的商品,需要把他取出来。
现在要求找出那些在一个月内(30天)买过三种不同商品的客户。一个月是指连续的一个月,不是指自然月。
比如这个例子中,甲27号买了2个商品,3号又买了1个商品,所以他在连续一个月内买了三个不同的商品,需要把他取出来。
这个需求的难点在于时间要求连续而非自然月,意味着不能按月进行group by。
不罗嗦了,先建测试表
create table temp
(
order_id number,
goods_id varchar2(100),
pay_time date,
user_id varchar2(100)
);
insert into temp values(1,'A',date'2013-11-27','甲');
insert into temp values(1,'B',date'2013-11-27','甲');
insert into temp values(2,'B',date'2013-12-2','乙');
insert into temp values(3,'C',date'2013-12-3','甲');
最先想到的思路是取一天,然后查看它后面30天的商品购买情况,不断循环往后取,直到满足条件或者数据取完。这个貌似不能用一句sql就能解决了,所以就写了个存储过程,按本示例改写后如下:
create or replace procedure zybiro.p_temp
is
start_date date;
end_date date;
v_user_id varchar2(100);
begin
select trunc(min(pay_time)),trunc(max(pay_time)) into start_date,end_date from temp;
loop
exit when start_date>=end_date;
begin
select user_id into v_user_id from temp where trunc(pay_time)<=start_date+30 and trunc(pay_time)>=start_date
group by user_id having count(distinct goods_id)>=3;
exception
when others then
v_user_id:='null';
end;
if v_user_id <> 'null' then
dbms_output.put_line('user_id is : '||v_user_id);
end if;
start_date:=start_date+1;
end loop;
commit;
end;
这里为方便起见用v_user_id变量直接输出。实际环境是不能这么搞的,因为主语句一次输出可能不止一个。正确的搞法是创建张临时表把数据都导进去。
不管怎么说,证明这种方式是可行的。记得先运行set serveroutput on;

PUB上有类似的一道题目
用了一堆分析函数解决。貌似我自己还没研究透,有时间再看看补充进去