single column value in multiple columns - sql

ID|Class | Number
--+------+---------
1 | 1 | 58.2
2 | 1 | 85.4
3 | 2 | 28.2
4 | 2 | 55.4
The desired result would be:
Column1 |Number | Column2 | Number
--------+-------+---------+---------
1 | 58.2 | 2 |28.2
1 | 85.4 | 2 |55.4
What would be the required SQL?

You can user row_number() and aggregate:
select 1, max(case when seqnum % 2 = 1 then number end),
2, max(case when seqnum % 2 = 0 then number end)
from (select t.*,
row_number() over (partition by class order by id) as seqnum
from t
) t
group by ceiling(seqnum / 2.0);
The aggregation uses arithmetic to put pairs of rows for each class into one row.

try this
SELECT 1 AS Column1,t2.Number,2 AS Column2,t1.Number
FROM
(
SELECT *
FROM test11
) t2
INNER JOIN
(
SELECT *
FROM test11
) t1
ON t1.Class = t2.Class
WHERE t1.ID < t2.ID
ORDER BY t1.ID DESC
Demo in db<>fiddle

Related

Get only rows where data is the max value

I have a table like this:
treatment | patient_id
3 | 1
3 | 1
3 | 1
2 | 1
2 | 1
1 | 1
2 | 2
2 | 2
1 | 2
I need to get only rows on max(treatment) like this:
treatment | patient_id
3 | 1
3 | 1
3 | 1
2 | 2
2 | 2
The patient_id 1 the max(treatment) is 3
The patient_id 2 the max(treatment) is 2
You can for example join on the aggregated table using the maximal value:
select t.*
from tmp t
inner join (
select max(a) max_a, b
from tmp
group by b
) it on t.a = it.max_a and t.b = it.b;
Here's the db fiddle.
Try this :
WITH list AS
( SELECT patient_id, max(treatment) AS treatment_max
FROM your_table
GROUP BY patient_id
)
SELECT *
FROM your_table AS t
INNER JOIN list AS l
ON t.patient_id = l.patient_id
AND t.treatment = l.treatment_max
You can use rank:
with u as
(select *, rank() over(partition by patient_id order by treatment desc) r
from table_name)
select treatment, patient_id
from u
where r = 1;
Fiddle
use corelated subquery
select t1.* from table_name t1
where t1.treatment=( select max(treatment) from table_name t2 where t1.patient_id=t2.patient_id
)

UPDATE Table value from other table like distribution

I have a table 1 where I have to distribute award from other table table 2 but in a manner of First in first serve from other table row.
Table 1
ATTIME | Absent | LeaveType
-----------------------------
2019-01-01| 1 |
2019-01-02| 1 |
2019-01-03| 1 |
2019-01-04| 1 |
2019-01-05| 1 |
2019-01-06| 1 |
Table 2
LeaveType | Total
-------------------
Casual | 3
Sick | 2
I have achieved it by using cursor, but want a set base UPDATE QUERY or any other option which optimize my execution plan,
Final Result will be....
Table 1
ATTIME | Absent | LeaveType
-----------------------------
2019-01-01| 1 | CL
2019-01-02| 1 | CL
2019-01-03| 1 | CL
2019-01-04| 1 | SL
2019-01-05| 1 | SL
2019-01-06| 1 |
In a supported version of SQL Server, you would use a cumulative sum and row_number():
with toupdate as (
select t1.*,
row_number() over (order by attime) as seqnum
from table1 t1
)
update toupdate
set leavetype = t2.leavetype
from (select t2.*,
sum(total) over (order by leavetype) as runningtotal
from table2 t2
) t2
where toupdate.seqnum between t2.runningtotal + 1 - total and t2.runningtotal;
In archaic, unsupported versions of SQL Server, the cumulative sum is more cumbersome. One method uses a correlated subquery:
with toupdate as (
select t1.*,
row_number() over (order by attime) as seqnum
from table1 t1
)
update toupdate
set leavetype = t2.leavetype
from (select t2.*,
(select sum(total)
from table2 tt2
where tt2.leavetype <= t2.leavetype
) as runningtotal
from table2 t2
) t2
where t1.seqnum between t2.runningtotal + 1 - total and t2.runningtotal;

Join column name with value from other table

