how to select values that sum up to 60% of the total - sql

assume I have a table of this kind:
A B 3
C D 1
E F 2
G H 4
The sum of the last column is 10, and I want the biggest values that sum up to at least 60% of the total value. So, in this case, it will return
G H 4
A B 3
It goes up to 70% but if only the 1st value was selected, it will only go up to 40%. Even though there could be a combination that will return exactly 60%, we want to take the largest numbers.
So, I think I know how to sort the values from biggest to smallest and how to sum up all the values, but I don't know how to then take only lines that sum up to 60%.

--save the whole sum into a variable
summa = select sum(val) from sometable;
select *
from sometable o
where (
select sum(val)
from sometable i
where i.val <= o.val
) >= 0.6*summa;

I think this gives you the correct result. Need to work with a temporary table though, not sure if this can be avoided.
DECLARE #total bigint
select #total = SUM(value) from SampleTable
select st.*,
convert(decimal(10,2), (select SUM(value) from SampleTable st2 where st2.Value >= st.Value))/#total as percentage
into #temptable
from sampletable st
select * from #temptable
where Value >= (select max(Value) from #temptable where percentage >= 0.6)

Related

Group By 2 Columns and Find Percentage of Group In SQL

I have a Game table with two columns TeamZeroScore and TeamOneScore. I would like to calculate the % of games that end with each score variance. The max score one team can have is 5.
I have got the following code which selects each team score with an additional 2 columns to have the max and min of these two values in order. I did this because I thought the next step is to group by these two columns
SELECT TOP (100000) [TeamOneScore],[TeamZeroScore],
(SELECT Max(v)
FROM (VALUES ([TeamOneScore]), ([TeamZeroScore])) AS value(v)) as [MaxScore],
(SELECT Min(v)
FROM (VALUES ([TeamOneScore]), ([TeamZeroScore])) AS value(v)) as [MinScore]
FROM [Database].[dbo].[Game]
Below is the sample data I have for the code above.
How do I produce something similar to this? I think I need to Group By MaxScore, MinScore and then use Count on each group to calculate the percentage based on the total.
Select
Count(*) as "number",
(100 * count(*)) / t
As "percentage",
TeamOneScore as score,
TeamTwoScore as score
From
( Select
TeamOneScore,TeamTwoScore
From tablename
Where TeamOneScore <= TeamTwoScore
Union all
Select
TeamTwoScore,TeamOneScore
from tablename
Where TeamOneScore > TeamTwoScore
) a,
(Select count(*) as t
From tablename) b
Group by
TeamOneScore,
TeamTwoScore
Order by
TeamOneScore,
TeamTwoScore;

Random Samples of XX rows per Column Value

I'm using T-SQL and require some sample output of random rows.
Typically I would write some SQL as per below
Select top 10 *
from SampleTable as ST
Order by NewID()
However this time I want say 100 rows but them split equally by another column value for instance Column 'Type'.
100 Rows with a sample of 25 rows for TypeA , 25 rows for Type B, 25 rows for Type C and lastly 25 rows for Type D scenerio.
My 'Type' values are saved to a temp table
Select top 10 *
from SampleTable as ST
Inner Join #Types as TY
on TY.Type = ST.Type
Order by NewID()
I've seen NTILE but not sure if applicable for my problem.
Thanks.
Use ROW_NUMBER in conjunction with NEWID():
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY ST.Type ORDER BY NEWID()) rn
FROM SampleTable AS ST
INNER JOIN #TypesAS TY ON TY.Type = ST.Type
)
SELECT *
FROM cte
WHERE rn <= 25;
The above solution will return 25 records from each type (or however many fewer might be available), randomly.

How to average the top n in each SQL group

