-2

Let's say I have a table TRANSACTIONS:

desc customer_transactions; 
+------------------------------+--------------+------+-----+---------+----------------+
| Field                        | Type         | Null | Key | Default | Extra          |
+------------------------------+--------------+------+-----+---------+----------------+
| id                           | int(11)      | NO   | PRI | NULL    | auto_increment |
| transactionID                | varchar(128) | YES  |     | NULL    |                |
| customerID                   | varchar(128) | YES  |     | NULL    |                |
| amountAuthorized             | DECIMAL(5,2) | YES  |     | NULL    |                |
| createdDatetime              | datetime     | YES  |     | NULL    |                |
+------------------------------+--------------+------+-----+---------+----------------+

This table has records of credit card transactions for a SAAS business for the last 5 years. The business has a typical monthly subscription model, where customers automatically charged based on their plan.

I need to find the top customers that are responsible for 80% of all revenue (per time period). The SAAS business is very uneven, because some customers pay 10/month, others may pay in thousands per month.

I will add a "time period" filter later, just need help with aggregation.

I want to generate a report where I only select the customers that generated 80% of revenue in this format:

+------------+-------+
| customerID | Total | 
+------------+-------+

Not sure why this question was "on hold". I just need help writing a query and do not have enough experience with SQL. Basically, the title of the question states what is needed here:

I need to list customers and their corresponding totals, however, only need to select those customers that make up 80% of total revenue. The report needs to aggregate a total per customer.

Using MariaDB version 10.3.9

Rick James
  • 135,179
  • 13
  • 127
  • 222
ipolevoy
  • 5,432
  • 2
  • 31
  • 46
  • How does one calculate "percentage of revenue" using this data? I don't see any numeric type on the table except for the `id`. Also 80% of what revenue? 80% of the total revenue? That could only be 0 or 1 customer by definition as any other customers would have generated less than 20% of total if there is one customer that generated 80% or more. – JNevill Sep 10 '18 at 17:33
  • what is the logic behind calculating revenue for a custoemr ? – Madhur Bhaiya Sep 10 '18 at 17:33
  • Edit the question add some more information about the current problem, where you stuck, show us your current attempt. – Yogesh Sharma Sep 10 '18 at 17:33
  • 1
    Also, what DBMS are you using (mysql, sql server, oracle, db2, postgres, etc.)? – JNevill Sep 10 '18 at 17:36
  • why amount is varchar ! – lagripe Sep 10 '18 at 17:41
  • apologies to all, the amount was a `varchar` because of a typo. Fixed now – ipolevoy Sep 11 '18 at 18:15

3 Answers3

0

This is the kind of thing you need to use window functions for.

WITH 
    -- define some sample data, 
    -- where the sum total of amountAuthorized is 10,000
    customer_transactions(  `id`, transactionID, customerID, 
                            amountAuthorized, createdDatetime) AS
    (
                    SELECT 1, 1, 1, 5000, '2018-08-01'
        UNION ALL   SELECT 2, 2, 2, 2000, '2018-08-01'
        UNION ALL   SELECT 3, 3, 3, 1000, '2018-08-01'
        UNION ALL   SELECT 4, 4, 4, 1000, '2018-08-01'
        UNION ALL   SELECT 5, 5, 5, 1000, '2018-08-01'
    )
    -- a query that gives us the running total, sorted to give us the biggest customers first.
    -- note that the additional sorts affect what customers might be returned.
    ,running_totals AS
    (
        SELECT *, SUM(amountAuthorized) OVER (ORDER BY amountAuthorized DESC, createdDatetime DESC, `id`)  AS runningTotal
        FROM customer_transactions
    )
SELECT *
FROM running_totals
WHERE runningTotal <= ( SELECT 0.8 * SUM(amountAuthorized) 
                        FROM customer_transactions)

Note that this takes into account (no pun intended) all data in the table. When you want to only look at a specific time period, you might want to create an intermediate CTE that filters out the dates you want.

Zack
  • 2,220
  • 1
  • 8
  • 12
-1

You will find that surprisingly close 20% of the customers account for the 80%. See the 80/20 rule .

But, if you don't want to go that direction, you have 2 options:

  • Switch to MySQL 8.0 or MariaDB 10.1 in order to use 'windowing' functions; or
  • Use @variables to produce a running total, then (in an outer query) grab the desired rows.

