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.
| 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 50002 30003 40004 80005 6000and 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 80005 60001 50003 4000total 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 desci 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 DATAcreate 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 QUERYwith 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. |
 |
|
|
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_ReportVALUES(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 amountsSELECT 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 PublishingAnalytics and OLAP in SQLData and Databases: Concepts in Practice Data, Measurements and Standards in SQLSQL for SmartiesSQL Programming Style SQL Puzzles and Answers Thinking in SetsTrees and Hierarchies in SQL |
 |
|
|
|
|
|
|
|