[英]Oracle - How to write this SQL?
在Oracle中,我有一张表记录用户的交易,如下表所示。 目标查询不需要用户列,只需在此处列出以供参考。
user1, transaction1, $10 <-row1
user1, transaction2, $20 <-row2
user1, transaction3, $5 <-row3
user1, transaction4, $100 <-row4
user2, ... ...
user3, ... ...
对于给定的用户,将有一个货币限额,并且我需要找出货币总和> =给定货币限额的最小行,或者如果货币限额大于总和,则找出属于该用户的所有行。 返回的行必须按事务按升序排序。
例如,对于user1,给定的资金上限为$ 30。 然后必须返回row1和row2。 您无法返回row4,因为我们必须遵循交易顺序。 如果给定的上限是$ 13,则必须返回row1和row2,因为row1不足以支付$ 13。 如果给定的上限是$ 136,则返回第1/2/3/4行,因为$ 10 + $ 20 + $ 5 + $ 100小于$ 136。
使用游标,我们可以使用存储过程来解决此问题,但是我找不到一种优雅的方法来使用一些嵌套查询和sum来实现此目的。 会非常感谢您的帮助!
您可以使用分析功能轻松完成此操作:
SELECT user_id, transaction_id, transaction_value
FROM (SELECT user_id,
transaction_id,
transaction_value,
SUM(transaction_value)
OVER (PARTITION BY user_id
ORDER BY transaction_id) AS running_total
FROM transactions)
WHERE running_total <= :transaction_cap
按照ORDER BY
子句(在这种情况下,该行的事务和所有具有较低ID的事务),以这种方式使用SUM
可以提供当前行加上所有先前的行的总和,其中PARTITION BY
子句指定的列相同。
再看一下问题,我意识到这是行不通的,因为它只会返回小于您要查找的值的值,而不是包含达到该点的值。 如果上一行小于目标总数,则以下修订版将返回当前行。
SELECT user_id, transaction_id, transaction_value
FROM (SELECT user_id,
transaction_id,
transaction_value,
running_total,
LAG(running_total)
OVER (PARTITION BY user_id
ORDER BY transaction_id) AS prior_total
FROM (SELECT user_id,
transaction_id,
transaction_value,
SUM(transaction_value)
OVER (PARTITION BY user_id
ORDER BY transaction_id) AS running_total
FROM transactions))
WHERE prior_total < :transaction_cap or prior_total is null
对于特定的上限,所有用户均应相同:
SELECT user, transaction, amount
FROM MyTable t
WHERE ( SELECT SUM(ts.amount)
FROM MyTable ts
WHERE ts.user = t.user
AND ts.transaction < t.transaction
) < @cap
ORDER BY user, transaction
根据要求,这是一个R解决方案。 我不得不做出一些假设,将它们放在一起,在这里是:
我在下面的代码中发表了很多评论,但是如果您有任何疑问,请告诉我。 我首先创建了一些代表您的数据的假数据,然后在最底部运行您需要的查询。
您可以考虑通过RODBC软件包将数据库与R接口。
#load needed package
require(plyr)
#Sed seet for reproducibility
set.seed(123)
#Make some fake data
dat <- data.frame(user = rep(letters[1:4], each = 4)
, transaction = rep(1:4, 4)
, value = sample(5:50, 16,TRUE)
)
#Separate "data.frame" or table with the money cap info
moneyCaps <- data.frame(user = letters[1:4], moneyCap = sample(50:100, 4, TRUE))
#Ensure that items are ordered by user and transcation #.
dat <- dat[order(dat$user, dat$transaction) ,]
#Merge the transaction data with the moneyCap data. This is equivalant to an inner join
dat <- merge(dat, moneyCaps)
#After the merge, the data looks like this:
user transaction value moneyCap
1 a 1 18 62
2 a 2 41 62
3 a 3 23 62
4 a 4 45 62
5 b 1 48 52
6 b 2 7 52
....
#Use the plyr function ddply to split at the user level and return values which are <=
#to the moneyCap for that individual. Note that if the first transaction for a user
#is greater than the moneyCap, then nothing is returned. Not sure if that's a possibility
#with your data
ddply(dat, "user", function(x) subset(x, cumsum(value) <= moneyCap))
#And the results look like:
user transaction value moneyCap
1 a 1 18 62
2 a 2 41 62
3 b 1 48 52
...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.