Reputation: 1800
I got totally stumped by this skill assessment question. The skill assessment is done. I am too old to use SO to cheat my way through... Just curious how to solve this.
You have a table with the following columns:
Sender | Recipient | Date | Amount
How would you select all Recipients that each have a sum of ANY 3 or fewer amounts that is greater than or equal to X?
For example:
Sender | Recipient | Date | Amount
--------+-----------+------------+-------
William | Jane | 2016-05-27 | $1243
Sarah | Josh | 2016-05-12 | $500
Rohit | Tammy | 2016-05-24 | $200
Jacob | Josh | 2016-05-17 | $500
Abraham | Josh | 2016-05-15 | $10
Marie | Vivian | 2016-05-16 | $1243
Alex | Josh | 2016-05-07 | $150
If X = $1024, you should get Jane, Vivian and Josh. Josh, because $500 + $500 + $150 > $1024.
SOLUTION All the three solutions worked. Thanks so much guys. Really appreciate it.
Chose @Tim Biegeleisen's answer for the simplicity factor.
Upvotes: 3
Views: 856
Reputation: 1
;
WITH cte
AS (SELECT
t.amount,
t.recipient,
1 AS cnt
FROM transfers t
UNION ALL
SELECT
t1.amount + t2.amount,
t2.recipient,
cnt + 1
FROM cte t2
INNER JOIN transfers t1
ON t1.recipient = t2.recipient
AND t1.amount + t2.amount <= 1024
WHERE cnt <= 2)
SELECT
*
FROM cte
WHERE amount >= 1024
Upvotes: -2
Reputation: 521409
One trick which will work is to generate a row number for each Amount
, for each recipient
. Then we can simply restrict to the greatest three amounts for each group using a simple WHERE
condition.
SELECT Recipient, SUM(Amount)
FROM test t
WHERE (SELECT 1 + COUNT(*)
FROM test
WHERE Amount >= t.Amount AND Recipient = t.Recipient AND
date < t.date) <= 3
GROUP BY Recipient
HAVING SUM(Amount) >= 1024
This solution uses the transaction date to break ties (e.g. in the case of Josh receiving $500 in two different records). This solution is not robust to the use case of a recipient having two transactions for the same amount on the same day. One nice thing about this solution is that it does not require the use of any user defined variables.
Upvotes: 3
Reputation: 350300
You could use variables to number records per receiver in order of descending amount, and then filter for those records that have received a number not more than 3:
select receiver,
sum(amount)
from (select @num := if(@rec = receiver, @num + 1, 1) as rn,
@rec := receiver as receiver,
amount
from transaction
order by receiver,
amount desc) as numbered
where rn <= 3
group by receiver
having sum(amount) >= 1024
The nice thing about this solution is that it uses variables to avoid the base table being queried twice, which on large data sets can have a huge performance impact.
Also, it guarantees that at most 3 records will be returned per receiver, even if in the third place there would be a tie: you'll never get 4.
Upvotes: 2
Reputation: 25842
What you can do is grab the top 3 of every recipient. and if their sum is greater than the set limit then you can return their names
SET @x = 1024;
SELECT t.Recipient
FROM (
SELECT
Sender,
Recipient,
Amount,
SUM(Amount) as total_amount,
@count := if(@rec = Recipient, @count + 1, 1) as counter,
@rec := Recipient
FROM test
CROSS JOIN (SELECT @count := 1, @rec := '') t
GROUP BY Recipient
HAVING counter <= 3
ORDER BY Recipient, Amount desc
) t
WHERE t.total_amount >= @x;
Upvotes: 1