Median in SQL Server 2000 - sql

For even rows formula for median is (104.5 + 108)/2 for table below and For odd rows it is 108 for table below
Total Total
100 100
101 101
104.5 104.5
108 108
108.3 108.3
112 112
114
Code below works in SQL Server 2008 but not in SQL Server 2000 as it does not understand row_number() and over.
How can we change the lower code to make it work on SQL Server 2000?
select avg(Total) median from
(select Total,
rnasc = row_number() over(order by Total),
rndesc = row_number() over(order by Total desc)
from [Table]
) b
where rnasc between rndesc - 1 and rndesc + 1

If you only want a median, you may use this simple query.
SELECT
(
(SELECT MAX(Total) FROM
(SELECT TOP 50 PERCENT Total FROM [Table] ORDER BY Total) AS BottomHalf)
+
(SELECT MIN(Total) FROM
(SELECT TOP 50 PERCENT Total FROM [Table] ORDER BY Total DESC) AS TopHalf)
) / 2.0 AS Median
Source: Function to Calculate Median in Sql Server

SELECT Median = AVG(Total) FROM
(
SELECT Total FROM (
SELECT TOP 1 Total = Total * 1.0 FROM
(
SELECT TOP 50 PERCENT Total
FROM dbo.[Table] ORDER BY Total
) AS sub_a
ORDER BY 1 DESC
) AS sub_1
UNION ALL
SELECT Total FROM (
SELECT TOP 1 Total = Total * 1.0 FROM
(
SELECT TOP 50 PERCENT Total
FROM dbo.[Table] ORDER BY Total DESC
) AS sub_b
ORDER BY 1
) AS sub_2
) AS median;

Related

How to Rank By Partition with island and gap issue

Is it possible to rank item by partition without use CTE method
Expected Table
item
value
ID
A
10
1
A
20
1
B
30
2
B
40
2
C
50
3
C
60
3
A
70
4
A
80
4
By giving id to the partition to allow agitated function to work the way I want.
item
MIN
MAX
ID
A
10
20
1
B
30
40
2
C
50
60
3
A
70
80
4
SQL Version: Microsoft SQL Sever 2017
Assuming that the value column provides the intended ordering of the records which we see in your question above, we can try using the difference in row numbers method here. Your problem is a type of gaps and islands problem.
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY value) rn1,
ROW_NUMBER() OVER (PARTITION BY item ORDER BY value) rn2
FROM yourTable
)
SELECT item, MIN(value) AS [MIN], MAX(value) AS [MAX], MIN(ID) AS ID
FROM cte
GROUP BY item, rn1 - rn2
ORDER BY MIN(value);
Demo
If you don't want to use a CTE here, for whatever reason, you may simply inline the SQL code in the CTE into the bottom query, as a subquery:
SELECT item, MIN(value) AS [MIN], MAX(value) AS [MAX], MIN(ID) AS ID
FROM
(
SELECT *, ROW_NUMBER() OVER (ORDER BY value) rn1,
ROW_NUMBER() OVER (PARTITION BY item ORDER BY value) rn2
FROM yourTable
) t
GROUP BY item, rn1 - rn2
ORDER BY MIN(value);
You can generate group IDs by analyzing the previous row item value that could be obtained with the LAG function and finally use GROUP BY to get the minimum and maximum value in item groups.
SELECT
item,
MIN(value) AS "min",
MAX(value) AS "max",
group_id + 1 AS id
FROM (
SELECT
*,
SUM(CASE WHEN item = prev_item THEN 0 ELSE 1 END) OVER (ORDER BY value) AS group_id
FROM (
SELECT
*,
LAG(item, 1, item) OVER (ORDER BY value) AS prev_item
FROM t
) items
) groups
GROUP BY item, group_id
Query produces output
item
min
max
id
A
10
20
1
B
30
40
2
C
50
60
3
A
70
80
4
You can check a working demo here

Running Total added to Next partition