SELECT id FROM Table2 t2
INNER JOIN Table1 t1
on t1.ordno = t2.ordno
and t1.testcode = t2.testcode
WHERE RN1 > 0
AND RN2 > 0
AND RN3 > 0
AND RN3 > 0
AND RN4 > 0
AND RN5 > 0
AND RN6 > 0
I only want to return the id from Table2 if a value from Table1 is >0 (column name from Table1 exists in Table2.RNVALUE). So in this case, I only want the first two rows of table2 to pop-up because they have a value in table1 which is greater then 0. Can anyone help me with a query to do this?
Table1:
+--------------------------------------------------------+
| ORDNO | TESTCODE | RN1 | RN2 | RN3 | RN4 | RN5 | RN6 |
+--------------------------------------------------------+
| 123 | 456 | 55 | 56 | 0 | 0 | null | null |
+--------------------------------------------------------+
Table2:
+----------------------------------+
| ORDNO | TESTCODE | RN_VALUE | ID |
+----------------------------------+
| 123 456 RN1 1 |
| 123 456 RN2 2 |
| 123 456 RN3 3 |
| 123 456 RN4 4 |
+----------------------------------+
I believe you want something like this:
SELECT t2.*
FROM Table2 t2 INNER JOIN
Table1 t1
ON t1.ordno = t2.ordno AND t1.testcode = t2.testcode
WHERE (RN1 > 0 AND t2.RN_VALUE = 'RN1') OR
(RN2 > 0 AND t2.RN_VALUE = 'RN2') OR
(RN3 > 0 AND t2.RN_VALUE = 'RN3') OR
(RN4 > 0 AND t2.RN_VALUE = 'RN4') OR
(RN5 > 0 AND t2.RN_VALUE = 'RN5') OR
(RN6 > 0 AND t2.RN_VALUE = 'RN6');
Having multiple columns with names like that suggests a poor data model. Perhaps these should be in separate rows, with one value per row.
select t2.*
from table2 t2
inner join (select ordno, testcode, 1 as rn, rn1 as val
union
select ordno, testcode, 2 as rn, rn2 as val
union
select ordno, testcode, 3 as rn, rn3 as val
union
select ordno, testcode, 4 as rn, rn4 as val
union
select ordno, testcode, 5 as rn, rn5 as val
union
select ordno, testcode, 6 as rn, rn6 as val
) t1
on t2.rn_value=t1.rn
and t2.ordno=t1.ordno
and t2.testcode=t1.testcode
where t1.val>0

Aggregating a unique pair of column values from the same table based on a common column value

I have the following table:
my_table
------------------------
| common_id | uniq_val |
------------------------
| 1 | foo |
------------------------
| 1 | bar |
------------------------
And I want to aggregate values from it such that the resulting query looks like:
DESIRED RESULT
---------------------------------------
| common_id | uniq_val_1 | uniq_val_2 |
---------------------------------------
| 1 | foo | bar |
---------------------------------------
OR
---------------------------------------
| common_id | uniq_val_1 | uniq_val_2 |
---------------------------------------
| 1 | bar | foo |
---------------------------------------
So I've written the query:
SELECT t1.common_id, t1.uniq_val, t2.uniq_val
FROM my_table t1 JOIN my_table AS t2
ON t1.common_id=t2.common_id
WHERE t1.uniq_val!=t2.uniq_val;
Which results in
RESULTING SELECT
---------------------------------------
| common_id | uniq_val_1 | uniq_val_2 |
---------------------------------------
| 1 | foo | foo |
---------------------------------------
| 1 | bar | bar |
---------------------------------------
But I only need one of those columns, so I should be able to do a GROUP BY t1.common_id, like:
SELECT t1.common_id, t1.uniq_val, t2.uniq_val
FROM my_table t1 JOIN my_table AS t2
ON t1.common_id=t2.common_id
WHERE t1.uniq_val!=t2.uniq_val
GROUP BY t1.common_id;
Unfortunately this returns the error:
ERROR: column "t1.uniq_val" must appear in the GROUP BY clause or be used in an aggregate function
Can anyone point out the error in my logic?
How about simple aggregation?
select common_id, min(uniq_val) as uniq_val_1, max(uniq_val) as uniq_val_2
from my_table
group by common_id;
you can try distinct on
SELECT distinct on (t1.common_id) t1.common_id, t1.uniq_val, t2.uniq_val
FROM my_table t1 JOIN my_table AS t2
ON t1.common_id=t2.common_id
WHERE t1.uniq_val!=t2.uniq_val;
I think it will produce what you need!
This will handle up to 10 uniq_val values per common_id. You can remove or add to account for fewer or more uniq_val values if needed.
See a demonstration here with common_id values that have varying counts of uniq_val values:
http://sqlfiddle.com/#!15/e2c87/1/0
select common_id,
max(case when rn = 1 then uniq_val else null end) as uniq_val_1,
max(case when rn = 2 then uniq_val else null end) as uniq_val_2,
max(case when rn = 3 then uniq_val else null end) as uniq_val_3,
max(case when rn = 4 then uniq_val else null end) as uniq_val_4,
max(case when rn = 5 then uniq_val else null end) as uniq_val_5,
max(case when rn = 6 then uniq_val else null end) as uniq_val_6,
max(case when rn = 7 then uniq_val else null end) as uniq_val_7,
max(case when rn = 8 then uniq_val else null end) as uniq_val_8,
max(case when rn = 9 then uniq_val else null end) as uniq_val_9,
max(case when rn = 10 then uniq_val else null end) as uniq_val_10
from(
select row_number() over (partition by common_id order by common_id, uniq_val) as rn,
common_id,
uniq_val
from my_table
order by common_id, uniq_val) x
group by common_id
order by common_id

