choosing row (from group) with max value in a SQL Server Database - sql-server

I have a large database and am putting together a report of the data. I have aggregated and summed the data from many tables to get two tables that look like the following.
id | code | value id | code | value
13 | AA | 0.5 13 | AC | 2.0
13 | AB | 1.0 14 | AB | 1.5
14 | AA | 2.0 13 | AA | 0.5
15 | AB | 0.5 15 | AB | 3.0
15 | AD | 1.5 15 | AA | 1.0
I need to get a list of id's, with the code (sumed from both tables) with the largest value.
13 | AC
14 | AA
15 | AB
There are 4-6 thousand records and it is not possible to change the original tables. I'm not too worried about performance as I only need to run it a few times a year.
edit:
Let me see if I can explain a bit more clearly, imagine the id is the customer id, the code is who they ordered from and the value is how much they spent there.
I need a list of the all the customer id's and the store that customer spent the most money at (and if they spent the same at two different stores, put a value such as 'ZZ' in for the store name).

try this:
DECLARE #Table1 table (id int, code char(2), value decimal(5,1))
INSERT #Table1 VALUES (13 , 'AA' , 0.5)
INSERT #Table1 VALUES (13 , 'AB' , 1.0)
INSERT #Table1 VALUES (14 , 'AA' , 2.0)
INSERT #Table1 VALUES (15 , 'AB' , 0.5)
INSERT #Table1 VALUES (15 , 'AD' , 1.5)
DECLARE #Table2 table (id int, code char(2), value decimal(5,1))
INSERT #Table2 VALUES (13 , 'AC' , 2.0)
INSERT #Table2 VALUES (14 , 'AB' , 1.5)
INSERT #Table2 VALUES (13 , 'AA' , 0.5)
INSERT #Table2 VALUES (15 , 'AB' , 3.0)
INSERT #Table2 VALUES (15 , 'AA' , 1.0)
SELECT
dt.id, MAX(dt.code) AS code, sum(dt.value) as value
from (select id, code, value
from #Table1
UNION ALL
select
id, code, value
from #Table2
) dt
group by dt.id
order by id
OUTPUT:
id code value
----------- ---- ---------------------------------------
13 AC 4.0
14 AB 3.5
15 AD 6.0
(3 row(s) affected)
I'm not sure what you are after? this is the MAX code per id summing the value. if this is not what you are after please specify i nthe question more clearly
EDIT after OP's edit, using same tables as code from above:
;WITH AllTAbles AS
(select
id, code, value
from #Table1
UNION ALL
select
id, code, value
from #Table2
)
, MaxValues AS
(SELECT
dt.id, MAX(dt.value) as MaxValue, SUM(dt.value) AS SumValue
from AllTAbles dt
group by dt.id
)
, StoreCount AS
(SELECT
a.id,a.Code, COUNT(*) AS StoreCount
FROM AllTAbles a
INNER JOIN MaxValues m ON a.id=m.id AND a.value=m.MaxValue
GROUP BY a.id,a.Code
)
SELECT
s.id
,CASE
WHEN s.StoreCount=1 THEN s.Code
ELSE 'ZZ'
END AS code
,m.SumValue
FROM StoreCount s
INNER JOIN MaxValues m ON s.id=m.id
ORDER BY s.id
OUTPUT:
id code SumValue
----------- ---- ----------
13 AC 4.0
14 AA 3.5
15 AB 6.0
(3 row(s) affected)
OP doesn't say the version of SQL Server, so here is a pre SQL Server 2005 version that does not use CTEs, has same output as the CTE version above:
SELECT
s.id
,CASE
WHEN s.StoreCount=1 THEN s.Code
ELSE 'ZZ'
END AS code
,s.SumValue
FROM (SELECT
a.id,a.Code, COUNT(*) AS StoreCount, m.SumValue
FROM (select
id, code, value
from #Table1
UNION ALL
select
id, code, value
from #Table2
) a
INNER JOIN (SELECT
dt.id, MAX(dt.value) as MaxValue, SUM(dt.value) AS SumValue
from (select
id, code, value
from #Table1
UNION ALL
select
id, code, value
from #Table2
) dt
group by dt.id
) m ON a.id=m.id AND a.value=m.MaxValue
GROUP BY a.id,a.Code,m.SumValue
) s
ORDER BY s.id

