Top 2 offers with sum of all offers - sql

Can someone please tell how can I get the results as below.
Using dense_rank function where rank <=2 will give me top 2 offers.
I am also looking to get 'total_offer' which should be sum of 'offer1' and 'offer2'. when there is no offer2 ( eg:taurus) 'total offer' should be 'offer1' and 'null' for 'offer2'
Input:
customer make zipcode offer notes
mark focus 101 250 cash
mark focus 101 2500 appreciation cash
mark focus 101 1000 cash
mark focus 101 1500 cash offer
henry 520i 21405 500 cash offer
henry 520i 21405 100 cash
henry 520i 21405 750 appreciation cash
henry 520i 21405 100 cash
mark taurus 48360 250 appreciation cash
mark mustang 730 500 cash
mark mustang 730 1000 Cash offer
mark mustang 730 1250 appreciation cash
Desired Output:
| CUSTOMER | MAKE | ZIPCODE | TOP_OFFER1 | notes1 | TOP_OFFER2 | notes2 | Total_offer |
| henry | 520i | 21405 | 750 | appreciation cash | 500 | cash offer | 1250
| mark | focus | 101 2500 | appreciation cash | 1500 | cash offer | 4000
| mark | mustang | 730 | 1250 | appreciation cash | 1000 | cash offer | 2250
| mark | taurus | 48360 | 250 | appreciation cash | NULL | 250 |
Thanks
PS:
The link below tells me that dense_rank need to be performed to get top 2 offers.
(Using a pl-sql procedure or cursor to select top 3 rank)

with x as
(select row_number() over(partition by customer,make order by offer desc) rn,
customer, make, zipcode, offer from tablename)
, y as (select customer, make, zipcode, offer from x where rn <=2)
, z as (select customer, make, zipcode,
case when rn = 1 then offer else 0 end as offer_1,
case when rn = 2 then offer else 0 end as offer_2
from y)
select customer, make, zipcode, offer_1, offer_2, offer_1+offer_2 total_offer
from z
This makes use of recursive cte's to accomplish your task.

Try this code...
WITH subqueryfactoring AS
(SELECT customer,
make ,
zipcode ,
offer,
lead(offer) over (partition BY customer, make , zipcode order by offer DESC) SecondLeadValue,
row_number() over (partition BY customer, make , zipcode order by offer DESC ) rownumber
FROM abc1
)
SELECT customer,
make ,
zipcode,
offer "Top Offer1 ",
SecondLeadValue "Top offer 2",
offer + COALESCE(SecondLeadValue,0) "Total Offer"
FROM subqueryfactoring
WHERE rownumber<2;

You better use ROW_NUMBER instead of DENSE_RANK (which might return more than two rows) plus a LEAD to find the 2nd highest value
select customer, make, zipcode,
TOP_OFFER1,
TOP_OFFER2,
TOP_OFFER1 + coalesce(TOP_OFFER2,0) as Total_offer
from
(
select customer, make, zipcode,
offer as TOP_OFFER1, -- max offer
lead(offer) over (partition by customer, make, zipped
order by offer desc) as TOP_OFFER2, -- 2nd highest offer
row_number() over (partition by customer, make, zipped
order by offer desc) as rn
from tab
) dt
where rn = 1 -- only the row with the highest offer

Related

Add a subtotal column for aggregated columns