I'm trying to figure out how to average the top N values within each group. I have a table with two columns, Group and Value. My goal is to average the top N values within each group where N is different based on another table.
For group A, N equals 3 and is highlighted in red. The output is the average of the top 3 values.
For group B, N equals 2 and is highlighted in green. Because we only have 1 value of 2.2 for group B, we need to go to the filler table. The filler value for group B is 2.0, so we will average 2.2 and 2.0. If N = 5, then the filler value will be repeated 4 times for Group B.
My initial idea is to:
Rank the values in each group
Join it to the second table
Use where Rank <= N to remove the duplicates before averaging
However, I not sure how the filling table could be incorporated since N could be greater than the number of values I have. I do need to use SQL Server 2008.
First of all, I hope that you're using more adequate names instead of Group and Value. Here's a sample code that first defines the order to later define the N values that will be used and get an average from those. The code is untested as you didn't provide consumable sample data.
WITH CTE AS(
SELECT *,
ROW_NUMBER() OVER( PARTITION BY [Group] ORDER BY [Value] DESC) AS rn,
COUNT(*) OVER( PARTITION BY [Group]) ItemCount
FROM TableWithValues
)
SELECT [Group],
(SUM( [Value]) + CASE WHEN N.n > c.ItemCount
THEN (N.n - c.ItemCount) * F.Filler
ELSE 0 END)/ N.n AS [Value]
FROM CTE c
JOIN TableWithN N ON c.[Group] = N.[Group] AND c.rn <= N.n
JOIN Fillers F ON c.[Group] = F.[Group]
GROUP BY [Group];

Get Percentage from SubQuery ResultSet

Am having a requirement to show the percentage value along with the other column values in result set.
SQl
select Name, days from table
O/P result set
Name days
Sam 20
ram 30
My required o\p is
Name days Percentage
Sam 20 40
ram 30 60
So i modified my query as below
select Name, days, (days / sum(days)) as Percentage
from (select Name, days from table) as temp
But able to get the desired o/p.
Thanks in advance
declare #tab table (name varchar(30),[days] int)
insert into #tab
select 'Sam', 20
union all
select 'ram', 30
declare #sum int
set #sum = (select sum(days) from #tab)
select name,days ,days*100/#sum as per
from #tab
output
name days per
Sam 20 40
ram 30 60
You can use CROSS JOIN
select Name, days, days * 100 / t.sum_days as Percentage
from table
cross join (
select sum(days) sum_days from table
) t
if the days is int then you should cast it first to get more accurate result: cast(days as decimal(10,2))
Another option is to use window function
select
Name, days
, Percentage = 100.0 * days / sum(days) over ()
from table
Have an uncorrelated subquery to return the total sum of days:
select t1.Name, t1.days, 100 * t1.days / (select sum(t2.days) from table t2) as Percentage
from table t1

SQL Server 2000: Length of field in 1/3 (or 2/3) of the records

Is there a simpler/cleaner way to do this using SQL Server 2000?
Every few days I need to do this.
I first look at how many records we have total:
SELECT COUNT(*) FROM MyTable
Then I list all the lengths of a certain field:
SELECT LEN(MyText)
FROM MyTable
ORDER BY LEN(MyText) ASC
I then need to scroll down 1/3rd of the way... and note the value.
I then need to scroll down 2/3rds of the way... and note the value.
And then finally the last value.
I need to find out x, y, and z:
33% of the records have this field with a length under x bytes
66% of the records have this field with a length under y bytes
100% of the records have this field with a length under z bytes
In SQL 2005 you could probably use the ranking functions for this. In SQL 2000 I think you're stuck doing something like this.
DECLARE #RC INT
CREATE TABLE #lengths
(
id INT IDENTITY(1,1),
[length] INT
)
INSERT INTO #lengths
SELECT LEN(MyText)
FROM MyTable
ORDER BY LEN(MyText) ASC
SET #rc= ##ROWCOUNT
SELECT [length]
FROM #lengths
WHERE id IN
(#rc/3, (2*#rc)/3, #rc)
I think you need something like this:
SELECT
x1.l AS Length,
x1.n * 1e2 / (SELECT COUNT(*) FROM MyTable) AS [Percent],
SUM(x2.n) * 1e2 / (SELECT COUNT(*) FROM MyTable) AS CumPercent
FROM (
SELECT LEN(MyText) AS l, COUNT(*) AS n
FROM MyTable
GROUP BY LEN(MyText)
) AS x1
LEFT JOIN (
SELECT LEN(MyText) AS l, COUNT(*) AS n
FROM MyTable
GROUP BY LEN(MyText)
) AS x2
ON x2.l <= x1.l
GROUP BY x1.l, x1.n
ORDER BY x1.l