How can I return a count of how many orders fall within incremental ranges? - sql

I have columns called order_id and purchase_amount, and I need to write a query to count how many orders fall within each incremental range of $100, along with the values of the range. For example, it has to return something like 12 orders are between $0-100, 9 orders are between $101-200 and continuing on that way, increasing by $100 each time, like below. And I'm stumped how to begin.
Count | Range
12 | $0-100
9 | $101-200

Look at using SQL function SUM combined with CASE to create a condition.
Select SUM(CASE WHEN OrderValue >= 0 AND OrderValue < 100 THEN 1 ELSE 0 END) AS Count FROM Table;
That should give you a starting point.

You didn't provide any source table data sample so I did use the standard table Orders from TPCH to build the following:
select
case
when o_totalprice < 1000 then '1k-'
when o_totalprice >= 1000 and o_totalprice < 5000 then '1k-5k'
when o_totalprice >= 5000 and o_totalprice < 10000 then '5k-10'
when o_totalprice >= 10000 and o_totalprice < 20000 then '10k-20k'
when o_totalprice >= 20000 and o_totalprice < 50000 then '20k-50k'
else '50k+'
end as 'Range',
count(*) as 'Range-Count'
from
orders
group by 1
;
Hope this answer your question...

Related

How can I create multiple groups within a column using SQL

I have a data set that looks like the below
follower_count
5,689
15,389
215,869
53,389
28,389
9,389
I essentially want to create 3 different groups.
Group 1: Follower_count between 0-25,000 called micro
Group 2: Follower_count between 25,001 to 50,0000 called mid
Group 3: Follower_count between 50,001 - 300,000 called macro
This is the code I have so far
SELECT pic.followers_count, AVG(engagement_rate / 1000000) AS avgER
FROM `public_instagram_channels` AS pic
WHERE pic.followers_count BETWEEN 0 and 25000 AS micro,
pic.followers_count BETWEEN 25001 and 50,000 AS mid,
pic.followers_count BETWEEN 50,001 - 300,000 as macro
GROUP BY micro, mid, macro
You would use a case expression:
select (case when follower_count < 25000 then 'micro'
when follower_count < 50000 then 'mid'
when follower_count < 300000 then 'maccro'
else 'other'
end) as grp, count(*),
avg(engagement_rate / 1000000)
from `public_instagram_channels` p
group by grp;

Extracting account's transactions with no other transactions with this counterparty

I have a database with transactions of accounts. The relevant columns for me are: Account,Amount, Date, description and Transaction_Code.
My goal is to extract rows for a given account which meets my trigger points.
The trigger points which I've succeeded writing are Amount greater than 200 and Transaction_Code in ('1,'2','3').
the only trigger point I'm struggling with is that: The account has no other transactions with this counterparty in the last 21 days. I've only succeeded in taking the range of dates I need.
Example for the Dataset:
**Account** **Amount** **Date** **Description** **Transaction_Code**
555 280 2019-10-06 amt_fee 1
555 700 2019-09-20 refund 2
555 250 2019-10-01 amt_fee 1
snippet of sql I wrote for the example for better understanding:
select Account, Amount, Date, Description
from MyTable
where Account = '555' and Date between '2019-09-15' and '2019-10-06'
and Amount >= 200
and Transaction_Code in ('1','2','3')
The problem I have is how to do the condition of: ''The account has no other transactions with this counterparty in the last 21 days.'' Counterparty refers to the Description or Transaction_Code columns.
How should I do that condition for my true larger dataset? with groupby and count distinct?
You could add a not exists condition with a correlated subquery that ensures that the same Account did not have a transaction with the same Description or Transaction_Code within the last 21 days.
select Account, Amount, Date, Description
from MyTable t
where
Account = '555' and Date between '2019-09-15' and '2019-10-06'
and Amount >= 200
and Transaction_Code in (1, 2, 3)
and not exists (
select 1
from MyTable t1
where
t1.Account = t.Account
and (t1.Description = t.Description or t1.Transaction_Code = t.Transaction_Code)
and t1.date < t.date
and t1.date >= dateadd(day, -21, t.date)
)

Grouping twice by different criteria, same column