Here's my dataset of trades, traders and counterparties:
TRADER_ID | TRADER_NAME | EXEC_BROKER | TRADE_AMOUNT | TRADE_ID
ABC123 | Jules Winnfield | GOLD | 10000 | ASDADAD
XDA241 | Jimmie Dimmick | GOLD | 12000 | ASSVASD
ADC123 | Vincent Vega | BARC | 10000 | ZXCZCX
ABC123 | Jules Winnfield | BARC | 15000 | ASSXCQA
ADC123 | Vincent Vega | CRED | 250000 | RFAQQA
ABC123 | Jules Winnfield | CRED | 5000 | ASDQ23A
ABC123 | Jules Winnfield | GOLD | 5000 | AVBDQ3A
I'm looking to produce a repeatable monthly report that gives me a view of trading activity aggregated at the counterparty (the EXEC_BROKER field) level, with subtotals - as shown below:
TRADER_ID | TRADER_NAME | NO._OF_CCP_USED | CCP | TRADED_AMT_WITH_CCP | VALUE_OF_TOTAL_TRADES | TRADES_WITH_CCP | TOTAL_TRADES
ABC123 | Jules Winnfield | 3 | GOLD | 15000 | 35000 | 2 | 4
ABC123 | Jules Winnfield | 3 | BARC | 15000 | 35000 | 1 | 4
ABC123 | Jules Winnfield | 3 | CRED | 5000 | 35000 | 1 | 4
...and so on the rest.
The idea is to aggregate the number of trades per counterparty (which I have done using a count function), and the sum of traded amounts with the ccp, but I'm struggling to get the 'subtotal' field next to each trader as shown in my desired output above - so you can see here that Jules has dealt with 3 counterparties in total, with 4 trades between them, and a collective amount of 35000.
I have tried using a combination of aggregate and over by functions, but to no avail.
SELECT
OT.TRADER_ID,
OT.TRADER_NAME,
OT.EXEC_BROKER,
SUM(OT.TRADE_AMOUNT) AS VALUE_OF_TOTAL_TRADES,
COUNT(OT.TRADE_ID) AS TOTAL_TRADES,
COUNT(OT.EXEC_BROKER) OVER PARTITION BY (OT.TRADER_ID) AS NO._OF_CCP_USED,
SUM(OT.TRADE_AMOUNT) OVER PARTITION BY (OT.EXEC_BROKER) AS TRADED_AMT_WITH_CCP,
COUNT(OT.TRADE_ID) OVER PARTITION BY (OT.EXEC_BROKER) AS TRADES_WITH_CCP
FROM dbo.ORDERS_TRADES OT
GROUP BY OT.TRADER_ID, OT.TRADER_NAME, OT.EXEC_BROKER, OT.TRADE_AMOUNT, OT.TRADE_ID
The code above runs but returns millions of rows. When I remove the partition by lines, I get the desired result minus the subtotal columns I'm looking for.
Any suggestions please? Thanks very much!
EDIT:
Final code which gave me the desired output: updating my question to provide this response (thanks to Gordon Linoff) so that others can benefit:
SELECT
OT.TRADER_ID,
OT.TRADER_NAME,
OT.EXEC_BROKER,
RANK() OVER (PARTITION BY OT.TRADER_ID ORDER BY
SUM(OT.TRADE_AMOUNT) DESC) AS CCP_RANK,
SUM(OT.TRADE_AMOUNT) AS TRADED_AMT_WITH_CCP,
SUM(SUM(OT.TRADE_AMOUNT)) OVER (PARTITION BY OT.TRADER_ID) AS
VALUE_OF_TOTAL_TRADES,
COUNT(*) OVER (PARTITION BY OT.TRADER_ID) AS NUM_OF_CCP_USED,
SUM(COUNT(OT.TRADE_ID)) OVER (PARTITION BY OT.TRADER_ID) AS
TOTAL_TRADES
FROM dbo.ORDERS_TRADES OT
GROUP BY OT.TRADER_ID, OT.TRADER_NAME, OT.EXEC_BROKER
You seem to want:
SELECT OT.TRADER_ID, OT.TRADER_NAME, OT.CCP,
COUNT(*) OVER (PARTITION BY OT.TRADER_ID) as NUM_CCP,
SUM(OT.TRADED_AMT) AS TRADED_AMT_WITH_CCP,
SUM(SUM(OT.TRADED_AMT)) OVER (PARTITION BY OT.TRADER_ID) AS VALUE_OF_TOTAL_TRADES,
COUNT(OT.TRADE_ID) AS CCP_TRADES,
SUM(COUNT(OT.TRADE_ID)) OVER (PARTITION BY OT.TRADER_ID) AS TOTAL_TRADES
FROM ORDERS_TRADES OT
GROUP BY OT.TRADER_ID, OT.TRADER_NAME, OT.CCP;
I'm not sure what your query has to do with the results you want. The columns have little to do with what you are asking.
Here is a db<>fiddle.
Making some assumptions about the nomenclature, here is a solution that doesn't use anything too fancy so it's easy to maintain, though it's not the most efficient:
create table trades
(
TRADER_ID varchar(10),
TRADER_NAME varchar(20),
CCP char(4),
TRADED_AMT decimal(10,2),
TRADE_ID varchar(10) primary key
);
insert trades
values
('ABC123', 'Jules Winnfield', 'GOLD', 10000 , 'ASDADAD'),
('XDA241', 'Jimmie Dimmick ', 'GOLD', 12000 , 'ASSVASD'),
('ADC123', 'Vincent Vega ', 'BARC', 10000 , 'ZXCZCX'),
('ABC123', 'Jules Winnfield', 'BARC', 15000 , 'ASSXCQA'),
('ADC123', 'Vincent Vega ', 'CRED', 250000, 'RFAQQA'),
('ABC123', 'Jules Winnfield', 'CRED', 5000 , 'ASDQ23A'),
('ABC123', 'Jules Winnfield', 'GOLD', 5000 , 'AVBDQ3A');
with trader_totals as
(
select trader_id,
distinct_ccps = count(distinct CCP),
total_amt = sum(traded_amt),
total_count = count(*)
from trades
group by trader_id
)
select trader_id = tr.trader_id,
trader_name = trader_name,
distinct_CCP_count = tt.distinct_ccps,
CCP = tr.CCP,
this_CCP_traded_amt = sum(traded_amt),
total_traded_amt = tt.total_amt,
this_CCP_traded_count = count(*),
total_traded_count = tt.total_count
from trades tr
join trader_totals tt on tt.trader_id = tr.trader_id
group by tr.trader_id,
tr.trader_name,
tr.CCP,
tt.distinct_ccps,
tt.total_amt,
tt.total_count

