Get data with condition - sql

declare #t table (
Id int ,
Section int,
Moment date
);
insert into #t values
( 1 , 1 , '2014-01-01'),
( 2 , 1 , '2014-01-02'),
( 3 , 1 , '2014-01-03'),
( 4 , 1 , '2014-01-04'),
( 5 , 1 , '2014-01-05'),
( 6 , 2 , '2014-02-06'),
( 7 , 2 , '2014-02-07'),
( 8 , 2 , '2014-02-08'),
( 9 , 2 , '2014-02-09'),
( 10 , 2 , '2014-02-10'),
( 11 , 3 , '2014-03-11'),
( 12 , 3 , '2014-03-12'),
( 13 , 3 , '2014-03-13'),
( 14 , 3 , '2014-03-14'),
( 15 , 3 , '2014-03-15');
getting data like this
select * from #t
Id Section Moment
1 1 2014-01-01
2 1 2014-01-02
3 1 2014-01-03
4 1 2014-01-04
5 1 2014-01-05
6 2 2014-02-06
7 2 2014-02-07
8 2 2014-02-08
9 2 2014-02-09
10 2 2014-02-10
11 3 2014-03-11
12 3 2014-03-12
13 3 2014-03-13
14 3 2014-03-14
15 3 2014-03-15
But i want data like this.group by 3 and Section wise
if ant Section have 5 rows there will create 2 group.
Id Section Moment Group by 3
1 1 1/1/2014 1
2 1 1/2/2014 1
3 1 1/3/2014 1
4 1 1/4/2014 2
5 1 1/5/2014 2
6 2 2/6/2014 3
7 2 2/7/2014 3
8 2 2/8/2014 3
9 2 2/9/2014 4
10 2 2/10/2014 4
11 3 3/11/2014 5
12 3 3/12/2014 5
13 3 3/13/2014 5
14 3 3/14/2014 6
15 3 3/15/2014 6

You can use window functions and arithmetic. The following enumerates within each section:
select (row_number() over (partition by section order by moment) + 2) / 3, t.*
from #t;
Then applying dense_rank() gets what you want:
select dense_rank() over (order by section, tempcol) as group3,
t.*
from (select (row_number() over (partition by section order by moment) + 2) / 3 as tempcol, t.*
from t
) t
order by id
Here is a db<>fiddle.

There might be mistake in interpretion but till where acknowledge the problem i beleive u are looking for this.I have done it using cursor .Hope it helps u .
DECLARE #i int =0 -- row count
DECLARE #GroupCount int=1
DECLARE #Id int
DECLARE #Section int
DECLARE #Moment DateTime
declare #temp table (
SNO int,
Id int ,
Section int,
Moment date,
GroupedIn nvarchar(200)
);
DECLARE db_cursor CURSOR FOR
SELECT Id,Section,Moment
FROM #t
WHERE Section = 3 --suppose
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #Id,#Section,#Moment
WHILE ##FETCH_STATUS = 0
BEGIN
Set #i=#i+1
Insert into #temp values(#i,#Id,#Section,#Moment,'G'+CONVERT(nvarchar(20),#GroupCount))
FETCH NEXT FROM db_cursor INTO #Id,#Section,#Moment
END
CLOSE db_cursor
DEALLOCATE db_cursor
Select * from #temp

Related

Retrieving Subset of a group of data in SQL or SAS

