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

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

Related

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.

LAG works, LEAD returning 1 within the same statement

I am puzzled over LEAD returning always ID=1, which is not even an ID in the table (ID start at ~18k), instead of a valid ID for the next record. NULL values are where they supposed to be, it's just the rows, which supposed to contain a valid ID. The LAG with the same syntax works as expected, returning correct values. The LEAD doesn't even work (correctly), when I comment out LAG.
By the way, I copied the code (and just changed table and column names) from my other script, where it was working fine.
UPDATE PRJ SET
PrevJob = SRC.PrevID, -- << write Previous ID
NextJob = SRC.NextID -- << write Next ID
FROM PRJ as PRJ
LEFT JOIN (
SELECT
ID, -- << ID for joining to the original record
LAG(ID) OVER (PARTITION BY RPCode,PNInt ORDER BY OrderNo) AS PrevID, -- << previous works
LEAD(ID) OVER (PARTITION BY RPCode,PNInt ORDER BY OrderNo) AS NextID -- << next returns 1
FROM PRJ as PRJ2
) as SRC ON SRC.ID = PRJ.ID
Here is a sample of results using SELECT (see the 1 values in the last column):
ID PNInt RPCode OrderNo PrevJob NextJob
-------- ----------- -------------------- ------- -------------------- -------
18783 53 00005320171113120000 1 NULL 1
18795 53 00005320171113120000 2 18783 1
18789 53 00005320171113120000 3 18795 NULL
18784 53 00005320171127120000 1 NULL 1
18796 53 00005320171127120000 2 18784 1
18790 53 00005320171127120000 3 18796 NULL
18785 53 00005320171211120000 1 NULL 1
18797 53 00005320171211120000 2 18785 1
18791 53 00005320171211120000 3 18797 NULL
18786 53 00005320171225120000 1 NULL 1
18798 53 00005320171225120000 2 18786 1
18792 53 00005320171225120000 3 18798 NULL
18787 53 00005320180108120000 1 NULL 1
18799 53 00005320180108120000 2 18787 1
18793 53 00005320180108120000 3 18799 NULL
I fear it might be some dumb typo mistake I am blind to see. Or are there any catches with LAG and LEAD?
First, you can write this much more simply as:
UPDATE toupdate
SET PrevJob = SRC.PrevID, -- << write Previous ID
NextJob = SRC.NextID -- << write Next ID
FROM (SELECT PRJ.*,
LAG(ID) OVER (PARTITION BY RPCode, PNInt ORDER BY OrderNo) AS new_PrevID, -- << previous works
LEAD(ID) OVER (PARTITION BY RPCode, PNInt ORDER BY OrderNo) AS new_NextID -- << next returns 1
FROM PRJ
) toupdate;
I cannot explain the behavior that you see. Two things to check:
The type of the id fields.
Any triggers that might be defined on the table.
Unable to replicate you issue, see this SQL Fiddle
MS SQL Server 2014 Schema Setup:
CREATE TABLE PRJ
([ID] int, [PNInt] int, [RPCode] varchar(20), [OrderNo] int, [PrevJ] varchar(5), [NextJ] varchar(4))
;
INSERT INTO PRJ
([ID], [PNInt], [RPCode], [OrderNo], [PrevJ], [NextJ])
VALUES
(18783, 53, 'O0005320171113120000', 1, NULL, '1'),
(18795, 53, 'O0005320171113120000', 2, '18783', '1'),
(18789, 53, 'O0005320171113120000', 3, '18795', NULL),
(18784, 53, 'O0005320171127120000', 1, NULL, '1'),
(18796, 53, 'O0005320171127120000', 2, '18784', '1'),
(18790, 53, 'O0005320171127120000', 3, '18796', NULL),
(18785, 53, 'O0005320171211120000', 1, NULL, '1'),
(18797, 53, 'O0005320171211120000', 2, '18785', '1'),
(18791, 53, 'O0005320171211120000', 3, '18797', NULL),
(18786, 53, 'O0005320171225120000', 1, NULL, '1'),
(18798, 53, 'O0005320171225120000', 2, '18786', '1'),
(18792, 53, 'O0005320171225120000', 3, '18798', NULL),
(18787, 53, 'O0005320180108120000', 1, NULL, '1'),
(18799, 53, 'O0005320180108120000', 2, '18787', '1'),
(18793, 53, 'O0005320180108120000', 3, '18799', NULL)
;
Query 1:
SELECT
ID, -- << ID for joining to the original record
LAG(ID) OVER (PARTITION BY RPCode,PNInt ORDER BY OrderNo) AS PrevID, -- << previous works
LEAD(ID) OVER (PARTITION BY RPCode,PNInt ORDER BY OrderNo) AS NextID -- << next returns 1
FROM PRJ as PRJ2
Results:
| ID | PrevID | NextID |
|-------|--------|--------|
| 18783 | (null) | 18795 |
| 18795 | 18783 | 18789 |
| 18789 | 18795 | (null) |
| 18784 | (null) | 18796 |
| 18796 | 18784 | 18790 |
| 18790 | 18796 | (null) |
| 18785 | (null) | 18797 |
| 18797 | 18785 | 18791 |
| 18791 | 18797 | (null) |
| 18786 | (null) | 18798 |
| 18798 | 18786 | 18792 |
| 18792 | 18798 | (null) |
| 18787 | (null) | 18799 |
| 18799 | 18787 | 18793 |
| 18793 | 18799 | (null) |

