Filter SQL Query? - sql

The sample data is:
l16seqno | l16lcode | carrno | ecarrno | l16qty | reasoncode
32001 | 12 | 207620 | 370036873034035916 | 32 | 0
32269 | 12 | 207620 | 370036873034035916 | -32 | 800
39075 | 12 | 207620 | 370036873034035916 | 32 | 0
39074 | 12 | 207622 | 370036873034035923 | 32 | 0
32268 | 12 | 207622 | 370036873034035923 | -32 | 800
31999 | 12 | 207622 | 370036873034035923 | 32 | 0
32271 | 12 | 207624 | 370036873034035930 | -32 | 800
32005 | 12 | 207624 | 370036873034035930 | 32 | 0
39077 | 12 | 207624 | 370036873034035930 | 32 | 0
I have logging of all the events in table Z02T1. Whenever I have l16lcode=12 – I am blocking or unblocking a pallet. When I block a pallet l16lqty feild is negative, and when I unblock – it is positive.
Reason codes can be found in Z02T2 table (can be connected to Z02T1 by l16seqno – a unique sequence number of each log record).
Z14T1 table contains info about pallets – pallet numbers.
My aim is to find two lines for each pallet i.e.
when blocked with code 800 ... and ... when unblocked with code 0
For this I have to find the nearest next record of l16lcode=12 for the same pallet with reason code 0 (after there was a record for this pallet with reason code 800).
The initial query I have made is:
select Z02T1.datreg, Z02T1.l16seqno, Z02T1.l16lcode, Z02T1.divcode, Z02T1.carrno,
Z14T1.ecarrno, Z02T1.l16qty, Z02T2.reascode from Z02T2
inner join Z02T1 on Z02T1.l16seqno=Z02T2.l16seqno
left outer join Z14T1 ON Z14T1.carrno=Z02T1.carrno
where Z02T1.l16lcode=12
and (Z02T2.reascode=800 or Z02T2.reascode=0 )
order by Z14T1.ecarrno
How I can change this query to get one record with reasoncode 800 and then very next record with reasoncode 0 for same ecarrno feild ?

Here is some sample code that you could use to modify your existing query.
Be aware that this example filters on the first occurance of reasoncode=800 and then subfilters on the first occurance of reasoncode=0 that has a l16seqno greater than the reasoncode=800 record.
CREATE TABLE reasons (
l16seqno int NOT NULL,
carrno int NOT NULL,
reasoncode int NOT NULL
);
INSERT INTO reasons
(l16seqno, carrno, reasoncode)
VALUES
(1, 1, 0),
(2, 1, 800),
(3, 1, 0),
(10, 300, 0),
(11, 300, 800),
(12, 300, 0),
(13, 300, 800),
(14, 300, 0),
(1003, 1212, 0),
(1004, 1212, 800),
(1005, 1212, 0),
(1006, 1212, 0);
WITH cte1 (l16seqno, carrno, reasoncode, rownumber)
AS
(
SELECT l16seqno, carrno, reasoncode, ROW_NUMBER() OVER (PARTITION BY carrno, reasoncode ORDER BY l16seqno)
FROM reasons
WHERE reasoncode = 800
),
cte2 (l16seqno, carrno, reasoncode, rownumber)
AS
(
SELECT r.l16seqno, r.carrno, r.reasoncode, ROW_NUMBER() OVER (PARTITION BY r.carrno, r.reasoncode ORDER BY r.l16seqno)
FROM reasons AS r
INNER JOIN cte1 AS c ON r.carrno = c.carrno
WHERE r.reasoncode = 0 AND r.l16seqno > c.l16seqno
)
SELECT r.l16seqno, r.carrno, r.reasoncode
FROM reasons AS r
LEFT OUTER JOIN cte1 AS c1 ON c1.l16seqno = r.l16seqno
LEFT OUTER JOIN cte2 AS c2 ON c2.l16seqno = r.l16seqno
WHERE c1.rownumber = 1
OR c2.rownumber = 1
ORDER BY r.carrno, r.l16seqno;
Here is the SQL Fiddle demo of the sample code listed above.
I hope this helps.

