Ascending & descending in single query with Aggregate & Partition - sql

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

Related

I have a select query inside a select query which throws the error :"more than one row returned by a subquery used as an expression" [duplicate]

This question already has answers here:
Postgres Error: More than one row returned by a subquery used as an expression
(6 answers)
Closed 3 years ago.
I am trying to display multiple things such as userid, salary, etc.
I used a select statement to do this. In this select statement, i need to use another select statement to provide 'fraction of salaries' . The query of fraction is correct. When i try to use this select into the main select, it gives the error "more than one row returned by.."
Sample data:
//sample data
insert into employee(id_user int,salary_date date,salary_value) values
(3, 2017-06-28, 15)
( 5, 2017-06-26 5)
( 2, 2017-06-20 5)
( 1, 2017-06-20 15)
( 4, 2017-06-17 25)
//expected output:
id date sal newdate days aboveavgsal date current/firstsal
1 2017-06-20 15 2017-05-20 31 N 2017-04-20 0.6
1 2017-05-20 10 2017-04-20 30 N 2017-04-20 0.4
1 2017-04-20 20 2017-03-20 31 Y 2017-03-20 0.8
1 2017-03-20 20 2017-02-20 28 Y NULL 0.8
1 2017-02-20 15 NULL NULL N NULL 0.6
SELECT id_user
, salary_date
, salary_value
, lead(salary_date) OVER(PARTITION BY id_user ORDER BY salary_date desc)
, salary_date::timestamp - lead(salary_date::timestamp) over (PARTITION BY id_user ORDER BY salary_date DESC)
, CASE WHEN salary_value >= 20 THEN 'Y' ELSE 'N' END
, CASE WHEN salary_value >= 20 THEN lead(salary_date) OVER(PARTITION BY id_user ORDER BY salary_date DESC) END
, (SELECT CAST(t1.salary_value AS float) / CAST(t.salary_value AS float)
FROM (
SELECT t.id_user
, t.salary_value
, ROW_NUMBER() OVER(PARTITION BY t.id_user ORDER BY t.salary_date) AS rowrank
FROM employee t
) AS t
INNER JOIN employee AS t1 ON t1.id_user = t.id_user
WHERE t.rowrank = 1
GROUP BY t1.id_user, t1.salary_value, t1.salary_date, t.salary_value, t.rowrank
ORDER BY t1.id_user, t1.salary_date DESC
) AS fraction
FROM employee
Error starts in this query which i tried enclosing in braces.
(SELECT CAST(t1.salary_value AS float) / CAST(t.salary_value AS float)
FROM (
SELECT t.id_user
, t.salary_value
, row_number() OVER(PARTITION BY t.id_user ORDER BY t.salary_date ) AS rowrank
FROM employee t) AS t
INNER JOIN employee t1 ON t1.id_user = t.id_user
WHERE t.rowrank = 1
GROUP BY t1.id_user, t1.salary_value, t1.salary_date, t.salary_value, t.rowrank
ORDER BY t1.id_user, t1.salary_date DESC
)
In this part of your statement
(SELECT CAST(t1.salary_value AS float) / CAST(t.salary_value AS float)
The cast requires t1.salary to be a single value.
However this part is returning more than one row.
SELECT t.id_user
, t.salary_value
, row_number() OVER(PARTITION BY t.id_user ORDER BY t.salary_date ) AS rowrank
FROM employee t) AS t
INNER JOIN employee t1 ON t1.id_user = t.id_user
WHERE t.rowrank = 1
GROUP BY t1.id_user, t1.salary_value, t1.salary_date, t.salary_value, t.rowrank
ORDER BY t1.id_user, t1.salary_date DESC

select to get rows based on minimum value of a column

