Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 General SQL Server Forums
 New to SQL Server Programming
 SQL Query need help

Author  Topic 

tralala
Starting Member

1 Post

Posted - 2011-07-02 : 09:30:34
hello everybody...
i do need help about sql query..
this is the problem, for example i have table like this:

no| sale
--------------
1 5000
2 3000
3 4000
4 8000
5 6000

and then i need to find peoples that give 80% of total sale??
total sale is 26,000. so 80% of it should hold value about 20,800.
so the result should look like this:
no| name |sale
--------------
4 8000
5 6000
1 5000
3 4000

total of that is 23,000, and that meet the condition.
i'm trying to limit condition based on 80% of total but it doesnt work...

select * from table where sum(sale) < 0.8 * sum(sale) order by sale desc

i hope that query will stop when total is still 80% of total_sale.
but it doesnot work...
i really want to know about how to solve this problem...
i do hope every body can help me...
thanks before...

sunitabeck
Master Smack Fu Yak Hacker

5155 Posts

Posted - 2011-07-02 : 13:52:08
You probably will need to calculate a running total and stop when the running total reaches 80 percent. Calculating running totals is not a pleasant task in SQL in terms of performance. So what I am showing below would certainly work if you are on SQL 2005 or above, but is likely to be a very slow query. Regardless, since I spent time writing it I might as well post it.

-- CREATE TEST DATA
create table #tmp (id int, amt int);
insert into #tmp values (1,5000),(2,3000),(3,4000),(4,8000),(5,6000);

-- SET THE CUT-OFF VALUE AS A FRACTION.
declare @cutoff decimal(9,4);
set @cutoff = 0.80;

-- THE QUERY
with cte1 as
(
select *,row_number() over (order by amt) as RN,
(1.0-@cutoff)*sum(amt) over() as Cutoff
from #tmp
),
cte2 as
(
select *,amt as RTotal from cte1 where RN=1
union all
select c1.*,RTotal+c1.amt
from cte1 c1 inner join cte2 c2 on c2.RN+1 = c1.RN and c2.RTotal+c1.amt <= c2.Cutoff
)
--select sum(amt) from cte1 where RN > (select max(RN) from cte2);
select * from cte1 where RN > (select max(RN) from cte2);

-- CLEANUP.
drop table #tmp;
If the amounts have a normal or uniform distribution, and if the cut-off percentage is more than 50, it would be more efficient to count what you don't want to include. That is what I tried to do here.
Go to Top of Page

jcelko
Esteemed SQL Purist

547 Posts

Posted - 2011-07-03 : 17:50:47
Please post real DDL and not narrative or your own personal programming language. Learn to use ISO-11179 rules for the data element names, avoid needless dialect and use ISO-8601 temporal formats, codes and so forth. Here at a polite posting:

CREATE TABLE Sales_Report
(sales_nbr INTEGER NOT NULL PRIMARY KEY,
sales_amt DECIMAL (8,2) NOT NULL
CHECK (sales_amt > 0.00));

INSERT INTO Sales_Report
VALUES
(1, 5000.00),
(2, 3000.00),
(3, 4000.00),
(4, 8000.00),
(5, 6000.00);

See how easy this is and how anyone can do a cut & paste to test their answers for you?

>> I need to find sales that give 80% of total sale? Total sale is 26,000.00. so 80% of it should hold value about 20,800.00. <<

This is called a Pareto report or “The 80-20 Rule” or “Sturgeon's Law”; It is hard and slow to do in SQL.

Getting the target total is easy and you can order the amounts

SELECT sales_nbr, sales_amt,
(0.80 * SUM(sales_amt) OVER ()) AS target_amt_tot,
ROW_NUMBER() OVER (ORDER BY sales_amt ASC) AS sales_amt_seq
FROM Sales_Report;

Now we need more specs. Do I report multiple subsets that equal the target? Do I find the closest subset that is less than the target? Do I find the closest subset that is greater than the target? Do you want to use a greedy algorithm? Or smallest cardinality of the subset?

Here is one version that depends on building an answer from smallest to largest:

WITH Reverse_Greedy (sales_nbr, sales_amt, target_amt_tot, sales_amt_seq)
AS
(SELECT sales_nbr, sales_amt,
(0.80 * SUM(sales_amt) OVER ()) AS target_amt_tot,
ROW_NUMBER() OVER (ORDER BY sales_amt ASC) AS sales_amt_seq
FROM Sales_Report)

SELECT RG1.sales_nbr, RG1.sales_amt, RG1.target_amt_tot, RG1.sales_amt_seq,
SUM (G1.sales_amt) AS running_sales_amt_tot
FROM Reverse_Greedy AS RG1,
Reverse_Greedy AS RG2
WHERE RG2.sales_amt_seq <= RG1.sales_amt_seq
GROUP BY RG1.sales_nbr, RG1.sales_amt, RG1.target_amt_tot, RG1.sales_amt_seq
HAVING SUM (G1.sales_amt) <= RG1.target_amt_tot;



--CELKO--
Books in Celko Series for Morgan-Kaufmann Publishing
Analytics and OLAP in SQL
Data and Databases: Concepts in Practice
Data, Measurements and Standards in SQL
SQL for Smarties
SQL Programming Style
SQL Puzzles and Answers
Thinking in Sets
Trees and Hierarchies in SQL
Go to Top of Page
   

- Advertisement -