Here you go:
;with cte as
(
Select l16seqno
,l16lcode
,carrno
,ecarrno
,l16qty
,reasoncode
,ROW_NUMBER() Over(Partition By ecarrno, reasoncode Order By l16seqno) rn
From MyTable
)
Select l16seqno
,l16lcode
,carrno
,ecarrno
,l16qty
,reasoncode
From cte
Where rn = 1
Order By ecarrno asc, reasoncode desc

Related

How can I select different values in one table?

INST | MAXRATE | MAXDATE | TAX |
--------------------------------
1 | 1.5 | 17 | 0 |
2 | 0 | 20 | 0 |
3 | 0 | 35 | 0 |
4 | 0 | 45 | 18 |
5 | 0 | 50 | 1 |
I have a table like this. And I want to select a maximum value in each row. For example, the first column has an INST and I want to pick the max value. And I also want to pick 50 as a MAXDATE. How can I pick these values? Here is an example output I want.
INST | MAXRATE | MAXDATE | TAX |
---------------------------------
5 | 1.5 | 50 | 18 |
CREATE TABLE t1 (
INST NUMBER, MAXRATE NUMBER, MAXDATE NUMBER, TAX NUMBER
);
INSERT INTO t1 (INST, MAXRATE, MAXDATE, TAX) VALUES (1, 1.5, 17, 0);
INSERT INTO t1 (INST, MAXRATE, MAXDATE, TAX) VALUES (2, 0, 20, 0);
INSERT INTO t1 (INST, MAXRATE, MAXDATE, TAX) VALUES (3, 0, 35, 0);
INSERT INTO t1 (INST, MAXRATE, MAXDATE, TAX) VALUES (4, 0, 45, 18);
INSERT INTO t1 (INST, MAXRATE, MAXDATE, TAX) VALUES (5, 0, 50, 1);
SELECT MAX(INST), MAX(MAXRATE), MAX(MAXDATE), MAX(TAX) FROM t1;
https://sqlize.online/sql/oracle21/d281b1d5c7e91c8779773733c4f4273e/
Could you try this idea or something similar?
SELECT MAX(INST) max_inst, b.max_maxrate
FROM YourTable a
INNER JOIN (
SELECT inst, MAX(maxrate) max_maxrate
FROM YourTable
GROUP BY inst
) b
ON a.inst = b.inst AND a.maxrate = b.maxrate

Selecting only top parent table row with all of it's children table rows