Since you are using MariaDB 10.3.9, the windowing seems to be the way to go. But first, you need a separate query (or derived table) that computes the total revenue so you can get 80% of it.

Suggest

 SELECT @revenue80 := 0.8 * SUM(amountAuthorized) 
     FROM customer_transactions

Then use @revenue80 inside the WHERE that Zack suggests.

I see that each amount can be no more than 999.99. Really? Is this a coffee shop?

Rick James
  • 135,179
  • 13
  • 127
  • 222
  • thanks for suggestions, I was not familiar with window functions. The amounts at 999.99 is a coincidence. This is for a SaaS business, which can have bigger transactions. Besides 999.99 seems a but too much for a late, right ? lol – ipolevoy Oct 04 '18 at 04:57
  • Maybe for a latte with gold flakes floating on it? – Rick James Oct 04 '18 at 15:27
-2

Use the following:

SELECT 
  ct1.customerID, 
  SUM(ct1.amountAuthorized) as Total, 
  100 * (SUM(ct1.amountAuthorized) / ct3.total_revenue) as percent_revenue 
FROM 
  customer_transactions ct1
CROSS JOIN (SELECT SUM(amountAuthorized) AS total_revenue 
            FROM customer_transactions ct2) AS ct3 
GROUP BY 
  ct1.customerID 
HAVING percent_revenue >= 80
Madhur Bhaiya
  • 28,155
  • 10
  • 49
  • 57
  • 1
    `amountAuthorized` is defined as VARCHAR(128). How do you SUM and perform division on a character string? Also, even if the column was numeric, your logic is wrong. It would only be correct if a single customer was more than 80% of the revenue. – Ken White Sep 10 '18 at 17:38
  • 1
    I just can't imagine that OP is really looking for a customer that generated, all by itself, 80% of all revenue in this table. It would only be one or no customer returned which would be a silly query. – JNevill Sep 10 '18 at 17:39
  • @KenWhite added Cast to convert varchar to double – Madhur Bhaiya Sep 10 '18 at 17:39
  • sorry for a typo, guys. I changed the amount to DECIMAL. – ipolevoy Sep 11 '18 at 18:16
  • @ipolevoy did you run the query in my answer ? It should work nonetheless – Madhur Bhaiya Sep 11 '18 at 18:17
  • @MadhurBhaiya, there was a typo in your query. However, after fixing it, there is no luck: `SELECT customerID, SUM(amountAuthorized) as total, 100 * SUM(amountAuthorized) / SELECT(SUM(amountAuthorized) FROM customer_transactions) AS percent_revenue FROM customer_transactions GROUP BY customerID HAVING percent_revenue >= 80; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'SELECT(SUM(amountAuthorized) FROM customer_transactions) AS percent_revenue FROM' at line 1` – ipolevoy Sep 11 '18 at 18:37
  • @ipolevoy What was the typo ? – Madhur Bhaiya Sep 11 '18 at 18:44
  • @MadhurBhaiya, not sure. I made some improvements to the query but still not working `SELECT customerID, SUM(amountAuthorized) as total, (100 * total / SELECT SUM(amountAuthorized) FROM customer_transactions) percent_revenue FROM customer_transactions GROUP BY customerID HAVING percent_revenue >= 80; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'SELECT SUM(amountAuthorized) FROM customer_transactions) percent_revenue FROM cu' at line 1 ` – ipolevoy Sep 11 '18 at 20:53
  • @ipolevoy can you provide a SQL fiddle ? I think mariadb does not allow this; although this works for me in MySQL – Madhur Bhaiya Sep 12 '18 at 03:41
  • @ipolevoy I have edited the query in my answer. Try the following: `SELECT ct1.customerID, SUM(ct1.amountAuthorized) as Total, 100 * (SUM(ct1.amountAuthorized) / ct3.total_revenue) as percent_revenue FROM customer_transactions ct1 CROSS JOIN (SELECT SUM(amountAuthorized) AS total_revenue FROM customer_transactions ct2) AS ct3 GROUP BY ct1.customerID HAVING percent_revenue >= 80` – Madhur Bhaiya Sep 12 '18 at 05:41
  • @MadhurBhaiya, this query is not causing issues, but also is not bringing any results. At this point, there is no need to work on it further, as I already implemented all logic in code, outside SQL. Thanks for the effort! – ipolevoy Sep 13 '18 at 21:41