My dataset is like the below table.
ARR INST DUE_DATE
1 1 1-Dec
1 2 8-Dec
1 3 15-Dec
1 4 22-Dec
2 1 1-Dec
2 2 8-Dec
3 1 5-Dec
3 2 12-Dec
3 3 19-Dec
4 1 6-Nov
4 2 13-Nov
4 3 20-Nov
4 4 27-Nov
4 5 4-Dec
4 6 11-Dec
5 1 1-Jan
5 2 7-Jan
5 3 13-Jan
5 4 20-Jan
5 5 27-Jan
5 6 3-Feb
5 7 10-Feb
5 8 17-Feb
5 9 23-Feb
5 10 24-Feb
I need to retrieve data for each arrangements based on the number of installments paid.
Eg. If the total no of installments for a particular arrangement is Less than or equal to 4 then the output should have all the installments values till the 4th installment.
If it is greater than four, or a multiple of four, the the values should be the next subsequent set of four values.
The output should be something like
ARR INST DUE_DATE
1 1 1-Dec
1 2 8-Dec
1 3 15-Dec
1 4 22-Dec
2 1 1-Dec
2 2 8-Dec
3 1 5-Dec
3 2 12-Dec
3 3 19-Dec
4 5 4-Dec
4 6 11-Dec
5 9 23-Feb
5 10 24-Feb
How to get this output either in SQL server or SAS Enterprise Guide?
Thanks.
You can use this.
DECLARE #MyTable TABLE (ARR INT, INST INT, DUE_DATE VARCHAR(10))
INSERT INTO #MyTable VALUES
(1 , 1 , '1-Dec '),
(1 , 2 , '8-Dec '),
(1 , 3 , '15-Dec'),
(1 , 4 , '22-Dec'),
(2 , 1 , '1-Dec '),
(2 , 2 , '8-Dec '),
(3 , 1 , '5-Dec '),
(3 , 2 , '12-Dec'),
(3 , 3 , '19-Dec'),
(4 , 1 , '6-Nov '),
(4 , 2 , '13-Nov'),
(4 , 3 , '20-Nov'),
(4 , 4 , '27-Nov'),
(4 , 5 , '4-Dec '),
(4 , 6 , '11-Dec'),
(5 , 1 , '1-Jan '),
(5 , 2 , '7-Jan '),
(5 , 3 , '13-Jan'),
(5 , 4 , '20-Jan'),
(5 , 5 , '27-Jan'),
(5 , 6 , '3-Feb '),
(5 , 7 , '10-Feb'),
(5 , 8 , '17-Feb'),
(5 , 9 , '23-Feb'),
(5 , 10, '24-Feb'),
(5 , 11, '25-Feb'),
(5 , 12, '26-Feb'),
(6 , 1, '27-Feb')
DECLARE #numofinst INT = 4
SELECT ARR, INST, DUE_DATE FROM (
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY ARR ORDER BY INST ),
CNT = COUNT(*) OVER(PARTITION BY ARR )
FROM #MyTable
) AS T
WHERE
RN > (( CEILING( CAST( CNT AS decimal(18,2) ) / CAST( #numofinst AS decimal(18,2) )) - 1 ) * #numofinst)
Result:
ARR INST DUE_DATE
----------- ----------- ----------
1 1 1-Dec
1 2 8-Dec
1 3 15-Dec
1 4 22-Dec
2 1 1-Dec
2 2 8-Dec
3 1 5-Dec
3 2 12-Dec
3 3 19-Dec
4 5 4-Dec
4 6 11-Dec
5 9 23-Feb
5 10 24-Feb
5 11 25-Feb
5 12 26-Feb
6 1 27-Feb
For the case of sorted SAS data sets, or a remote data source delivering ordered data, the following DATA Step example shows how a double DOW loop can identify and output the rows belonging to the final 4-row chunk of each id:
data want(label="Rows from each ids last 4-row chunk");
do _n_ = 0 by 1 until (last.id);
set have;
by id sequence; %* by sequence not strictly necessary, but enforces the expectation of increasing sequence within id;
end;
_out_from_n = floor ( _n_ / 4 ) * 4;
do _n_ = 0 to _n_;
set have;
if _n_ >= _out_from_n then OUTPUT;
end;
drop _:;
run;
Alternatively, you could modify the code by Richard to use random read access with the SET Statement POINT= option as follows:
data want;
retain point 1;
drop point;
do _n_ = 0 by 1 until (last.arr);
set have;
by arr inst;
end;
do point = point+(floor(_n_/4)*4) to point+_n_;
set have point=point;
output;
end;
run;

SQL Recursive CTE unexpectedly returns alternating sets

I am trying to get the use recursive CTE to repeat the same pattern over and over, resetting when "Scenario" increases in value. RowNumber repeats 1-21 (as desired), but whenever "Scenario" is an even number, there are too few items in the "Vals" column to feed into "Value". I can't figure out which part of the code is causing me to be 1 short for only even Scenarios.
Below are the results of the code I'm using at the bottom.
Scenario RowNumber Value Vals
1 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 5 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 6 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 7 A A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 8 A A,A,A,A,A,A,A,A,A,A,A,B,C
1 9 A A,A,A,A,A,A,A,A,A,A,B,C
1 10 A A,A,A,A,A,A,A,A,A,B,C
1 11 A A,A,A,A,A,A,A,A,B,C
1 12 A A,A,A,A,A,A,A,B,C
1 13 A A,A,A,A,A,A,B,C
1 14 A A,A,A,A,A,B,C
1 15 A A,A,A,A,B,C
1 16 A A,A,A,B,C
1 17 A A,A,B,C
1 18 A A,B,C
1 19 A B,C
1 20 B C
1 21 C
2 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 5 A A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 6 A A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 7 A A,A,A,A,A,A,A,A,A,A,B,B,C
2 8 A A,A,A,A,A,A,A,A,A,B,B,C
2 9 A A,A,A,A,A,A,A,A,B,B,C
2 10 A A,A,A,A,A,A,A,B,B,C
2 11 A A,A,A,A,A,A,B,B,C
2 12 A A,A,A,A,A,B,B,C
2 13 A A,A,A,A,B,B,C
2 14 A A,A,A,B,B,C
2 15 A A,A,B,B,C
2 16 A A,B,B,C
2 17 A B,B,C
2 18 B B,C
2 19 B C
2 20 C
2 21 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
3 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 5 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 6 A A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 7 A A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 8 A A,A,A,A,A,A,A,A,A,A,B,C,C
3 9 A A,A,A,A,A,A,A,A,A,B,C,C
3 10 A A,A,A,A,A,A,A,A,B,C,C
3 11 A A,A,A,A,A,A,A,B,C,C
3 12 A A,A,A,A,A,A,B,C,C
3 13 A A,A,A,A,A,B,C,C
3 14 A A,A,A,A,B,C,C
3 15 A A,A,A,B,C,C
3 16 A A,A,B,C,C
3 17 A A,B,C,C
3 18 A B,C,C
3 19 B C,C
3 20 C C
3 21 C
4 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 4 A A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 5 A A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 6 A A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 7 A A,A,A,A,A,A,A,A,A,B,B,B,C
4 8 A A,A,A,A,A,A,A,A,B,B,B,C
4 9 A A,A,A,A,A,A,A,B,B,B,C
4 10 A A,A,A,A,A,A,B,B,B,C
4 11 A A,A,A,A,A,B,B,B,C
4 12 A A,A,A,A,B,B,B,C
4 13 A A,A,A,B,B,B,C
4 14 A A,A,B,B,B,C
4 15 A A,B,B,B,C
4 16 A B,B,B,C
4 17 B B,B,C
4 18 B B,C
4 19 B C
4 20 C
This is the code I used to generate the above sample. Where am I going wrong?
CREATE TABLE #temp3
(
Scenario INT
,Vals VARCHAR(64)
,LEN INT
)
;
WITH vals AS
(
SELECT
v.*
FROM
(VALUES ('A'), ('B'), ('C')) v(x)
),
CTE AS
(
SELECT CAST('A' AS VARCHAR(MAX)) AS STR, 0 AS LEN
UNION ALL
SELECT (CTE.STR + ',' + vals.x), CTE.LEN + 1
FROM
CTE
JOIN vals
ON vals.x >= RIGHT(CTE.STR, 1)
WHERE CTE.LEN < 19
)
INSERT INTO #temp3
SELECT
ROW_NUMBER() OVER(ORDER BY STR + ',C') AS Scenario
,STR + ',C' AS Vals
,LEN
FROM
CTE
WHERE
STR + 'C' LIKE '%B%'
AND LEN = 19
;
-- Split strings created above into individual characters
WITH cte(Scenario, Value, Vals) AS
(
SELECT
Scenario
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) AS Value
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') AS Vals
FROM #temp3
UNION ALL
SELECT
Scenario
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10))
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '')
FROM cte
WHERE Vals > ''
)
SELECT
Scenario
,ROW_NUMBER() OVER (PARTITION BY Scenario ORDER BY Scenario) RowNumber
,Value
,Vals
FROM cte t
I'm not exactly sure what the problem you are describing is, but the ROW_NUMBER() should use an ORDER BY clause that completely orders the rows in each partition.
When you use "PARTITION BY Scenario ORDER BY Scenario" the order in which the ROW_NUMBER() values are assigned is undefined. Try something like
WITH cte(Scenario, depth, Value, Vals) AS
(
SELECT
Scenario, 0 depth
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) AS Value
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') AS Vals
FROM #temp3
UNION ALL
SELECT
Scenario, depth+1
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10))
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '')
FROM cte
WHERE Vals > ''
)
SELECT
Scenario
,depth
,ROW_NUMBER() OVER (PARTITION BY Scenario ORDER BY depth ) RowNumber
,Value
,Vals
FROM cte t