select id, code, sum(value) as value
from
(
select id, code, value
from yyy
UNION
select id, code, value
from zzz
) aaa
group by id, code
order by sum(value)
or this removing id from the grouping:
select code, sum(value) as value
from
(
select id, code, value
from yyy
UNION
select id, code, value
from zzz
) aaa
group by code
order by sum(value)

Related

Generate rows within table based on existing rows

Is it possible to generate rows within a table based on existing column in the table in SQL Server? For example, if RunDates value = 31/01/2020 and RunTimes = 3, then there should be 3 rows in the table for RunDate = 31/01/2020
Current table
Desired table
You can do it with a recursive CTE:
with cte as (
select RunDates, RunTimes, 1 nr from tablename
union all
select RunDates, RunTimes, nr + 1
from cte
where nr < RunTimes
)
select RunDates, RunTimes from cte
order by RunDates
See the demo.
Results:
> RunDates | RunTimes
> :--------- | -------:
> 2020-01-31 | 3
> 2020-01-31 | 3
> 2020-01-31 | 3
> 2020-02-29 | 2
> 2020-02-29 | 2
> 2020-03-31 | 1
First you need a tally table (view) e.g.
CREATE View [dbo].[cteTally]
as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n))
, E2(N) AS (SELECT 1 FROM E1 a, E1 b) --10E+2 or 100 rows
, E4(N) AS (SELECT 1 FROM E2 a, E2 b) --10E+4 or 10,000 rows max
, cteTally(N) AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
GO
Then you join it on to your table e.g.
declare #MyTable table (MyDate date, RunCount int);
insert into #MyTable (MyDate, RunCount)
values
('31 Jan 2020', 3),
('29 Feb 2020', 2),
('31 Mar 2020', 1);
select MT.*
from #MyTable MT
inner join cteTally T on T.N <= MT.RunCount
order by MyDate, RunCount;
Returns:
MyDate RunCount
--------------------
2020-01-31 3
2020-01-31 3
2020-01-31 3
2020-02-29 2
2020-02-29 2
2020-03-31 1
NOTE: The Tally Table is courtesy of #Larnu but I can't find the original post.
You can try the following inner join by creating a serial number from sys.objects.
Here I have fixed 10 assuming the maximum value of RunTime. You can create a variable and assign the maximum value of the RunTime value and use that variable in place of 10.
Here is an another way to do that.
create table SampleTable (DtDate Date, RunTimes int)
insert into SampleTable Values
('31 Jan 2020', 3),
('29 Feb 2020', 2),
('31 Mar 2020', 1)
SELECT SampleTable.*
FROM SampleTable
INNER JOIN (
SELECT TOP 10 ROW_NUMBER() OVER (
ORDER BY object_id
) AS SrNo
FROM sys.objects
) mst ON RunTimes >= SrNo
ORDER BY DtDate
Live db<>fiddle demo.

How to get previous record if current record does not exist in table

