对于Oracle数据库,假设我这里有两个表(结构相似,但数据量大得多)定义如下:
create table payments(
record_no INTEGER;
cust_no INTEGER;
amount NUMBER;
date_entered DATE;
);
insert into payments values(1,3,34.5,sysdate-1);
insert into payments values(2,2,34.5,sysdate-2);
insert into payments values(3,3,34.5,sysdate-18/1440);
insert into payments values(4,1,34.5,sysdate-1);
insert into payments values(5,2,34.5,sysdate-2/24);
insert into payments values(6,3,34.5,sysdate-56/1440);
insert into payments values(7,4,34.5,sysdate-2);
insert into payments values(8,2,34.5,sysdate-1);
create table customer(
cust_no INTEGER;
name VARCHAR2;
zip VARCHAR2;
);
insert into customer values(1,'Tom',90001);
insert into customer values(2,'Bob',90001);
insert into customer values(3,'Jack',90001);
insert into customer values(4,'Jay',90001);
现在我想生成一个包含这些列的报告(通过paydate获取每个客户订单的前两个付款金额和日期):
Cust_no | pay_amount1 | pay_date1 | pay_amount2 | pay_date2
我想要的示例报告
CUST_NO PAYMENT1 PAYDATE1 PAYMENT2 PAYDATE2
1 34.5 October, 09 2013 0 null
2 34.5 October, 08 2013 34.5 October, 09 2013
3 34.5 October, 09 2013 34.5 October, 10 2013
4 34.5 October, 08 2013 0 null
任何人都可以提出正确有效的查询吗? 非常感谢。
答案 0 :(得分:3)
首先,您需要正确创建脚本。 ;
终止语句而不是语句中的一行。其次,varchar2
数据类型需要长度规范:name VARCHAR2(20)
而不是name VARCHAR2
。字符文字也需要用单引号括起来。 '90001'
是字符文字,90001
是一个数字。这是两件不同的事情。
所以这导致以下脚本:
create table payments(
record_no INTEGER,
cust_no INTEGER,
amount NUMBER,
date_entered DATE
);
insert into payments values(1,3,34.5,sysdate-1);
insert into payments values(2,2,34.5,sysdate-2);
insert into payments values(3,3,34.5,sysdate-18/1440);
insert into payments values(4,1,34.5,sysdate-1);
insert into payments values(5,2,34.5,sysdate-2/24);
insert into payments values(6,3,34.5,sysdate-56/1440);
insert into payments values(7,4,34.5,sysdate-2);
insert into payments values(8,2,34.5,sysdate-1);
create table customer(
cust_no INTEGER,
name VARCHAR2(20),
zip VARCHAR2(20)
);
insert into customer values(1,'Tom','90001');
insert into customer values(2,'Bob','90001');
insert into customer values(3,'Jack','90001');
insert into customer values(4,'Jay','90001');
请注意,不在INSERT
语句中指定列是不好的编码习惯。它应该是insert into customer (cust_no, name, zip) values(1,'Tom','90001');
而不是insert into customer values(1,'Tom','90001');
现在,对于您的查询,您需要以下内容:
with numbered_payments as (
select cust_no,
amount,
date_entered,
row_number() over (partition by cust_no order by date_entered) as rn
from payments
)
select c.cust_no,
c.name,
p1.amount as pay_amount1,
p1.date_entered as pay_date1,
p2.amount as pay_amount2,
p2.date_entered as pay_date2
from customer c
left join numbered_payments p1
on p1.cust_no = c.cust_no
and p1.rn = 1
left join numbered_payments p2
on p2.cust_no = c.cust_no
and p2.rn = 2;
请注意,我使用外部联接来确保即使没有或只有一次付款,也会退回每个客户。
这是一个包含所有更正和查询的SQLFiddle:http://sqlfiddle.com/#!4/74349/3
答案 1 :(得分:1)
SELECT * FROM (
SELECT
c.cust_no,
p.amount as payment,
p.date_entered as paydate,
ROW_NUMBER() OVER (PARTITION BY cust_no ORDER BY p.record_no ASC) AS rn
FROM customer c
JOIN payments p ON p.cust_no = c.cust_no
) t
WHERE
rn <= 2
ORDER BY cust_no, rn;
将显示您需要的每个客户端的2条记录,分为两行。如果您希望将它放在同一行中,请使用此查询:
SELECT
cust_no,
payment1,
paydate1,
CASE WHEN nextcli <> cust_no THEN 0 ELSE payment2 END AS payment2,
CASE WHEN nextcli <> cust_no THEN SYSDATE ELSE paydate2 END AS paydate2
FROM (
SELECT
c.cust_no,
p.amount as payment1,
p.date_entered as paydate1,
ROW_NUMBER() OVER (PARTITION BY c.cust_no ORDER BY p.record_no ASC) AS rn,
LEAD(c.cust_no, 1, -1) OVER (ORDER BY c.cust_no ASC) as nextcli,
LEAD(p.amount, 1, 0) OVER (ORDER BY c.cust_no ASC) as payment2,
LEAD(p.date_entered, 1, NULL) OVER (ORDER BY c.cust_no ASC) as paydate2
FROM customer c
JOIN payments p ON p.cust_no = c.cust_no
) t
WHERE
rn <= 1
ORDER BY cust_no, rn;
答案 2 :(得分:0)
分析函数ROW_NUMBER
可以帮助您为每笔付款提供一个数字:
select cust_no, amount, date_entered,
row_number() over(partition by cust_no order by date_entered ) rn
from payments ;
使用这个我认为我们可以得到你想要的东西,例如:
With ordered_payments as (
select cust_no, amount, date_entered,
row_number() over(partition by cust_no order by date_entered ) rn
from payments)
select customer.cust_no, p1.amount, p1.date_entered, p2.amount, p2.date_entered
from customer left join ordered_payments p1
on customer.cust_no = p1.cust_no and p1.rn = 1
left join ordered_payments p2
on customer.cust_no = p2.cust_no and p2.rn = 2 ;