T-sql rank for max and min value

I need help with a t-sql query.
I have a table with this structure:
id | OverallRank | FirstRank | SecondRank | Nrank..
1 | 10 | 20 | 30 | 5
2 | 15 | 24 | 12 | 80
3 | 10 | 40 | 37 | 12
I need a query that produces this kind of result:
When id: 1
id | OverallRank | BestRankLabel | BestRankValue | WorstRankLabel | WorkRankValue
1 | 10 | SecondRank | 30 | Nrank | 5
When id: 2
id | OverallRank | BestRankLabel | BestRankValue | WorstRankLabel | WorkRankValue
1 | 15 | FirstRank | 24 | SecondRank | 12
How can I do it?
Thanks in advance
with cte(id, RankValue,RankName) as (
SELECT id, RankValue,RankName
FROM
(SELECT id, OverallRank, FirstRank, SecondRank, Nrank
FROM ##input) p
UNPIVOT
(RankValue FOR RankName IN
(OverallRank, FirstRank, SecondRank, Nrank)
)AS unpvt)
select t1.id, max(case when RankName = 'OverallRank' then RankValue else null end) as OverallRank,
max(case when t1.RankValue = t2.MaxRankValue then RankName else null end) as BestRankName,
MAX(t2.MaxRankValue) as BestRankValue,
max(case when t1.RankValue = t3.MinRankValue then RankName else null end) as WorstRankName,
MAX(t3.MinRankValue) as WorstRankValue
from cte as t1
left join (select id, MAX(RankValue) as MaxRankValue from cte group by id) as t2 on t1.id = t2.id
left join (select id, min(RankValue) as MinRankValue from cte group by id) as t3 on t1.id = t3.id
group by t1.id
Working good with your test data. You should only edit RankName IN (OverallRank, FirstRank, SecondRank, Nrank) by adding right columns' names.
CASE
WHEN OverallRank > FirstRank and OverallRank > FirstSecondRand and OverallRank > nRank THEN 'OverallRank'
WHEN FirstRank > OverallRank ... THEN 'FirstRank'
END
This kind of query is why you should normalise your data.
declare #id int, #numranks int
select #id = 1, #numranks = 3 -- number of Rank columns
;with cte as
(
select *
from
(
select *,
ROW_NUMBER() over (partition by id order by rank desc) rn
from
(
select * from YourBadlyDesignedTable
unpivot (Rank for RankNo in (FirstRank, SecondRank, ThirdRank))u -- etc
) v2
) v1
where id=#id and rn in (1, #numranks)
)
select
tMin.id,
tMin.OverallRank,
tMin.RankNo as BestRankLabel,
tMin.Rank as BestRankValue,
tMax.RankNo as WorstRankLabel,
tMax.Rank as WorstRankValue
from (select * from cte where rn=1) tMin
inner join (select * from cte where rn>1) tMax
on tMin.id = tmax.id
You can take out the id = #id if you want all rows.