I have a table WT_RT.
a select on it will give me this result
select * from WT_RT;
WT_SEQ W RT_AMT R
---------- - ---------- -
0 0 125 M
1 2 9.3 N
45 2 7.19 Q
100 1 6.38 Q
300 1 5.11 Q
500 2 4.69 Q
1000 2 4.39 Q
On input of WT_SEQ=250, since 250 is in between 100 and 300 I need to select the row which has the least RT_AMT. In this case, since 5.11 < 6.38, I need to select the row with WT_SEQ = 300.
Expected Result:
WT_SEQ W RT_AMT R
---------- - ---------- -
300 1 5.11 Q
So far I have managed to find the enclosing 2 rows.
select ........;
WT_SEQ W RT_AMT R
---------- - ---------- -
100 1 6.38 Q
300 1 5.11 Q
But I need to implement the condition of minimum value of RT_AMT
How to do it?
You can try to write a subquery to get nextval by LEAD then, use BETWEEN to get between WT_SEQ column, then self join by WT_SEQ
then use Row_NUMBER with Windows Function to make Rownumber by RT_AMT, then get rn = 1
TestDLL
CREATE TABLE WT_RT(
WT_SEQ INT,
RT_AMT FLOAT
);
INSERT INTO WT_RT VALUES (0,125);
INSERT INTO WT_RT VALUES ( 1, 9.3);
INSERT INTO WT_RT VALUES ( 45,7.19);
INSERT INTO WT_RT VALUES ( 100,6.38);
INSERT INTO WT_RT VALUES ( 300,5.11);
INSERT INTO WT_RT VALUES ( 500,4.69);
INSERT INTO WT_RT VALUES (1000,4.39);
Query
WITH CTE1 AS(
SELECT t2.WT_SEQ,t2.RT_AMT,Row_NUMBER() OVER(ORDER BY t2.RT_AMT) rn
FROM (
SELECT * FROM (
SELECT t1.*,LEAD(WT_SEQ,1,WT_SEQ) over(order by WT_SEQ) nextval
FROM WT_RT t1
)t1
WHERE 250 BETWEEN WT_SEQ and nextval
) t1 INNER JOIN WT_RT t2
on t1.WT_SEQ = t2.WT_SEQ or t1.NEXTVAL = t2.WT_SEQ
)
SELECT *
FROM CTE1
WHERE RN = 1
sqlfiddle
[Results]:
| WT_SEQ | RT_AMT | RN |
|--------|--------|----|
| 300 | 5.11 | 1 |
One way is via a top 2 of an order by distance.
Then the top 1 of an order by RT_AMT.
select WT_SEQ, RT_AMT
from
(
select *
from
(
select *
from WT_RT
cross join (select 250 val from dual) v
order by ABS(WT_SEQ - val)
) q1
where rownum <= 2
order by (case when WT_SEQ = val then 1 else 2 end), RT_AMT asc
) q2
where rownum = 1;
The CASE WHEN is added to give priority to an WT_SEQ that's equal to the value.
You can just use aggregation:
select MIN(RT_AMT)
from WT_RT
where WT_SEQ <= 250
If you need the entire row, you can select one row. In Oracle 12C+:
select MIN(RT_AMT)
from WT_RT
where WT_SEQ <= 250
order by WT_SEQ desc
fetch first 1 row only;
In older version:
select x.*
from (select MIN(RT_AMT)
from WT_RT
where WT_SEQ <= 250
order by WT_SEQ desc
) x
where rownum = 1;

Oracle Remove Consecutive duplicates

I have table in which I store the evauation results of customer. Evaluation can be triggered multiple times. Below is the sample data
CUSTOMER_EVAL_RESULTS:
SEQ CUSTOMER_ID STATUS RESULT
1 100 C XYZ
3 100 C XYZ
7 100 C ABC
8 100 C PQR
11 100 C ABC
12 100 C ABC
From above data set I want only the rows with SEQ as 1,7,8,11.
I used below query suggested on other links but it is not giving the desired result. Please help
SELECT * FROM (
SELECT E.*, ROW_NUMBER() OVER(PARTITION BY CUSTOMER_ID, STATUS, RESULT ORDER BY SEQ) ROW_NUM
FROM CUSTOMER_EVAL_RESULTS E WHERE E.CUSTOMER_ID=100
) WHERE ROW_NUM=1;
You can utilize LAG to check the previous row's value:
SELECT *
FROM
(
SELECT E.*,
LAG(RESULT)
OVER(PARTITION BY CUSTOMER_ID, STATUS
ORDER BY SEQ) prevResult
FROM CUSTOMER_EVAL_RESULTS E
WHERE E.CUSTOMER_ID=100
)
WHERE prevResult IS NULL
OR prevResult <> RESULT
Please try the below
select * from CUSTOMER_EVAL_RESULTS
where not exists (select 1 from CUSTOMER_EVAL_RESULTS
a,CUSTOMER_EVAL_RESULTS b
where a.seq_no < b.seq_no and a.customer_id=b.customer_id
and a.status=b.status and a.result=b.result
and not exists(select 1 from CUSTOMER_EVAL_RESULTS c
where a.seq_no < c.seq_no and c.seq_no < b.seq_no ));

Median in SQL Server 2000

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;

How to write a cross tab query with median

Here's my table
ST NUM
1 1
1 2
1 2
2 1
2 2
2 2
3 2
3 8
I want to return a query where it returns the median of NUM for each ST
ST NUM
1 2
2 2
3 5
I already have a median function
SELECT
CONVERT(DECIMAL(10,2), (
(CONVERT (DECIMAL(10,2),
(SELECT MAX(num) FROM
(SELECT TOP 50 PERCENT num FROM dbo.t ORDER BY num ASC) AS H1)
+
(SELECT MIN(sortTime) FROM
(SELECT TOP 50 PERCENT num FROM dbo.t ORDER BY num DESC) AS H2)
))) / 2) AS Median
Any tips for how to do this?
try this
With
MedianResult
as
(
Select
ST,NUM ,
Row_Number() OVER(Partition by ST Order by NUM) as A,
Row_Number() OVER(Partition by ST Order by NUM desc) as B
from **YourTableName**
)
Select ST, Avg(NUM) as Median
From MedianResult
Where Abs(A-B)<=1
Group by ST