How to use Dense Rank and automatically generate dates

I have two questions in regards to DENSE_RANK and the other based on inserting dates. Basically I have 2 leagues with 4 teams per league. Each league has a round of fixtures like so:
League 1
Week1: 1v4, 2v3 - Date: 10-June-2016
Week2: 1v3, 2v4 - Date: 17-June-2016
Week3: 1v2, 3v4 - Date: 24-June-2016
League 2
Week1: 5v8, 6v7 - Date: 10-June-2016
Week2: 5v7, 6v8 - Date: 17-June-2016
Week3: 5v6, 7v8 - Date: 24-June-2016
(They play each other home and away)
Ok so league 1 and League 2 is (LeagueID 1 and League ID 2)
Week 1 and 2 are displayed under WeekNumber column
Teams 1 -8 have their own IDs (TeamID which is then displayed as HomeTeamID and AwayTeamID)
Date goes into a column which is FixtureDate
My questions are:
1- How under WeekNumber can I set it so that the group of games mentioned, it notices them as these games belong to week 1, these week 2, these games week 3 etc.
2- How to auto generate the date so that if week 1 is played 10 June 2016, the next round of fixtures are played 7 days later, then the round after 7 days later etc.
Below is what the table looks like currently:
WeekNumber HomeTeamID AwayTeamID FixtureWeek LeagueID
1 1 4 NULL 1
1 1 3 NULL 1
1 1 2 NULL 1
1 2 3 NULL 1
1 2 4 NULL 1
1 3 4 NULL 1
1 5 8 NULL 2
1 5 7 NULL 2
1 5 6 NULL 2
1 6 7 NULL 2
1 6 8 NULL 2
1 7 8 NULL 2
Below is what it should like:
WeekNumber HomeTeamID AwayTeamID FixtureWeek LeagueID
1 1 4 10-06-2016 1
2 1 3 17-06-2016 1
3 1 2 24-06-2016 1
1 2 3 10-06-2016 1
2 2 4 17-06-2016 1
3 3 4 24-06-2016 1
1 5 8 10-06-2016 2
2 5 7 17-06-2016 2
3 5 6 24-06-2016 2
1 6 7 10-06-2016 2
2 6 8 17-06-2016 2
3 7 8 24-06-2016 2
Below is my current code which needs to be modified but I need help with this:
CREATE PROCEDURE [dbo].[Fixture_Insert]
#LeagueID INT
AS
SET NOCOUNT ON
BEGIN
INSERT INTO dbo.Fixture (WeekNumber, HomeTeamID, AwayTeamID, FixtureWeek, LeagueID)
SELECT
ROW_NUMBER() OVER (ORDER BY a.LeagueID) AS WeekNumber,
h.TeamID,
a.TeamID,
NULL AS FixtureWeek, -- Don't know what to set this to for automatic dates
h.LeagueID
FROM dbo.Team h
CROSS JOIN dbo.Team a
WHERE h.TeamID <> a.TeamID
AND h.LeagueID = a.LeagueID
END
UPDATE:
I've applied images to showcase what is happening so you can see what needs to be done to fix it (the table displayed is when I did a select* from dbo.Fixture):
The proc I excuted for the above is displayed here:
DECLARE #StartFixtureWeek date = '2016-06-10'
;WITH team AS (
SELECT *
FROM (VALUES
(1,1),(2,1),(3,1),(4,1),(5,2),(6,2),(7,2),(8,2)
) as t (teamid, leagueid)
)
, cte AS (
SELECT h.teamid AS HomeTeamID,
a.teamid AS AwayTeamID,
h.leagueid AS LeagueID
FROM team h
CROSS JOIN team a
WHERE h.teamid != a.teamid AND h.leagueid = a.leagueid
), final AS (
SELECT ROW_NUMBER() OVER (PARTITION BY c.LeagueID ORDER BY c.LeagueID, c.HomeTeamID, c.AwayTeamID) as rn,
c.HomeTeamID,
c.AwayTeamID,
c.LeagueID
FROM cte c
CROSS APPLY (
SELECT TOP 1 a.HomeTeamID, a.AwayTeamID
FROM cte a
WHERE a.LeagueID= c.LeagueID and a.AwayTeamID=c.HomeTeamID and a.HomeTeamID =c.AwayTeamID
ORDER BY a.HomeTeamID, a.LeagueID) as b
WHERE c.HomeTeamID < b.HomeTeamID
)
SELECT CASE WHEN rn > 3 THEN rn-3 ELSE rn END as WeekNumber,
HomeTeamID,
AwayTeamID,
CAST(DATEADD(week,(CASE WHEN rn > 3 THEN rn-3 ELSE rn END)-1,#StartFixtureWeek) as date) FixtureWeek,
LeagueID
FROM final
Output:
WeekNumber HomeTeamID AwayTeamID FixtureWeek LeagueID
-------------------- ----------- ----------- ----------- -----------
1 1 2 2016-06-10 1
2 1 3 2016-06-17 1
3 1 4 2016-06-24 1
1 2 3 2016-06-10 1
2 2 4 2016-06-17 1
3 3 4 2016-06-24 1
1 5 6 2016-06-10 2
2 5 7 2016-06-17 2
3 5 8 2016-06-24 2
1 6 7 2016-06-10 2
2 6 8 2016-06-17 2
3 7 8 2016-06-24 2
(12 row(s) affected)
Add a parameter to your stored procedure called #StartFixtureWeek DATETIME
Then you can use DATEADD
CREATE PROCEDURE [dbo].[Fixture_Insert]
#LeagueID INT,
#StartFixtureWeek DATETIME
AS
SET NOCOUNT ON
BEGIN
INSERT INTO dbo.Fixture (WeekNumber, HomeTeamID, AwayTeamID, FixtureWeek, LeagueID)
SELECT
ROW_NUMBER() OVER (ORDER BY a.LeagueID) AS WeekNumber,
h.TeamID,
a.TeamID,
SELECT DATEADD(day,(ROW_NUMBER() OVER (ORDER BY a.LeagueID)-1)*7,#StartFixtureWeek) AS FixtureWeek,
h.LeagueID
FROM dbo.Team h
CROSS JOIN dbo.Team a
WHERE h.TeamID <> a.TeamID
AND h.LeagueID = a.LeagueID
END

SELECT MAX(column) AND DISTINCT by one of two columns in MS SQL

Using MS SQL Server 2014. I need to select the row where (userid=1 or memberid=1) that has the max(messageid) value from all the messages where the user #1 sent or received messages ordered by messageid desc
I tried the solution here: How can I SELECT rows with MAX(Column value), DISTINCT by another column in SQL?, but since a user can send OR receive messages, the solution only partly solves my problem.
Message Table
messageid userid memberid message created
--------------------------------------------------------------
9 4 1 Hi 9 2016-05-09 01:50:59.423
8 4 1 Hi 8 2016-05-09 01:50:43.950
7 1 4 Hi 7 2016-05-09 01:50:35.310
6 1 4 Hi 6 2016-05-09 01:50:25.887
5 1 2 Hi 5 2016-05-08 23:49:41.610
11 2 1 Hi 11 2016-05-09 03:26:42.267
12 1 3 Hi 12 2016-05-09 05:06:11.030
1 1 2 Hi 1 2016-05-08 22:37:57.803
Expected Result
messageid userid memberid message created
--------------------------------------------------------------
12 1 3 Hi 12 2016-05-09 05:06:11.030
11 2 1 Hi 11 2016-05-09 03:26:42.267
9 4 1 Hi 9 2016-05-09 01:50:59.423
Query I tried
DECLARE #userid bigint
SET #userid=1
SELECT mm.*
FROM messages mm
INNER JOIN
(SELECT memberid AS otherperson, MAX(m.messageid) AS MaxID
FROM messages m
WHERE m.userid=#userid
GROUP BY memberid
) groupedmm
ON mm.memberid = groupedmm.otherperson
AND mm.messageid = groupedmm.MaxID
UNION
SELECT mmm.*
FROM messages mmm
INNER JOIN
(SELECT userid AS otherperson, MAX(m.messageid) AS MaxID
FROM messages m
WHERE m.memberid=#userid
GROUP BY userid
) groupedmmm
ON mmm.userid = groupedmmm.otherperson
AND mmm.messageid = groupedmmm.MaxID
Above Query Returned
messageid userid memberid message created
--------------------------------------------------------------
5 1 2 Hi 5 2016-05-08 23:49:41.610
7 1 4 Hi 7 2016-05-09 01:50:35.310
9 4 1 Hi 9 2016-05-09 01:50:59.423
11 2 1 Hi 11 2016-05-09 03:26:42.267
12 1 3 Hi 12 2016-05-09 05:06:11.030
This seems to do the job - quite straightforward if you use a partitioned function:
declare #user_id int
set #user_id = 1
declare #t table (messageid tinyint, userid tinyint, memberid tinyint, message varchar(17),
created datetime)
insert into #t(messageid, userid, memberid, message, created) values
(9 ,4 ,1 ,'Hi 9' ,'2016-05-09T01:50:59.423'),
(8 ,4 ,1 ,'Hi 8' ,'2016-05-09T01:50:43.950'),
(7 ,1 ,4 ,'Hi 7' ,'2016-05-09T01:50:35.310'),
(6 ,1 ,4 ,'Hi 6' ,'2016-05-09T01:50:25.887'),
(5 ,1 ,2 ,'Hi 5' ,'2016-05-08T23:49:41.610'),
(11 ,2 ,1 ,'Hi 11' ,'2016-05-09T03:26:42.267'),
(12 ,1 ,3 ,'Hi 12' ,'2016-05-09T05:06:11.030'),
(1 ,1 ,2 ,'Hi 1' ,'2016-05-08T22:37:57.803')
;With Ordered as (
select *,
ROW_NUMBER() OVER (PARTITION BY
CASE WHEN userid = #user_id THEN memberid else userid END
ORDER BY created desc) rn
from #t
where
userid = #user_id or
memberid = #user_id
)
select * from Ordered where rn = 1
Results:
messageid userid memberid message created rn
--------- ------ -------- ----------------- ----------------------- --------------------
11 2 1 Hi 11 2016-05-09 03:26:42.267 1
12 1 3 Hi 12 2016-05-09 05:06:11.030 1
9 4 1 Hi 9 2016-05-09 01:50:59.423 1
Note the use of the CASE expression to derive the PARTITION value as, essentially, "whichever column wasn't matched in the WHERE clause". This can be trickier to extend to more than two columns.
CREATE TABLE #D
(
MESSAGEID INT, USERID INT, MEMBERID INT, MESSAGE VARCHAR(10), CREATED DATETIME)
INSERT INTO #D VALUES
(9,4,1,'HI 9','2016-05-09 01:50:59.423'),
(8,4,1,'HI 8','2016-05-09 01:50:43.950'),
(7,1,4,'HI 7','2016-05-09 01:50:35.310'),
(6,1,4,'HI 6','2016-05-09 01:50:25.887'),
(5,1,2,'HI 5','2016-05-08 23:49:41.610'),
(11,2,1,'HI 11','2016-05-09 03:26:42.267'),
(12,1,3,'HI 12','2016-05-09 05:06:11.030'),
(1,1,2,'HI 1','2016-05-08 22:37:57.803')
SELECT TT.*
FROM #D TT
INNER JOIN
(SELECT USERID, MAX(CREATED) AS MAXDATETIME
FROM #D
GROUP BY USERID) GROUPEDTT
ON TT.USERID = GROUPEDTT.USERID
AND TT.CREATED = GROUPEDTT.MAXDATETIME

Assign rownumber in SQL grouped on value and n rows per rownumber

I am trying to generate a report with 3 rows per page for each order number using the following SQL.
As you can see from the results the fields Actual & Expected do not match up.
Any help would be appreciated.
set nocount on
DECLARE #Orders TABLE (Expected int, OrderNumber INT, OrderDetailsNumber int)
Insert into #orders values (0,1,1)
Insert into #orders values (0,1,2)
Insert into #orders values (0,1,3)
Insert into #orders values (1,1,4)
Insert into #orders values (2,2,5)
Insert into #orders values (2,2,6)
Insert into #orders values (2,2,7)
Insert into #orders values (3,2,8)
Insert into #orders values (3,2,9)
select cast(((row_number() over( order by OrderNumber)) -1) /3 as int) as [Actual]
,*
from #orders
Actual Expected OrderNumber OrderDetailsNumber
----------- ----------- ----------- ------------------
0 0 1 1
0 0 1 2
0 0 1 3
1 1 1 4
1 2 2 5
1 2 2 6
2 2 2 7
2 3 2 8
2 3 2 9
Right, after a couple of edits I have the final answer:
SELECT DENSE_RANK() OVER (Order BY OrderNumber, floor(RowNumber/3)) - 1 AS Actual,
Expected,
OrderNumber,
OrderDetailsNumber
FROM
(
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY OrderNumber
ORDER BY OrderDetailsNumber
) - 1 AS RowNumber
FROM #Orders
) RowNumberTable
Gives the result (with extra rows for testing):
Actual Expected OrderNumber OrderDetailsNumber
-------------------- ----------- ----------- ------------------
0 0 1 1
0 0 1 2
0 0 1 3
1 1 1 4
1 1 1 12
2 2 2 5
2 2 2 6
2 2 2 7
3 3 2 8
3 3 2 9
3 4 2 11
4 3 2 27
5 5 3 10
This only works where OrderDetailsNumber is unique such that the result is deterministic.
Edit
I've now got the complete code working, however the dependence on OrderDetailsNumber being in order is very iffy, hopefully you can test and edit as required.
Edit 2
I've put the 'golfed' version in the main answer.
WITH FirstCTE AS
(
SELECT
OrderNumber,
OrderDetailsNumber,
Expected,
ROW_NUMBER() OVER (
PARTITION BY OrderNumber
ORDER BY OrderDetailsNumber
) - 1 AS RowNumber
FROM #Orders
)
, SecondCTE AS
(
SELECT OrderDetailsNumber as odn,
floor(RowNumber/3) as page_for_order_number,
DENSE_RANK() OVER (Order BY OrderNumber, floor(RowNumber/3)) - 1 AS Actual
FROM FirstCTE
)
SELECT c2.page_for_order_number,
c1.RowNumber,
C2.Actual,
c1.Expected,
c1.OrderNumber,
c1.OrderDetailsNumber
FROM FirstCTE AS c1
INNER JOIN SecondCTE AS c2
on c2.odn = c1.OrderDetailsNumber
This strikes me as a bit of a hack, but it works...
Divide the row_number() by 3, and use CEILINGto get the smallest integer greater than or equal to the result of that division.
select row_number() over( order by OrderNumber) as [Actual],
cast (row_number() over(order by ordernumber) as decimal(5,1)) / 3,
CEILING(cast (row_number() over(order by ordernumber) as decimal(5,1)) / 3)as GRPR,
*
from #orders
EDIT: Dang it, can never get results to line up. The 3rd column in the result set is your "page number".
Which yields:
Actual (No column name) PG_NBR Expected OrderNumber OrderDetailsNumber
1 0.333333 1 0 1 1
2 0.666666 1 0 1 2
3 1.000000 1 0 1 3
4 1.333333 2 1 1 4
5 1.666666 2 2 2 5
6 2.000000 2 2 2 6
7 2.333333 3 2 2 7
8 2.666666 3 3 2 8
9 3.000000 3 3 2 9