I have data with the following columns:
OFFICER_ID, CLIENT_ID, SECURITY_CODE, POSITION_SIZE
and then per each row for instance:
officer1, client100, securityZYX, $100k,
officer2, client124, securityADF, $200k,
officer1, client130, securityARR, $150k,
officer4, client452, securityADF, $200k,
officer2, client124, securityARR, $500k,
officer7, client108, securityZYX, $223k,
and so on.
As you see, each client has a single officer assigned to either buy o sell securities, but each client can have bought different securities.
Apart from ranking officers by total amount in US$ of securities held by their clients (which I've done) I need to create ranges of total client accounts by adding total securities held by client ID, for example, total securities held sum < $1million, between $1million and $3million and > $3 million.
I've tried:
SELECT officer_ID, SUM(position_size) as AUM
FROM trades
GROUP BY client_ID
HAVING AUM > 1000000 AND AUM < 3000000;
and I get a list of all officers appearing several times, no totals.
I'd need a simple:
Officer_ID | range < 1m | range 1m-3m | range > 3m
officer1, [total amount of client accounts with securities adding up < 1m totals], [total amount of client accounts with securities adding up between 1m and 3m totals], etc.
Please, would you point me in the right direction?
UPDATE
I modified Tim's suggested code and obtained the desired output:
SELECT
OFFICER_ID,
SUM(CASE WHEN total < 1000000 THEN total END) AS "range < 1m",
SUM(CASE WHEN total >= 1000000 AND total < 3000000 THEN total END) AS "range 1m-3m",
SUM(CASE WHEN total >= 3000000 THEN total END) AS "range > 3m"
FROM
(
SELECT OFFICER_ID, CLIENT_ID, SUM(POSITION_SIZE) AS total
FROM trades
GROUP BY OFFICER_ID, CLIENT_ID
) t
GROUP BY
OFFICER_ID;
Too kind, Tim, thanks!
We can try aggregating twice, first by both officer and client, to get the client totals, and a second time by officer alone, to get the counts:
SELECT
OFFICER_ID,
COUNT(CASE WHEN total < 1000000 THEN 1 END) AS "range < 1m",
COUNT(CASE WHEN total >= 1000000 AND total < 3000000 THEN 1 END) AS "range 1m-3m",
COUNT(CASE WHEN total >= 3000000 THEN 1 END) AS "range > 3m"
FROM
(
SELECT OFFICER_ID, CLIENT_ID, SUM(POSITION_SIZE) AS total
FROM trades
GROUP BY OFFICER_ID, CLIENT_ID
) t
GROUP BY
OFFICER_ID;

SQL Increment column value in select statement

I'm trying to write a Select statement that increments a column value by 50, but the range can end up being 200,000 so I can't do it all in a case statement manually.
Something similar to this, but instead of manually writing the increments
Select count(order_id) as order_count,
case when revenue between 0 and 50 then ‘$50’
when order_value between 51 and 100 then ‘$100’
else ‘over $101’
end as revenue_bucket
from Orders
group by 2
Turn your revenue into the bucket value, then make string out of it:
SELECT count(order_id) AS order_count,
'$' || ((((revenue - 0.01)/50)::int + 1) * 50)::text AS revenue_bucket
FROM Orders
GROUP BY 2;
This obviously runs well past $200,000.
You can work with modulo to get this. Limit would be 101 in your example. All you have to do, is cast the result in a string and add the $ before it
Select count(order_id) as order_count,
case when revenue < limit then revenue - (revenue % 50) + 50
else ‘over $101’
end as revenue_bucket
from Orders
group by 2
You can round to the nearest 50 with div (integer division):
revenue div 50 * 50
To round up instead of down:
(revenue div 50 + 1) * 50
To include 50 in the next bracket (so 50 as $50 instead of $100):
((revenue-1) div 50 + 1) * 50
Example query:
select revenue
, concat('$', ((revenue-1) div 50 + 1) * 50) as revenue_bucket
from YourTable
See it working at SQL Fiddle.
figured out something similar
select floor((revenue+49)/50)*50 as revenue_bucket,
count(1) as order_count
from Orders
group by 1;

Using COUNT and UNION to extract data and support a scenario

I am dealing with a database with thousands of customers.
I am wanting to find groups of single customers who have exactly ONE qualifying discount voucher which is valid and exactly ONE non-qualifying voucher which is valid.
A qualifying voucher is one that has a minimum spend amount of £0.01 or more.
A non-qualifying voucher is one that does not have a minimum spend and is therefore £0.00
'Valid' refers to the 'from' date being today or before and the 'to' date being today or in the future
I have initially set up the query below but all this is doing is searching for all customers who have valid qualifying AND non-qualifying voucherS. I am trying to find customers who have JUST ONE valid qualifying voucher and JUST ONE non-qualifying voucher:
select CustomerId, VoucherId, MinimumSpendAmount, ValidFromDate, ValidToDate
from dbo.discountvoucher
where ValidFromDate <= 15/11/2013
and ValidToDate >= 15/11/2013
order by CustomerId
I think I need to split this into 2 separate SELECT statements, one looking for single customers with 1 qualifying voucher (using COUNT), and one looking for single customers with 1 non-qualifying voucher (using COUNT). And then combining them with a UNION. But I could be totally wrong...
Please can anybody help
You can use a sub select with a GROUP BY and HAVING CLAUSE to find the customers that match your criteria.
select CustomerId, VoucherId, MinimumSpendAmount, ValidFromDate, ValidToDate
from dbo.discountvoucher
where ValidFromDate <= 15/11/2013
and ValidToDate >= 15/11/2013
and CustomerId in
(select CustomerId
from dbo.discountvoucher
where ValidFromDate <= 15/11/2013
and ValidToDate >= 15/11/2013
group by CustomerId
having sum(case when MinimumSpendAmount > 0 then 1 else 0 end) = 1
and sum(case when MinimumSpendAmount = 0 then 1 else 0 end) = 1
)
order by CustomerId