Aggregating column values based on range

I'm new to SQL and stackoverflow, forgive if my question is trivial. I have record of customers purchase quantities in a table, so I want a count of customers whose purchase fall in a range.
TABLE:
+-------------+----------------+
| customer_id | order_quantity |
+-------------+----------------+
| 123 | 10000 |
| 143 | 5000 |
| 999 | 200000 |
| 555 | 50000 |
+-------------+----------------+
The goal is to count how many customers buy < 5000, between 5000-50000 and 50000-100000 order quantities.
I used:
SELECT customer_id,
CASE
WHEN COUNT(order_quantity) < 5000
....
FROM purchases
Which isn't correct (don't even work).
You can use:
select (case when order_quantity < 5000 then '[0-5000)'
when order_quantity < 10000 then '[5000-10000)'
else '10000+'
end) as grp,
count(*) as num_purchases,
count(distinct customer_id) as num_customers
from t
group by grp
order by min(order_quantity);
If a customer makes more than one purchase in a given group, it is not clear if you want to count the number of purchases or the number of customers. This does both.

Finding top 5 worst performing customers by salesperson

I have a view that returns data as follows:
| salesman | customer | invoiced | budgeted | difference
|--------------|--------------|-------------|-------------|--------------
| Joe | ACME Ltd | 1000 | 2000 | -1 000
| Sam | Apple | 1500 | 1100 | +400
| Joe | Apple | 2000 | 2300 | -300
| Kim | ACME Ltd | 1000 | 1500 | -500
This is a snippet of of over 1500 rows that are returned. What I would like to do is return all rows by salesman showing that salespersons 5 worst customers sales. Each salesperson would therefore have 5 rows and each salesperson does sell to various customers who can be the same customer as another salesperson... right now I am looking at 5 worst customers by salesperson by value.
Any ideas on how I can achieve this?
Since one salesman can invoice the same company multiple times, you need to average the invoice amount across that group, then rank withing that partition and take the top five. If you are looking for the absolute worse sale per salesman then you would need to replace AVG with MIN.
SELECT
*
FROM
(
SELECT
salesman,
customer,
AverageInvoiced,
DescendingRank=DENSE_RANK() OVER(PARTITION BY salesman ORDER BY AverageInvoiced DESC)
FROM
(
SELECT
salesman,
customer,
AverageInvoiced=AVG(invoiced)
FROM
Table
WHERE
GROUP BY
salesman,
customer
)
)AS A
WHERE
DescendingRank<=5
I think you can use this
TRy This
SELECT
*
FROM
(
SELECT
salesman,
customer,
AverageDiffrence,
DescendingRank=DENSE_RANK() OVER(PARTITION BY salesman ORDER BY AverageDiffrence DESC)
FROM
(
SELECT
salesman,
customer,
AverageDiffrence=AVG(difference)
FROM
ireports_salesman_variance
WHERE
GROUP BY
salesman,
customer
)
)AS A
WHERE
DescendingRank<=5