declare #Temp table
(
Grp int,
Bal float,
[Value] float
)
declare #Amt float =1000;
Insert into #Temp(Grp,[Value])
Values(1,10),(1,5),(1,15)
,(2,20),(2,5),(2,15)
,(3,50),(3,50)
select Grp,#Amt as Amount,Value,Bal from #Temp
Required Output:
Grp Amount Value Bal
1 1000 10 1000
1 1000 5 1000
1 1000 15 1000
2 1000 20 1030 ---(10+5+15)
2 1000 5 1030
2 1000 15 1030
3 1000 50 1070 ---- (20+5+15)
3 1000 50 1070
Balance calculated based on running total of 'Value' of Group1 added to Group2 and running total of group 2 added to balance of Group3 and soon
I know how to calculate the running total but I can't as sums are added to next partition.
Please help to get required result efficiently. I am using SQL Server 2017
One method is outer apply:
select t.*, t.amount + coalesce(t2.value, 0)
from #temp t outer apply
(select sum(t2.value) as value
from #temp t2
where t2.grp < t.grp
) t2;
It is possibly more efficient to use aggregation and a running sum:
select t.*,
(1000 + tt.running_value)
from #temp t join
(select t.grp, sum(value) as value,
sum(sum(value)) over (order by grp) - sum(value) as running_value
from #temp t
group by grp
) tt
on t.grp = tt.grp;
Unfortunately, SQL Server doesn't fully support range window frames, so I don't think there is a convenient way to do this only with window functions. But the group by will probably have much better performance.

Divide each value of a column by the total count of records in a table

A query that is capable of dividing each value of a column by the total number of records in the table
I tried the following query
select ( (p.rank/count(*)) * 100 ) as rankratio from RankTable p;
I see an error and not able to execute the query.
for example
total records is 5 so (1/5)*100 = 20
RankTable
rank rankratio
1 20
2 40
3 60
4 80
5 100
use analytic count(*) over():
select ( (s.rank/s.total_count) * 100 ) as rankratio
from
(
select rank, count(*) over() as total_count
from RankTable p
)s
order by s.rank;

Ascending & descending in single query with Aggregate & Partition

I have the data like shown below:
name SAL1 SAL2 SAL3
A 10 20 30
B 11 21 31
C 12 22 32
D 13 23 33
i was trying to get an additional calculated column, i.e 10 / 25 *100
[ SAL1 of A / Sum( TOP 2 Sal1 ORDER BY Sal1 DESC ) ]
Instead of considering sum for all the records in the denominator i need to consider only top 2 sal order by sal desc
respectively for the rest of the rows.
with other columns subsequently.
name SAL_calculated
A 40
B 44
C 48
D 52
And do order by Asc for name column and Desc for the rest of the columns
SELECT name, SAL_calculated = SAL * 100.0 / SUM(SAL) OVER ()
FROM table
ORDER BY name ASC, SAL_calculated DESC
for SAL1 of A / Sum(TOP 2 Sal1 ORDER BY Sal1 DESC)
select *, t.SAL1 * 100.0 / c.TOP2_SAL
from tbl t
CROSS APPLY
(
SELECT TOP2_SAL = SUM(SAL1)
FROM
(
SELECT TOP 2 SAL1
FROM tbl
ORDER BY SAL1 desc
) c
) c
Try this:
SELECT name, SAL1, SAL2, SAL3,
ROUND(SAL1 * 1.0 / x.v * 100, 1) AS SAL_calculated
FROM mytable
CROSS JOIN (SELECT SUM(SAL1) FROM mytable) AS x(v)
ORDER BY name, SAL1 DESC, SAL2 DESC, SAL3 DESC
well here is what i did,
In subquery, We can store aggregation of top n or whole set of records into the variable a and then use that variable as denominator.
Declare #a as integer = 0
SELECT #a =
(
SELECT TOP2_SAL = SUM(SAL1)
FROM
(
SELECT TOP 2 SAL1
FROM [dbo].[TESTY]
ORDER BY SAL1 desc
) c
)
select
*,
t.SAL1 * 100 / #a
from [dbo].[TESTY] t

Get the row with max(column) for distinct key

I have some data like
code amount month
aaa1 100 1
aaa1 200 2
aaa1 300 3
aaa4 450 2
aaa4 400 3
aaa6 0 2
From the above, for each code I want to get the row with max(month)
code amount month
aaa1 300 3
aaa4 400 3
aaa6 0 2
How can I create a ms sql query for this?
;WITH MyCTE AS
(
SELECT code,
amount,
month,
ROW_NUMBER() OVER(PARTITION BY code ORDER BY code,month DESC) AS rownum
FROM table
)
SELECT *
FROM MyCTE
WHERE rownum = 1
You can use the ranking function ROW_NUMBER() with PARTITION BY code ORDER BY month DESC to do this:
WITH CTE
AS
(
SELECT
code, amount, month,
ROW_NUMBER() OVER(PARTITION BY code
ORDER BY month DESC) AS RN
FROM Tablename
)
SELECT code, amount, month
FROM CTE
WHERE RN = 1;
This will give you the maximum month for each code.
SQL Fiddle Demo
Try this
SELECT *
FROM
(SELECT MAX(MONTH) month, code
FROM table1
GROUP BY code) res
JOIN table1
ON res.month = table1.month
AND res.code = table1.code
Here is the SQLfiddle