my table like this
Id Date type quantity
1 29/04/2019 APPLE 2
2 29/04/2019 Banana 15
3 29/04/2019 Mango 100
4 29/04/2019 Grapes 50
5 29/04/2019 Fish 80
6 30/04/2019 APPLE 4
7 30/04/2019 Grapes 100
8 30/04/2019 Fish 90
9 01/05/2019 APPLE 6
10 01/05/2019 Banana 30
11 01/05/2019 Grapes 150
12 01/05/2019 Fish 100
13 02/05/2019 Mango 200
14 02/05/2019 Grapes 200
15 02/05/2019 Fish 110
16 03/05/2019 APPLE 8
17 03/05/2019 Banana 45
18 03/05/2019 Mango 300
19 04/05/2019 APPLE 10
20 04/05/2019 Grapes 300
21 04/05/2019 Fish 120
22 05/05/2019 APPLE 12
23 05/05/2019 Fish 130
i miss some inputs every day,But i need to fill the gaps with previous row of the same "Type" on 30/04/2019 i missed "Banana & Mango" bu i need like
Id Date type quantity
1 29/04/2019 APPLE 2
2 29/04/2019 Banana 15
3 29/04/2019 Mango 100
4 29/04/2019 Grapes 50
5 29/04/2019 Fish 80
6 30/04/2019 APPLE 4
7 30/04/2019 Grapes 100
8 30/04/2019 Fish 90
9 30/04/2019 Banana 15
10 30/04/2019 Mango 100
actually last two rows are null but it should updated same on 29/04/2019
I think the easiets way might be this:
DECLARE #PDate DATE = SELECT TOP 1 Date FROM YourTable ORDER BY Date ASC --Previous Date
DECLARE #NDate DATE = SELECT TOP 1 Date FROM YourTable WHERE DATE>#PDate --Next Date
WHILE (#NDate IS NOT NULL)
BEGIN
WITH X AS
(
SELECT T1.Date AS Date1, T1.Type AS Type1, T1.Quantity AS Q1
T2.Date AS Date2, T2.Type AS Type2, T2.Quantity AS Q2
FROM YourTable T1
LEFT JOIN YourTable T2 ON T1.Type = T2.Type
WHERE T1.Date = #PDate AND T2.Date = #NDate
)
INSERT INTO YourTable (Date,Type,Quantity)
SELECT #NDate,Type1,Q1
WHERE X.Type2 IS NULL
SET #PDate = #NDate
SET #NDate = NULL -- If next result wasnt found this stays null for while condition
SET #NDate = SELECT TOP 1 Date FROM YourTable WHERE Date>#PDate
END
I think this is the way that may work and I wish so
( if there is any syntax or ... mistakes its because I didnt have SSMS installed to test. Sorry)
try this :
declare #date date
and for initiate #date you can use select #date=max(date) from table1 or pass static value set #date='02/01/2019'
and then find input
select input,max(date) as MaxDate into #temp
from table1
where input not in (select input from table1 where date=#date )
group by input
then :
select t.* from Table1 t join #temp on Table1.input=#temp.Input and Table1.date=#temp.MaxDate
OK, after the goal posts are settled, this is one method. Note that this solution builds both a Types and Dates dataset. Really the Types dataset should already exist somewhere in your database, and you should create a Calendar Table if you're going to be doing this type of work often.
Any way, I've left comments in the code for you. I've assumed you're using SQL Server 2012+, as 2008 is literally about to run out of support.
CREATE TABLE dbo.MyTable (ID int IDENTITY(1,1),
[date] date,
[type] varchar(10),
Quantity int);
INSERT INTO dbo.MyTable
SELECT CONVERT(date,[date],103),
RTRIM([Type]),
Quantity
FROM (VALUES('29/04/2019','APPLE ',2),
('29/04/2019','Banana',15),
('29/04/2019','Mango ',100),
('29/04/2019','Grapes',50),
('29/04/2019','Fish ',80),
('30/04/2019','APPLE ',4),
('30/04/2019','Grapes',100),
('30/04/2019','Fish ',90),
('01/05/2019','APPLE ',6),
('01/05/2019','Banana',30),
('01/05/2019','Grapes',150),
('01/05/2019','Fish ',100),
('02/05/2019','Mango ',200),
('02/05/2019','Grapes',200),
('02/05/2019','Fish ',110),
('03/05/2019','APPLE ',8),
('03/05/2019','Banana',45),
('03/05/2019','Mango ',300),
('04/05/2019','APPLE ',10),
('04/05/2019','Grapes',300),
('04/05/2019','Fish ',120),
('05/05/2019','APPLE ',12),
('05/05/2019','Fish ',130)) V([date],[Type],Quantity);
GO
--SELECT *
--FROM dbo.MyTable;
GO
--Create a calendar table
WITH N AS (
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)) N(N)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I
FROM N N1, N N2, N N3), --1000 days shuld be enough
Dates AS(
SELECT DATEADD(DAY, T.I, MIN(MT.[date])) AS [Date]
FROM Tally T
CROSS JOIN dbo.MyTable MT
GROUP BY T.I
HAVING DATEADD(DAY, T.I, MIN(MT.[date])) <= MAX([Date])),
--Get Types
Types AS (
SELECT DISTINCT [Type]
FROM dbo.MyTable MT),
--Create islands
Grps AS(
SELECT MT.ID,
D.[Date],
T.[Type],
MT.Quantity,
COUNT(MT.Quantity) OVER (PARTITION BY T.[Type] ORDER BY D.[date]
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Grp
FROM Dates D
CROSS JOIN Types T
LEFT JOIN dbo.MyTable MT ON D.[Date] = MT.[date]
AND T.[type] = MT.[type])
SELECT G.ID AS ID,
ROW_NUMBER() OVER (ORDER BY G.[Date], G.[Type]) AS RN,
G.[Date],
G.[Type],
MAX(G.Quantity) OVER (PARTITION BY G.[Type], G.Grp) AS Quantity
FROM Grps G
ORDER BY G.[Date],
G.[Type];
GO
DROP TABLE dbo.MyTable;
db<>fiddle
I think using cursor is a good option to insert your missing entries in the table. By cursor you will be able to check date wise missing types and insert them with the previous quantity.
You can also use this following script to find the missing records in your table. To create the script I consider the table name = 'add_missing_records'
SELECT AA.date AS [Date],
AA.type AS [Type],
BB.quantity AS [Original Quantity] ,
CASE
WHEN BB.quantity IS NULL THEN
(
SELECT quantity
FROM add_missing_records C
WHERE C.date = (
SELECT MAX([date])
FROM add_missing_records B
WHERE B.date < AA.date
AND B.type = AA.type
)
AND C.type = AA.type
)
ELSE BB.quantity
END AS [New Quantuty]
FROM (
SELECT date,type
FROM (
SELECT DISTINCT 'A' AS common,date
FROM add_missing_records
)A
FULL JOIN (
SELECT DISTINCT 'A' as common, type
FROM add_missing_records
)B
ON a.common = b.common
) AA
LEFT JOIN add_missing_records BB
ON AA.date = BB.date
AND AA.type = BB.type
WHERE BB.quantity IS NULL
ORDER BY 1,2

If Value is present in two consecutive months , display only one month in sql

I would want to check ID in consecutive months, IF Same ID is present in two consecutive months then consider that ID only for 1st month.
If ID's are not in consecutive month then show the distinct ID's grouped by start date month.(We consider only start date)
For example, ID 1 is present in start date months january and Feb , then Distinct count of this ID will be 1 in Jan, how ever ID 2 and 3 are
present in Jan and March and Feb and May Resp, now I would like to see this distinct count of ID in Jan and March.
Current Data
Table1:
ID StartDate EndDate
1 2017-01-12 2017-01-28
1 2017-01-19 2017-01-28
1 2017-01-29 2017-02-11
1 2017-02-01 2017-02-11
1 2017-02-19 2017-02-24
2 2017-01-12 2017-01-28
2 2017-01-19 2017-01-28
2 2017-03-09 2017-03-20
3 2017-02-12 2017-02-28
3 2017-02-19 2017-02-28
3 2017-05-05 2017-05-29
3 2017-05-09 2017-05-29
I tried with below logic bt I know I am missing on something here.
select t.* from Table1 t
join Table1 t t1
on t1.ID=t.ID
and datepart(mm,t.StartDate)<> datepart(mm,t1.StartDate)+1
Expected Result:
DistinctCount StartDateMonth(In Numbers)
1 1(Jan)
2 1(Jan)
2 3(March)
3 2(Feb)
3 5(May)
Any help is appreciated!
Here's my solution. The thinking for this is:
1) Round all the dates to the first of the month, then work with the distinct dataset of (ID, StartDateRounded). From your dataset, the result should look like this:
ID StartDateRounded
1 2017-01-01
1 2017-02-01
2 2017-01-01
2 2017-03-01
3 2017-02-01
3 2017-05-01
2) From this consolidated dataset, find all records by ID that do not have a record for the previous month (which means it's not a consecutive month and thus is a beginning of a new data point). This is your final dataset
with DatesTable AS
(
SELECT DISTINCT ID
,DATEADD(month,DateDiff(month,0,StartDate),0) StartDateRounded
,DATEADD(month,DateDiff(month,0,StartDate)+1,0) StartDateRoundedPlusOne
FROM Table1
)
SELECT t1.ID, DatePart(month,t1.StartDateRounded) AS StartDateMonth
FROM DatesTable t1
LEFT JOIN DatesTable t2
ON t1.ID = t2.ID
AND t1.StartDateRounded = t2.StartDateRoundedPlusOne
WHERE t2.ID IS NULL; --Verify no record exists for prior month
sqlfiddler for reference. Let me know if this helps
Just need to take advantage of the lag on the inner query to compare values between rows, and apply the logic in question on the middle query, and then do a final select.
/*SAMPLE DATA*/
create table #table1
(
ID int not null
, StartDate date not null
, EndDate date null
)
insert into #table1
values (1, '2017-01-12', '2017-01-28')
, (1, '2017-01-19', '2017-01-28')
, (1, '2017-01-29', '2017-02-11')
, (1, '2017-02-01', '2017-02-11')
, (1, '2017-02-19', '2017-02-24')
, (2, '2017-01-12', '2017-01-28')
, (2, '2017-01-19', '2017-01-28')
, (2, '2017-03-09', '2017-03-20')
, (3, '2017-02-12', '2017-02-28')
, (3, '2017-02-19', '2017-02-28')
, (3, '2017-05-05', '2017-05-29')
, (3, '2017-05-09', '2017-05-29')
/*ANSWER*/
--Final Select
select c.ID
, c.StartDateMonth
from (
--Compare record values to rule a record in/out based on OP's logic
select b.ID
, b.StartDateMonth
, case when b.StartDateMonth = b.StartDateMonthPrev then 0 --still the same month?
when b.StartDateMonth = b.StartDateMonthPrev + 1 then 0 --immediately prior month?
when b.StartDateMonth = 1 and b.StartDateMonthPrev = 12 then 0 --Dec/Jan combo
else 1
end as IncludeFlag
from (
--pull StartDateMonth of previous record into current record
select a.ID
, datepart(mm, a.StartDate) as StartDateMonth
, lag(datepart(mm, a.StartDate), 1, NULL) over (partition by a.ID order by a.StartDate asc) as StartDateMonthPrev
from #table1 as a
) as b
) as c
where 1=1
and c.IncludeFlag = 1
Output:
+----+----------------+
| ID | StartDateMonth |
+----+----------------+
| 1 | 1 |
| 2 | 1 |
| 2 | 3 |
| 3 | 2 |
| 3 | 5 |
+----+----------------+
Try the below query,
SELECT ID,MIN(YEARMONTH) AS YEARMONTH
FROM (
SELECT ID
,YEAR([StartDate])*100+MONTH([StartDate]) AS YEARMONTH
,LAG(YEAR([StartDate])*100+MONTH([StartDate]))
OVER(ORDER BY ID) AS PREVYEARMONTH
,ROW_NUMBER() OVER(ORDER BY ID) AS ROW_NO
FROM #Table1
GROUP BY ID,((YEAR([StartDate])*100)+MONTH([StartDate]))
) AS T
GROUP BY ID
,(CASE WHEN YEARMONTH - PREVYEARMONTH > 1 THEN ROW_NO ELSE 0 END)
ORDER BY ID
Output:
ID YEARMONTH
1 201701
2 201701
2 201703
3 201702
3 201705
Thank you all guys. most of the logic seemed to work..but I tried just with below one and I Was good with thiis.
SELECT t1.ID, DatePart(month,t1.Startdate) AS StartDateMonth
FROM DatesTable t1
LEFT JOIN DatesTable t2
ON t1.ID = t2.ID
AND DatePart(month,t1.Startdate) = DatePart(month,t2.Startdate)+1
WHERE t2.ID IS NULL;
Thanks again
Ok, I wrote my first query without checking, believed that will work correctly. This is my updated version, should be faster than other solutions
select
id
, min(st)%12 --this will return start month
, min(st)/12 + 1 --this will return year, just in case if you need it
from (
select
id, st, gr = st - row_number() over (partition by ID order by st)
from (
select
distinct ID, st = (year(StartDate) - 1) * 12 + month(StartDate)
from
#table2
) t
) t
group by id, gr

Getting a column based on a record

In the input table the values keep changing every week. So, the next week column E would be have A instead of F and at that point I would want the E field.
So, whichever field has A has to be selected but the field having A should be the last field in the table that has an A as a record.
Input Table:
A | B | C | D | E
A A A A F
12 32 43 23 2
Output :
D
A
23
I understand that your data is unnormalized for some reason if you need a logic to get the results you can use this code:
DECLARE #table TABLE (A varchar(3), B varchar(3),C varchar(3), D varchar(3), E varchar(3))
;WITH cte
AS (SELECT ColumnName
,ColumnValue
,Row_number()
OVER(
partition BY ColumnName
ORDER BY Rn) ResultOrder
FROM (SELECT Row_number() OVER (ORDER BY (SELECT NULL))Rn,a ,b ,c ,d ,e FROM #table) p
UNPIVOT ( ColumnValue
FOR ColumnName IN( a ,b ,c ,d ,e)) AS unpvt),
cte1
AS (SELECT Ntile(5)
OVER(
ORDER BY ColumnName) ColumnId
,*
FROM cte),
cte2
AS (SELECT DISTINCT Isnull(NULLIF(ColumnId - 1, 0), 5) ColumnId
,ColumnName
FROM cte1
WHERE ColumnValue = 'F')
SELECT result
FROM (SELECT CASE ColumnId
WHEN 1 THEN 'A'
WHEN 2 THEN 'B'
WHEN 3 THEN 'C'
WHEN 4 THEN 'D'
WHEN 5 THEN 'E'
END Result
,0 ResultOrder
FROM cte2
UNION
SELECT ColumnValue
,resultorder
FROM cte1 a
WHERE EXISTS (SELECT 1
FROM cte2 b
WHERE a.ColumnId = b.ColumnId)) AS R
ORDER BY resultorder
Result For
insert #table VALUES
('A','A','A','A','F')
,('12','32','43','23','2')
A B C D E
---- ---- ---- ---- ----
A A A A F
12 32 43 23 2
result
------
D
23
A
Result For
insert #table VALUES
('F','A','A','A','A')
,('12','32','43','23','2')
A B C D E
---- ---- ---- ---- ----
F A A A A
12 32 43 23 2
result
------
E
A
2
Explanation:
CTE table does the Unpivot the table and create a row number for order the column name and the value
CT1 table creates a group base on column name using the NTILE to create 5 groups
CTE2 table get the previous column where the value is equal F
The Select Is complex to translate the ColumnId to ColumnName, if it not necessary,
you can use
SELECT ColumnValue Result
FROM cte1 a
WHERE EXISTS (SELECT 1
FROM cte2 b
WHERE a.ColumnId = b.ColumnId)
Result
------
A
23
(2 row(s) affected)
OR
SELECT ColumnValue Result
FROM cte1 a
WHERE EXISTS (SELECT 1
FROM cte2 b
WHERE a.ColumnId = b.ColumnId)
AND a.ResultOrder = 2
Result
------
23
(1 row(s) affected)

Select all second highest values only from temp table

Using T-SQL (SQL Server 2008 R2), I'm trying to list only the rows with the second highest value in a particular column from a temp table and then place the results into a new temp table. The PK is the ID, which can have increasing version numbers and then unique codes.
Example:
ID | Name| Version | Code
------------------------
1 | A | 1 | 10
1 | A | 2 | 20
1 | A | 3 | NULL
2 | B | 1 | 40
2 | B | 2 | 50
2 | C | 1 | 60
The desired outcome of the query is
ID | Version | Code
------------------------
1 | 2 | 20
2 | 1 | 40
To achieve this I need the below query to be adapted to pull the second highest value as long as the result gives a version number greater than 1. These results come from a temp table and will then be placed into a final results temp table. EDIT: Please note this will be applied over 33000 rows of data so I would prefer something neater than INSERT VALUES. Thanks.
Current query:
SELECT
ID
,Version
,Code
INTO
#table2
FROM
#table1
SELECT *
FROM #table2
WHERE Version > 1
ORDER BY ID asc
DROP TABLE #table1
DROP TABLE #table2
I have tried running the where clause WHERE Version < (SELECT MAX(VERSION) FROM #TABLE 2) but this has no effect, presumably due to the unique code values and in any case wouldn't work where I have more than 3 Versions.
Ideas would be gratefully received.
Thanks in advance.
i HAVE TEST THE BELOW CODE AND IT IS GIVING OUTPUT AS PER The YOUR desired outcome of the query is
SELECT ID,Name,[Version],Code
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY [Version] DESC) AS RNK,*
FROM
(
SELECT 1 ID, 'A' Name ,1 [Version] ,10 Code
UNION ALL
SELECT 1, 'A', 2 ,20
UNION ALL
SELECT 1, 'A', 3 ,30
UNION ALL
SELECT 1, 'A', 4 ,NULL
UNION ALL
SELECT 2, 'B', 1 ,40
UNION ALL
SELECT 2, 'B', 2 ,50
UNION ALL
SELECT 2, 'C', 1 ,60
)B
)BASE
WHERE RNK =2
If your primary key is only ID, you have duplicate rows. So I assume your primary key is something else, for example ID, Version, Name. You have two rows with the same ID and same Version, what kind of rule do you want to apply on this ? Lowest number ?
I made an example that does kind of what you want:
First declare the necessary tables:
declare #table1 table (
Id int,
Name nvarchar(20),
[Version] int,
Code int
)
insert into #table1 values (1,'A',1,10),(1,'A',2,20),(1,'A',3,30),(1,'A',4,NULL)
,(2,'B',1,40),(2,'B',2,50),(2,'C',1,60);
And then the query to get the results:
with HighestVersions (Id, MaxVersion) As
(
select Id, max(version) from #table1 group by Id
)
select
t1.Id,
t1.[Version],
min(t1.Code) as Code
from
#table1 t1
inner join
HighestVersions hv
on
hv.Id = t1.Id
and (hv.MaxVersion-1) = t1.[Version]
group by
t1.Id
,t1.[Version]
I had to do a little dirty trick with the outermost select, this is because of the duplicate 'Id' and 'Version'. Else you would have gotten two rows with ID = 2, Version = 1
If you want to remove the NULL value you can change the WITH part (according to your last edit):
with HighestVersions (Id, MaxVersion) As
(
select Id, max(version) from #table1 where Code is not null group by Id
)
Try this:
DECLARE #List TABLE (ID int, Name char(1), Version int, Code int NULL)
INSERT INTO #List
VALUES
(1, 'A', 1, 10),
(1, 'A', 2, 20),
(1, 'A', 3, 30),
(1, 'A', 4, NULL),
(2, 'B', 1, 40),
(2, 'B', 2, 50),
(2, 'C', 1, 60)
SELECT
ID, Name, Version, Code
FROM
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY ID, Name ORDER BY Version DESC) Rn
FROM #List
) a
WHERE
a.Rn = 2

Resources