Simply query with calculated fields

I have 2 tables in my sqlite3 database. Can someone help me with the sql command below:
tbltrans:
transid | transdate | discountpercentage | Bank
12345 10/09/2011 5 20.00
tbltrans2:
transid | itemnr |price | btwpercentage | qty
12345 205 10.11 12 5
12345 302 15.00 6 7
12345 501 20.00 21 3
SO I want to get a query table with total amount of sale for each transid's and calculated cash column, Like:
Select
Sum(tbltrans2.qty * tbltrans2.price) as TotalAmount,
(Totalamount - tbltrans.Bank) as Cash
where
tbltrans.transid = tbltrans2.transid and transdate = '10/09/12'
Can someone please correct this sql satement ?
Select
Sum(ifnull((tbltrans2.qty * tbltrans2.price),0))-tbltrans.Bank as cash
from tbltrans,tbltrans2
where
tbltrans.transid = tbltrans2.transid and tbltrans.transdate = '10/09/12'
group by tbltrans.transid
try this
If you want to select total amount also then include this in
select,
Sum(ifnull((tbltrans2.qty * tbltrans2.price),0)) as TotalAmount

Finding a date from a SUM

I am having trouble finding what date my customers hit a certain threshold in how much money they make.
customer_id | Amount | created_at
---------------------------
1134 | 10 | 01.01.2010
1134 | 15 | 02.01.2010
1134 | 5 | 03.24.2010
1235 | 10 | 01.03.2010
1235 | 15 | 01.03.2010
1235 | 30 | 01.03.2010
1756 | 50 | 01.05.2010
1756 | 100 | 01.25.2010
To determine how much total amount they made I run a simple query like this:
SELECT customer_id, SUM(amount)
FROM table GROUP BY customer_id
But I need to be able to find for e.g. the date a customer hits $100 in total amount.
Any help is greatly appreciated. Thanks!
Jesse,
I believe you are looking for a version of "running total" calculation.
Take a look at this post calculate-a-running-total.
There is number of useful links there.
This article have a lot of code that you could reuse as well: http://www.sqlteam.com/article/calculating-running-totals.
Something like having clause
SELECT customer_id, SUM(amount) as Total FROM table GROUP BY customer_id having Total > 100
I'm not sure if MySQL supports subqueries, so take this with a grain of salt:
SELECT customer_id
, MIN(created_at) AS FirstDate
FROM ( SELECT customer_id
, created_at
, ( SELECT SUM(amount)
FROM [Table] t
WHERE t.CustomerID = [Table].CustomerID
AND t.created_at <= [Table].created_at
) AS RunTot
FROM [Table]
) x
WHERE x.RunTot >= 100
GROUP BY customer_id