Empty column values but keep values equal to the number provided using SQL Query

Suppose the following table
ID Name RowNumber
2314 YY 1
213 XH 2
421 XD 3
123 AA 4
213 QQQ 5
12 WW 6
312 RR 7
123 GG 8
12 F 9
12 FF 10
312 VV 11
12 BB 12
32 NN 13
43 DD 14
53 DD 15
658 QQQQ 16
768 GGG 17
I want to replace the Name field with empty string based on condition that
First and Last cells value will not be removed.
Need to return values not in continuous cells.
Only n number of cells will be preserved
if n is less than or equal to the number entered by user than do nothing
For example, if user enters 5 then only 5 values will be preserved and the result should be (OR similar)-
ID Name RowNumber
2314 YY 1
213 2
421 3
123 AA 4
213 5
12 6
312 7
123 GG 8
12 9
12 10
312 11
12 12
32 NN 13
43 14
53 15
658 16
768 GGG 17
There could be more records than this.
I'm using SQL Server
The following will work in SQL Server 2012+, because it uses running/cumulative SUM. The query assumes that values in RowNumber column are sequential from 1 to total row count without gaps. If your data is not like this, you can use ROW_NUMBER to generate them.
Calculate Ratio of the given number N and total number of rows (CTE_Ratio)
Calculate running sum of this Ratio, truncating the fractional part of the sum (CTE_Groups)
Each integer value of the running rum defines a group of rows, re-number rows within each group (CTE_Final)
Preserve Name only for the first row from each group
To understand better how it works include intermediate columns (Ratio, GroupNumber, rn) into the output
SQL Fiddle
Sample data
DECLARE #T TABLE ([ID] int, [Name] varchar(50), [RowNumber] int);
INSERT INTO #T([ID], [Name], [RowNumber]) VALUES
(2314, 'YY', 1)
,(213, 'XH', 2)
,(421, 'XD', 3)
,(123, 'AA', 4)
,(213, 'QQQ', 5)
,(12, 'WW', 6)
,(312, 'RR', 7)
,(123, 'GG', 8)
,(12, 'F', 9)
,(12, 'FF', 10)
,(312, 'VV', 11)
,(12, 'BB', 12)
,(32, 'NN', 13)
,(43, 'DD', 14)
,(53, 'DD', 15)
,(658, 'QQQQ', 16)
,(768, 'GGG', 17);
DECLARE #N int = 5;
Query
WITH
CTE_Ratio AS
(
SELECT
ID
,Name
,RowNumber
,COUNT(*) OVER() AS TotalRows
,CAST(#N-1 AS float) / CAST(COUNT(*) OVER() AS float) AS Ratio
FROM #T
)
,CTE_Groups AS
(
SELECT
ID
,Name
,RowNumber
,TotalRows
,ROUND(SUM(Ratio) OVER(ORDER BY RowNumber), 0, 1) AS GroupNumber
FROM CTE_Ratio
)
,CTE_Final AS
(
SELECT
ID
,Name
,RowNumber
,TotalRows
,ROW_NUMBER() OVER(PARTITION BY GroupNumber ORDER BY RowNumber) AS rn
FROM CTE_Groups
)
SELECT
ID
,CASE WHEN rn=1 OR RowNumber = TotalRows THEN Name ELSE '' END AS Name
,RowNumber
FROM CTE_Final
ORDER BY RowNumber;
Result
+------+------+-----------+
| ID | Name | RowNumber |
+------+------+-----------+
| 2314 | YY | 1 |
| 213 | | 2 |
| 421 | | 3 |
| 123 | | 4 |
| 213 | QQQ | 5 |
| 12 | | 6 |
| 312 | | 7 |
| 123 | | 8 |
| 12 | F | 9 |
| 12 | | 10 |
| 312 | | 11 |
| 12 | | 12 |
| 32 | NN | 13 |
| 43 | | 14 |
| 53 | | 15 |
| 658 | | 16 |
| 768 | GGG | 17 |
+------+------+-----------+
Try this:
--Number that user enter
DECLARE #InputNumber INT
DECLARE #WorkingNumber INT
DECLARE #TotalRecords INT
DECLARE #Devider INT
SET #InputNumber = 5
SET #WorkingNumber = #InputNumber -2
--Assume #InputNumber greater than 2 and #TotalRecords greater than 4
SELECT #TotalRecords = COUNT(*)
FROM Table;
SET #Devider = CONVERT(#TotalRecords, DECIMAL(18,2))/CONVERT(#WorkingNumber, DECIMAL(18,2));
WITH Conditioned (RowNumber)
AS
(
SELECT RowNumber
FROM Table
WHERE RowNumber = 1
UNION
SELECT T.RowNumber
FROM (SELECT TOP 1 RowNumber
FROM Conditioned
ORDER BY RowNumber DESC) AS C
INNER JOIN Table AS T ON CONVERT(CEILING(C.RowNumber + #Devider), INT) = T.RowNumber
)
SELECT T.Id, CASE WHEN C.RowNumber IS NULL THEN '' ELSE T.Name END, T.RowNumber
FROM Table T
LEFT OUTER JOIN Conditioned C ON T.RowNumber = C.RowNumber
WHERE
UNION RowNumber != #TotalRecords
SELECT Id, Name, RowNumber
FROM Table
WHERE RowNumber = #TotalRecords

Filter SQL Query?

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

SQL Unpivot multiple columns Data

I am using SQL server 2008 and I am trying to unpivot the data. Here is the SQL code that I am using,
CREATE TABLE #pvt1 (VendorID int, Sa int, Emp1 int,Sa1 int,Emp2 int)
GO
INSERT INTO #pvt1 VALUES (1,2,4,3,9);
GO
--Unpivot the table.
SELECT distinct VendorID,Orders,Orders1
FROM
(SELECT VendorID, Emp1, Sa,Emp2,Sa1
FROM #pvt1 ) p
UNPIVOT
(Orders FOR Emp IN
(Emp1,Emp2)
)AS unpvt
UNPIVOT
(Orders1 FOR Emp1 IN
(Sa,Sa1)
)AS unpvt1;
GO
And Here is the result of the above code.
VendorID Orders Orders1
1 4 2
1 4 3
1 9 2
1 9 3
But I want my Output to be the way indicated below
VendorID Orders Orders1
1 4 2
1 9 3
The relationship from the above code is 2 is related to 4, and 3 is related to 9.
How can I achieve this?
An easier way to unpivot the data would be to use a CROSS APPLY to unpivot the columns in pairs:
select vendorid, orders, orders1
from pvt1
cross apply
(
select emp1, sa union all
select emp2, sa1
) c (orders, orders1);
See SQL Fiddle with Demo. Or you can use CROSS APPLY with the VALUES clause if you don't want to use the UNION ALL:
select vendorid, orders, orders1
from pvt1
cross apply
(
values
(emp1, sa),
(emp2, sa1)
) c (orders, orders1);
See SQL Fiddle with Demo
The answer by Taryn is indeed super useful, and I'd like to expand one aspect of it.
If you have a very un-normalized table like this, with multiple sets of columns for e.g. 4 quarters or 12 months:
+-------+------+------+------+------+------+------+-------+------+
| cYear | foo1 | foo2 | foo3 | foo4 | bar1 | bar2 | bar3 | bar4 |
+-------+------+------+------+------+------+------+-------+------+
| 2020 | 42 | 888 | 0 | 33 | one | two | three | four |
+-------+------+------+------+------+------+------+-------+------+
Then the CROSS APPLY method is easy to write and understand, when you got the hang of it. For the numbered column, use constant values.
SELECT
cYear,
cQuarter,
foo,
bar
FROM temp
CROSS APPLY
(
VALUES
(1, foo1, bar1),
(2, foo2, bar2),
(3, foo3, bar3),
(4, foo4, bar4)
) c (cQuarter, foo, bar)
Result:
+-------+----------+-----+-------+
| cYear | cQuarter | foo | bar |
+-------+----------+-----+-------+
| 2020 | 1 | 42 | one |
| 2020 | 2 | 888 | two |
| 2020 | 3 | 0 | three |
| 2020 | 4 | 33 | four |
+-------+----------+-----+-------+
SQL Fiddle
I needed composit key AND skip extras row in case when data is missing (NULLs). For ex. when x2 and y2 are possible replacement vendor and price
WITH pvt AS (SELECT * FROM (VALUES
( 1, 6, 11, 111, 12, 13, 122, 133),
( 2, 6, 21, 211, 22, 23, 222, 233),
( 3, 6, 31, 311, 32, 33, 322, 333),
( 5, 4, 41, 411, 42, NULL, 422, NULL),
( 6, 4, 51, 511, 52, NULL, 522, NULL))
s( id, s, a, b, x1, x2, y1, y2)
)
-- SELECT * FROM pvt
SELECT CONCAT('xy_',s,'_', id, postfix) as comp_id, a, b, x, y
FROM pvt
CROSS APPLY
(
VALUES
(NULL, x1, y1),
('_ext', x2, y2)
) c (postfix, x, y)
WHERE x IS NOT NULL
produces
comp_id a b x y
-------------------------------- ----------- ----------- ----------- -----------
xy_6_1 11 111 12 122
xy_6_1_ext 11 111 13 133
xy_6_2 21 211 22 222
xy_6_2_ext 21 211 23 233
xy_6_3 31 311 32 322
xy_6_3_ext 31 311 33 333
xy_4_5 41 411 42 422
xy_4_6 51 511 52 522
(8 rows affected)
from:
id s a b x1 x2 y1 y2
----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
1 6 11 111 12 13 122 133
2 6 21 211 22 23 222 233
3 6 31 311 32 33 322 333
5 4 41 411 42 NULL 422 NULL
6 4 51 511 52 NULL 522 NULL
(5 rows affected)