简体   繁体   中英

Get Max(value) without grouping in SQL Server

I have this grouping problem that I do not need.

Here is my SQL code to create and seed the table:

create table player 
(
    playerid int identity(1,1) primary key,
    Name varchar(255)
)

create table gameplay
(
    gameplayid int identity(1,1) primary key,
    Name varchar(255),
    playerid int references player(playerid)
)

Create table room
(
    Roomid int identity(1,1) primary key,
    number int
)

Create table PC
(
    PCid int identity(1,1) primary key,
    Name varchar(255)
)

Create table playperiod
(
    PKid int identity(1,1) primary key,
    StartDate Datetime null,
    EndDate Datetime null,
    Roomid int references room(roomid),
    PCID int references PC(PCid),
    gameplayid int references gameplay(gameplayid)
)

Insert into player (name) values ('Kris')
Insert into player (name) values ('Bart')
Insert into player (name) values ('Bob')
Insert into player (name) values ('John')
Insert into player (name) values ('Iris')

insert into room (number) values (1000)
insert into room (number) values (1001)
insert into room (number) values (1002)
insert into room (number) values (1003)
insert into room (number) values (1004)
insert into room (number) values (1005)
insert into room (number) values (1006)
insert into room (number) values (1007)
insert into room (number) values (1008)
insert into room (number) values (1009)
insert into room (number) values (1010)

insert into gameplay (name, playerid) values ('WOW', 1)
insert into gameplay (name, playerid) values ('LOL', 2)
insert into gameplay (name, playerid) values ('DIablo', 3)
insert into gameplay (name, playerid) values ('Starcraft', 4)
insert into gameplay (name, playerid) values ('Borderlands', 5)

insert into PC (name) values ('Dell (1)')
insert into PC (name) values ('HP (1)')
insert into PC (name) values ('Dell (2)')
insert into PC (name) values ('HP (2)')
insert into PC (name) values ('Dell (3)')
insert into PC (name) values ('HP (3)')
insert into PC (name) values ('Dell (4)')
insert into PC (name) values ('HP (4)')
insert into PC (name) values ('Dell (5)')
insert into PC (name) values ('HP (5)')
insert into PC (name) values ('Dell (6)')
insert into PC (name) values ('HP (6)')
insert into PC (name) values ('Dell (7)')
insert into PC (name) values ('HP (7)')
insert into PC (name) values ('Dell (8)')
insert into PC (name) values ('HP (8)')
insert into PC (name) values ('Dell (9)')
insert into PC (name) values ('HP (9)')
insert into PC (name) values ('Dell (10)')
insert into PC (name) values ('HP (10)')

truncate table playperiod

insert into playperiod (StartDate, EndDate, Roomid, PCID, gameplayid) 
values ('2015-01-22 10:02:00.000', '2015-01-22 11:30:00.000', 1, 1, 1)

insert into playperiod (StartDate, EndDate, Roomid, PCID, gameplayid)   
values ('2015-01-22 10:02:00.000', '2015-01-22 16:02:00.000', 1, 2, 2)

insert into playperiod (StartDate, EndDate, Roomid, PCID, gameplayid) 
values ('2015-01-22 10:04:00.000', '2015-01-28 10:02:00.000', 2, 3, 3)

insert into playperiod (StartDate, EndDate, Roomid, PCID, gameplayid) 
values ('2015-01-20 10:02:00.000', '2015-01-22 10:02:00.000', 2, 4, 4)

insert into playperiod (StartDate, EndDate, Roomid, PCID, gameplayid) 
values ('2015-01-22 22:40:00.000', '2015-01-22 22:50:00.000', 3, 5, 1)

insert into playperiod (StartDate, EndDate, Roomid, PCID, gameplayid) 
values ('2015-01-22 22:55:00.000', '2015-01-22 23:50:00.000', 3, 6, 1)