So I have two tables:
#ProjectHealthReports
Id | From | SubmittedOn
1 | 2020-01-01 |
2 | 2020-02-01 | 2020-10-23
3 | 2020-03-01 |
4 | 2020-04-01 | 2020-10-23
5 | 2020-05-01 | 2020-10-23
#ProjectHealthReportItems
Id | Note | ProjectHealthReportId
1 | First for 2020-01-01 | 1
2 | Second for 2020-01-01 | 1
3 | First for 2020-02-01 | 2
4 | Second for 2020-02-01 | 2
5 | First for 2020-03-01 | 3
6 | Second for 2020-03-01 | 3
7 | First for 2020-04-01 | 4
8 | Second for 2020-04-01 | 4
9 | (We want this one) First for 2020-05-01 | 5
10 | (We want this one) Second for 2020-05-01 | 5
How can I get all #ProjectHealthReportItems and #ProjectHealthReport details for the last From date which has value for SubmittedOn (so in this case it would be ProjectHealthReport 5 and ProjectHealthReportItems 9, 10).
Basically, I need something like this just, obviously without top 1 as it only returns one row and I need, in this case, to return 2 rows :)
select top 1 phr.Id, phr.[From], phr.SubmittedOn, phri.Note from #ProjectHealthReports phr
inner join #ProjectHealthReportItems phri on phr.Id = phri.ProjectHealthReportId
where phr.SubmittedOn is not null
order by phr.[From] desc
Here is the SQL for creating and seeding the tables
create table #ProjectHealthReports(
Id int primary key,
[From] date not null ,
SubmittedOn date null
)
go
create table #ProjectHealthReportItems(
Id int primary key,
Note nvarchar(max),
ProjectHealthReportId int constraint FK_PHR references #ProjectHealthReports
)
go
insert into #ProjectHealthReports(Id, [From], SubmittedOn)
values (1, '2020-01-01', null),
(2, '2020-02-01', getutcdate()),
(3, '2020-03-01', null),
(4, '2020-04-01', getutcdate()),
(5, '2020-05-01', getutcdate())
go
insert into #ProjectHealthReportItems(Id, Note, ProjectHealthReportId)
values (1, 'First for 2020-01-01', 1),
(2, 'Second for 2020-01-01', 1),
(3, 'First for 2020-02-01', 2),
(4, 'Second for 2020-02-01', 2),
(5, 'First for 2020-03-01', 3),
(6, 'Second for 2020-03-01', 3),
(7, 'First for 2020-04-01', 4),
(8, 'Second for 2020-04-01', 4),
(9, '(We want this one) First for 2020-05-01', 5),
(10, '(We want this one) Second for 2020-05-01', 5)
go
First select top then join
select t.*, phri.Note
from (select top(1) phr.Id phrid, phr.[From], phr.SubmittedOn
from #ProjectHealthReports phr
where phr.SubmittedOn is not null
order by phr.[From] desc) t
inner join #ProjectHealthReportItems phri on t.phrId = phri.ProjectHealthReportId
I would suggest window functions:
select phr.*, phri.*
from #ProjectHealthReports phr left join
(select phri.*,
row_number() over (partition by ProjectHealthReportId order by id desc) as seqnum
from #ProjectHealthReportItems phri
) phri
on phr.Id = phri.ProjectHealthReportId and seqnum = 1
order by phr.[From] desc;
You can also do this using filtering in the where, such as correlated subquery:
select phr.*, phri.*
from #ProjectHealthReports phr join
#ProjectHealthReportItems phri
on phr.Id = phri.ProjectHealthReportId and seqnum = 1
where phri.id = (select max(phri2.id)
from #ProjectHealthReportItems phri2
where phri2.ProjectHealthReportId = phri.ProjectHealthReportId
)
order by phr.[From] desc
An efficient way to do this without a LEFT JOIN would be assign a row number, using the ROW_NUMBER() windowing function, to the #ProjectHealthReports table. Something like this
with lv_cte as (
select *, row_number() over (order by [From] desc) rn
from #ProjectHealthReports)
select l.*, phri.*
from lv_cte l
join #ProjectHealthReportItems phri on l.id=phri.ProjectHealthReportId
where l.rn=1;
Output
Id From SubmittedOn rn Id Note ProjectHealthReportId
5 2020-05-01 2020-10-23 1 9 (We want this one) First for 2020-05-01 5
5 2020-05-01 2020-10-23 1 10 (We want this one) Second for 2020-05-01 5

SELECT check the colum of the max row

Here my row with my first select:
SELECT
user.id, analytic_youtube_demographic.age,
analytic_youtube_demographic.percent
FROM
`user`
INNER JOIN
analytic ON analytic.user_id = user.id
INNER JOIN
analytic_youtube_demographic ON analytic_youtube_demographic.analytic_id = analytic.id
Result:
---------------------------
| id | Age | Percent |
|--------------------------
| 1 |13-17| 19,6 |
| 1 |18-24| 38.4 |
| 1 |25-34| 22.5 |
| 1 |35-44| 11.5 |
| 1 |45-54| 5.3 |
| 1 |55-64| 1.6 |
| 1 |65+ | 1.2 |
| 2 |13-17| 10 |
| 2 |18-24| 10 |
| 2 |25-34| 25 |
| 2 |35-44| 5 |
| 2 |45-54| 25 |
| 2 |55-64| 5 |
| 1 |65+ | 20 |
---------------------------
The max value by user_id:
---------------------------
| id | Age | Percent |
|--------------------------
| 1 |18-24| 38.4 |
| 2 |45-54| 25 |
| 2 |25-34| 25 |
---------------------------
And I need to filter Age in ['25-34', '65+']
I must have at the end :
-----------
| id |
|----------
| 2 |
-----------
Thanks a lot for your help.
Have tried to use MAX(analytic_youtube_demographic.percent). But I don't know how to filter with the age too.
Thanks a lot for your help.
You can use the rank() function to identify the largest percentage values within each user's data set, and then a simple WHERE clause to get those entries that are both of the highest rank and belong to one of the specific demographics you're interested in. Since you can't use windowed functions like rank() in a WHERE clause, this is a two-step process with a subquery or a CTE. Something like this ought to do it:
-- Sample data from the question:
create table [user] (id bigint);
insert [user] values
(1), (2);
create table analytic (id bigint, [user_id] bigint);
insert analytic values
(1, 1), (2, 2);
create table analytic_youtube_demographic (analytic_id bigint, age varchar(32), [percent] decimal(5, 2));
insert analytic_youtube_demographic values
(1, '13-17', 19.6),
(1, '18-24', 38.4),
(1, '25-34', 22.5),
(1, '35-44', 11.5),
(1, '45-54', 5.3),
(1, '55-64', 1.6),
(1, '65+', 1.2),
(2, '13-17', 10),
(2, '18-24', 10),
(2, '25-34', 25),
(2, '35-44', 5),
(2, '45-54', 25),
(2, '55-64', 5),
(2, '65+', 20);
-- First, within the set of records for each user.id, use the rank() function to
-- identify the demographics with the highest percentage.
with RankedDataCTE as
(
select
[user].id,
youtube.age,
youtube.[percent],
[rank] = rank() over (partition by [user].id order by youtube.[percent] desc)
from
[user]
inner join analytic on analytic.[user_id] = [user].id
inner join analytic_youtube_demographic youtube on youtube.analytic_id = analytic.id
)
-- Now select only those records that are (a) of the highest rank within their
-- user.id and (b) either the '25-34' or the '65+' age group.
select
id,
age,
[percent]
from
RankedDataCTE
where
[rank] = 1 and
age in ('25-34', '65+');

Selecting data from 3 tables, grouping by latest date value and another value

Okay, so I've been racking my brains on this for a while, and I think it's time to ask the collective!
I'm using SQLServer and I've got 3 tables, defined as such:
VolumeData
__________________________
| dataid | currentReading|
--------------------------
| 1 | 22 |
| 7 | 33 |
| 9 | 25 |
| 12 | 12 |
--------------------------
LatestData
________________________________________________________________
| dataid | unitNumber | unitLocation | dateTimeStamp |
----------------------------------------------------------------
| 1 | 2344454 | 2 | 2017-07-10 13:16:29.000 |
| 7 | 2344451 | 44 | 2017-07-10 13:22:29.000 |
| 9 | 2344456 | 92 | 2017-07-10 12:16:29.000 |
| 12 | 2344456 | 12 | 2017-07-10 12:13:23.000 |
----------------------------------------------------------------
unitData
____________________________________________________________________________________
| unitNumber | unitLocation | buildingNumber | officeNumber | officeName | country |
------------------------------------------------------------------------------------
| 2344454 | 2 | 44 | 1 | Telford | UK |
| 2344451 | 44 | 22 | 1 | Telford | UK |
| 2344456 | 92 | 12 | 2 | Hamburg | GER |
| 2344456 | 12 | 33 | 2 | Hamburg | GER |
------------------------------------------------------------------------------------
I need to retrieve just the latest currentReading (based on the dateTimeStamp field in LatestData) along with the following fields, grouped on the unitNumber:
currentReading, unitNumber, officeName, country, buildingNumber
One more thing to note is that records can arrive in any order.
The following is one example that I tried, I've tried many more but I've not kept them open unfortunately:
SELECT
a.currentReading
,MAX(b.dateTimeStamp)
,c.unitNumber
,c.country
,c.officeName
FROM [VolumeData] a INNER JOIN LatestData b ON a.dataid = b.dataid INNER JOIN
unitData c ON c.[unitNumber] = b.[unitNumber] AND c.[unitLocation] = b.[unitLocation];
This results in: Column 'VolumeData.currentReading' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Any advice would be much appreciated! Everything I try either results in retrieving far too many rows or results in logical SQL errors. I should also add that these tables contain millions of rows, and grow daily, so I'm looking for a really efficient way to do this.
Thanks!
You can use ROW_NUMBER() to order the date. Then you just take the first one, which correspond to the latest date.
SELECT *
FROM (
SELECT a.currentReading
, b.dateTimeStamp
, c.unitNumber
, c.country
, c.officeName
, ROW_NUMBER() OVER (PARTITION BY c.unitNumber ORDER BY b.dateTimeStamp DESC) AS rowNum
FROM [VolumeData] a
INNER JOIN LatestData b ON a.dataid = b.dataid
INNER JOIN unitData c ON c.[unitNumber] = b.[unitNumber] AND c.[unitLocation] = b.[unitLocation]
) a
WHERE rowNum = 1
Same logic as Eric's answer, probably a bit cleaner using CTE and joins lesser records.
DECLARE #VolumeData TABLE
(
dataid int,
currentReading int
);
INSERT INTO #VolumeData VALUES(1, 22);
INSERT INTO #VolumeData VALUES(7, 33);
INSERT INTO #VolumeData VALUES(9, 25);
INSERT INTO #VolumeData VALUES(12,12);
DECLARE #LatestData TABLE
(
dataid int,
unitNumber int,
unitLocation int,
dateTimeStamp datetime
);
INSERT INTO #LatestData VALUES(1, 2344454, 2, '2017-07-10 13:16:29.000');
INSERT INTO #LatestData VALUES(7, 2344451, 44, '2017-07-10 13:22:29.000');
INSERT INTO #LatestData VALUES(9, 2344456, 92, '2017-07-10 12:16:29.000');
INSERT INTO #LatestData VALUES(12, 2344456, 12, '2017-07-10 12:13:23.000');
DECLARE #UnitData TABLE
(
unitNumber int,
unitLocation int,
buildingNumber int,
officeNumber int,
officeName varchar(50),
country varchar(50)
);
INSERT INTO #UnitData VALUES(2344454, 2, 44, 1, 'Telford', 'UK');
INSERT INTO #UnitData VALUES(2344451, 44, 22, 1, 'Telford', 'UK');
INSERT INTO #UnitData VALUES(2344456, 92, 12, 2, 'Hamburg', 'GER');
INSERT INTO #UnitData VALUES(2344456, 12, 33, 2, 'Hamburg', 'GER');
WITH LatestData_CTE (dataid, unitNumber, unitLocation, dateTimeStamp, rowNum)
AS
(
SELECT dataid
, unitNumber
, unitLocation
, dateTimeStamp
, ROW_NUMBER() OVER (PARTITION BY unitNumber ORDER BY dateTimeStamp DESC) AS rowNum
FROM #LatestData
)
SELECT currentReading, l.unitNumber, officeName, country, buildingNumber
FROM LatestData_CTE l
INNER JOIN #VolumeData v ON v.dataid = l.dataid
INNER JOIN #UnitData u ON u.[unitNumber] = l.[unitNumber] AND u.[unitLocation] = l.[unitLocation]
WHERE l.rowNum = 1
Not complete code, but an advice - It can be implemented by ROW_NUMBER function in CTE
Similar to
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/597b876e-eb00-4013-a613-97c377408668/rownumber-and-cte?forum=transactsql
http://datachix.com/2010/02/10/use-a-common-table-expression-and-the-row_number-function-to-eliminate-duplicate-rows-3/
Just google CTE+ROW_NUMBER to get more examples.
So in CTE you join all required tables and you apply ROW_NUMBER over partition, ordered by dateTimestamp (DESC) and then you use WHERE CTE_name.Rank = 1 in the query that uses that CTE.

Getting the column name that holds the maximum value of a row

I have generated an extensive view which simulates certain occurrences based on different statistic models. These models are defined in each column header by a number and the maximum value of a row is the best model.
The table generated looks (partially) as follows;
+--------+----+------+------+------+------+------+------+
| Number | LI | PHSE | 0505 | 0506 | 0507 | 0508 | 0509 | [...] etc.
+--------+----+------+------+------+------+------+------+
| 100254 | 2 | M1 | 44 | 46 | 45 | 44 | 44 |
| 100254 | 2 | M2 | 36 | 36 | 35 | 37 | 37 |
| 100254 | 2 | M3 | 5 | 5 | 5 | 5 | 5 |
| 100254 | 2 | R1 | 34 | 36 | 37 | 37 | 37 |
| 100254 | 2 | R2 | 41 | 41 | 40 | 41 | 41 |
| 100329 | 1 | M1 | 37 | 38 | 38 | 38 | 39 |
| 100329 | 1 | M2 | 31 | 29 | 28 | 29 | 29 |
| 100329 | 1 | M3 | 6 | 6 | 6 | 6 | 6 |
| 100329 | 1 | R1 | 29 | 29 | 29 | 30 | 30 |
| 100329 | 1 | R2 | 25 | 26 | 26 | 27 | 26 |
+--------+----+------+------+------+------+------+------+
[...] etc.
Now I want to find the highest value in each row and display the corresponding column name as such;
| Number | LI | PHSE | MAXCOL |
+--------+----+------+--------+
| 100254 | 2 | M1 | 0506 |
| 100254 | 2 | M2 | 0508 |
| 100254 | 2 | M3 | 0505 |
| 100254 | 2 | R1 | 0507 |
| 100254 | 2 | R2 | 0505 |
+--------+----+------+--------+
[...] etc.
This is derived from 100254 - 2 - M1 largest value 46 occurring in column 0506, etc.
I've been toying around with PIVOT functions but no success there. I've also looked for an Index/Match equivalent like in Excel but since I can't refer to column headers as values this obviously won't work (and haven't found such function either).
Any help would be hugely appreciated.
UPDATE per Damien's comment:
An excerpt from the code that led to this:
SELECT DISTINCT sub2.Number, sub2.LI, sub2.PHSE
, sum(sub2.[0505]) over (partition by sub2.Number, sub2.LI, sub2.PHSE) as '0505'
, sum(sub2.[0506]) over (partition by sub2.Number, sub2.LI, sub2.PHSE) as '0506'
[...] etc. /*64 rows*/
FROM
(SELECT DISTINCT sub.*
, CASE WHEN sub.MF > sub.[5PAV] - sub.[5PSTDEV] THEN 1 ELSE 0 END AS '0505'
, CASE WHEN sub.MF > sub.[5PAV] - sub.[6PSTDEV] THEN 1 ELSE 0 END AS '0506'
[...] etc. /*64 rows*/
FROM
(SELECT DISTINCT ra.*
, sum(ra.qtyr) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 5 preceding AND 1 preceding) /
sum(ra.qtyu) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 5 preceding AND 1 preceding) AS '5PAV'
, sum(ra.qtyr) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 6 preceding AND 1 preceding) /
sum(ra.qtyu) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 6 preceding AND 1 preceding) AS '6PAV'
[...] etc. /*8 rows*/
, stdev(ra.MF) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 4 preceding AND CURRENT row) AS '5PSTDEV'
, stdev(ra.MF) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 5 preceding AND CURRENT row) AS '6PSTDEV'
[...] etc. /*8 rows*/
FROM ra
) AS sub
) AS sub2
No doubt that this may be one of the most ineffective uses of SQL, but time-pressure and inexperience made me write it this way.
Any suggestions to change this code and achieve the desired table result more efficiently would be much appreciated too.
EDIT per Anton's answer;
The above code summarized as pvt continues as follows;
SELECT Number, LI, PHSE, combo, hitrate
FROM (...) AS pvt
UNPIVOT
(Hitrate FOR Combo IN (
[0505],
[0506],
[...] etc.)) AS upvt
Great solution to an inelegant problem.
You need to use UNPIVOT, not PIVOT
https://msdn.microsoft.com/en-us/library/ms177410.aspx
If the number of columns is variable, you have to use dynamic sql to construct the list of columns
If you want to see all MAXCOL (equal columns) try this:
CREATE TABLE table1 (
Number NUMERIC(10),
LI numeric(10),
PHSE NVARCHAR(10),
[0505] numeric(10),
[0506] numeric(10),
[0507] numeric(10),
[0508] numeric(10),
[0509] numeric(10))
INSERT INTO table1 VALUES(100254,2,'M1',44 ,46 ,45 ,44,44)
INSERT INTO table1 VALUES(100254,2,'M2',36,36,35,37,37)
INSERT INTO table1 VALUES(100254,2,'M3',5,5,5,5,5)
INSERT INTO table1 VALUES(100254,2,'R1',34,36,37,37,37)
INSERT INTO table1 VALUES(100254,2, 'R2',41,41,40,41,41)
INSERT INTO table1 VALUES(100329,1, 'M1',37,38,38,38,39)
INSERT INTO table1 VALUES(100329,1, 'M2',31,29,28,29,29)
INSERT INTO table1 VALUES(100329,1, 'M3',6,6,6,6,6)
INSERT INTO table1 VALUES(100329,1, 'R1',29,29,29,30,30)
INSERT INTO table1 VALUES(100329,1, 'R2',25,26,26,27,26)
SELECT *
INTO #UNPIVOT
FROM table1
UNPIVOT ( num
FOR MAXCOL IN ([0505],[0506],[0507],[0508],[0509])) AS k
SELECT A.Number,A.LI,A.PHSE,A.num,B.MAXCOL FROM
(SELECT number,LI,PHSE,MAX(num) AS num FROM #UNPIVOT GROUP BY number,LI,PHSE) A
LEFT JOIN
(SELECT * FROM #UNPIVOT) B ON A.num=B.num AND A.Number=B.Number AND A.LI=B.LI AND A.PHSE=B.PHSE
I would use CROSS APPLY as follows:
WITH Src AS
(
SELECT * FROM (VALUES
(100254, 2, 'M1', 44, 46, 45, 44, 44),
(100254, 2, 'M2', 36, 36, 35, 37, 37),
(100254, 2, 'M3', 5, 5, 5, 5, 5),
(100254, 2, 'R1', 34, 36, 37, 37, 37),
(100254, 2, 'R2', 41, 41, 40, 41, 41),
(100329, 1, 'M1', 37, 38, 38, 38, 39),
(100329, 1, 'M2', 31, 29, 28, 29, 29),
(100329, 1, 'M3', 6, 6, 6, 6, 6),
(100329, 1, 'R1', 29, 29, 29, 30, 30),
(100329, 1, 'R2', 25, 26, 26, 27, 26)) T(Number, LI, PHSE, [0505], [0506], [0507], [0508], [0509])
)
SELECT Number, LI, PHSE, MaxCol
FROM Src
CROSS APPLY (SELECT TOP 1 * FROM (VALUES
('0505', [0505]),
('0506', [0506]),
('0507', [0507]),
('0508', [0508]),
('0509', [0509])
) T(MaxCol, Val) ORDER BY Val DESC) Q
Note that equal columns may be chosen randomly unless some additional value is specified, i.e. MaxCol.
Update
You need dynamic query, like following one:
DECLARE #sql nvarchar(MAX) =
'SELECT Number, LI, PHSE, MaxCol
FROM Src
CROSS APPLY (SELECT TOP 1 * FROM (VALUES' +STUFF(
(SELECT ',(', QUOTENAME(name, '''')+','+QUOTENAME(name)+')'
FROM sys.columns
WHERE object_id=OBJECT_ID('Src') AND name NOT IN ('Number', 'LI', 'PHSE')
FOR XML PATH('')), 1, 1, '')+') T(MaxCol, Val) ORDER BY Val DESC) Q';
EXEC(#sql);
Src is your table name, replace it accordingly.
You can use UNPIVOT and CROSS APPLY.
With this solution you don't have to specify all the columns multiple times.
Source data:
CREATE TABLE Student
([Name] varchar(5), [Maths] int, [Science] int, [English] int)
;
INSERT INTO Student
([Name], [Maths], [Science], [English])
VALUES
('Tilak', 90, 40, 60),
('Raj', 30, 50, 70)
;
Solution:
with foo as (
select name, subject, marks
from student
unpivot
(
marks
for subject in (Science, Maths, English)
) u
)
select distinct f1.name, f2.subject
from foo f1
cross apply (
select top 1 name, subject
from foo
where f1.Name = Name
order by Marks desc) f2
Result:
--------------------------------------------------
| Name | Subject
--------------------------------------------------
| Tilak | Maths
--------------------------------------------------
| Raj | Science
--------------------------------------------------
http://sqlfiddle.com/#!18/780cd8/5