insert into playperiod (StartDate, EndDate, Roomid, PCID, gameplayid) 
values ('2015-01-22 11:30:00.000', '2015-01-22 13:30:00.000', 1, 1, 1)

insert into playperiod (StartDate, EndDate, Roomid, PCID, gameplayid) 
values ('2015-01-22 14:30:00.000', '2015-01-22 16:30:00.000', 1, 1, 1)

insert into playperiod (StartDate, EndDate, Roomid, PCID, gameplayid) 
values ('2015-01-22 19:30:00.000', '2015-01-22 22:30:00.000', 1, 1, 1)

Know these are my queries:

Step 1) get everything for 1 day (No problem so far)

Select startdate, number, pc.name, gp.Name,p.Name  
from playperiod PD
left join room R on R.Roomid = PD.Roomid
left join pc on pc.PCid = PD.PCID
left join gameplay gp on GP.gameplayid = PD.gameplayid
left join player P on P.playerid = GP.playerid 
where 
DATEDIFF(day, pd.startdate, '2015-01-22') >= 0 and DATEDIFF(day, pd.startdate, '2015-01-22') <= 0

在此处输入图片说明

Step 2) Get the max date value from start date (Problem constation)

Select max(startdate) as startdate, number, pc.name, gp.Name,p.Name  
from playperiod PD
left join room R on R.Roomid = PD.Roomid
left join pc on pc.PCid = PD.PCID
left join gameplay gp on GP.gameplayid = PD.gameplayid
left join player P on P.playerid = GP.playerid 
where 
DATEDIFF(day, pd.startdate, '2015-01-22') >= 0 and DATEDIFF(day, pd.startdate, '2015-01-22') <= 0
group by number, pc.name, gp.Name,p.Name

在此处输入图片说明

At the end I do group by so the behavior is correct. But here the user 'Kris' plays on 3 Pc that day in 2 rooms.

But I would like to have this situation:

  • Only the max startdate with that room number / pc / game / name (for that row)
  • I only need the latest situation. (even if he changed pc or room)

Here is the result that I need:

在此处输入图片说明

Can anyone help me ?

You can read about row_number. Basically it assign number by some criteria. Here I said that make numbering for every group combination of gp.Name, P.Name and then I ordered these groups by datetime and took the latest one.

SELECT
    startdate ,
    number ,
    pc_name ,
    gp_name ,
    Name
FROM
    (
      SELECT
        startdate ,
        number ,
        pc.name pc_name ,
        gp.Name gp_name ,
        P.Name ,
        ROW_NUMBER() OVER ( PARTITION BY gp.Name, P.Name ORDER BY startdate DESC ) rn
      FROM
        playperiod PD
        LEFT JOIN room R ON R.Roomid = PD.Roomid
        LEFT JOIN pc ON pc.PCid = PD.PCID
        LEFT JOIN gameplay gp ON gp.gameplayid = PD.gameplayid
        LEFT JOIN player P ON P.playerid = gp.playerid
      WHERE
            DATEDIFF(DAY, PD.startdate, '2015-01-22') >= 0
        AND DATEDIFF(DAY, PD.startdate, '2015-01-22') <= 0
    ) a
WHERE
    rn = 1

You can do this a tiny bit more compactly with the nonstandard TOP WITH TIES clause:

SELECT TOP (1) WITH TIES
  startdate ,
  number ,
  pc.name pc_name ,
  gp.Name gp_name ,
  P.Name
FROM
  playperiod PD
  LEFT JOIN room R ON R.Roomid = PD.Roomid
  LEFT JOIN pc ON pc.PCid = PD.PCID
  LEFT JOIN gameplay gp ON gp.gameplayid = PD.gameplayid
  LEFT JOIN player P ON P.playerid = gp.playerid
WHERE PD.startdate >= '20150122'
AND PD.startdate < DATEADD(DAY,1,'20150122')
ORDER BY ROW_NUMBER() OVER (
  PARTITION BY gp.Name, P.Name
  ORDER BY startdate DESC
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM