How do I select max value from a table, based on unique value on joined tables?











up vote
0
down vote

favorite












I have three tables. All follow the CRUD-pattern for inserts and updates. I need to make a query qhich selects all latest updates in one table, based on unique values of another table, twice removed through joins: (I have left out the CRUD structure on two of the tables in the example)



table COPY_HISTORY



COPY_ID | DATA_ID | STATUS | TIME  |        
1 | A | open | 10:34 |
1 | A | locked | 10:37 |
2 | A | open | 10:38 |
3 | B | open | 11:29 |
4 | C | open | 10:37 |
5 | D | locked | 09:34 |


table DATA_SET



DATA_ID | LOCATION |    
A | 88 |
B | 77 |
C | 88 |
D | 99 |
E | 88 |
F | 88 |


table COPY_RULES



LOCATION_FROM | LOCATION_TO 
55 | 110
66 | 120
77 | 120
88 | 130
99 | 130


What I need to do, is get the status for each LOCATION_TO from the copy rules table. If a copy is executed, it will be registered in copy history, and a copy will always overwrite all data at a location (110,120,130).



In this case, location 110 has never had a copy happen, and 110 should not be included in the returned data at all. Even though it exists in the rules, that is not relevant. 120 will receive copies from data sets on 66 and 77. However, we have no data set on 66 yet, so only 77 is relevant. B is located at 77, and data set B had a copy history registration inserted at 11:29, with the status open. Since this is the only status belonging to 120, status is open for 120, and overwrite will be possible for location 120.



Location 130 however, receive copies from both 88 and 99. This means that data sets A, C, D, E and F all will copy into 130, as they are all located in 88 or 99. We have two copy history logs for A, and one each for C and D. Meaning I want the status of only the newest registration, in this case, 10:38.



Now, I have tried to select the maximum value from COPY_HISTORY, based on the LOCATION_TO, but I am at an impasse getting the LOCATION_TO to be unique



I try:



SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME FROM COPY_HISTORY ch
JOIN DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
WHERE ch.TIME = (SELECT MAX(TIME) FROM COPY_HISTORY
WHERE COPY_ID = ch.COPY_ID
AND ch.DATA_ID = ds.DATA_ID
AND ds.LOCATION = cr.LOCATION_FROM)


That select max time statement is incomplete. The two AND-lines does nothing at all. The only thing I can accomplish, is getting rid of the first line in COPY_HISTORY, meaning I can get all max times for each COPY_ID, but I can not figure out how to filter them to get only the maximum for each LOCATION_TO instead.



I managed to get the max value from one single LOCATION_TO, by doing this:



SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME FROM COPY_HISTORY ch
JOIN DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
WHERE ch.TIME = (SELECT MAX(TIME) FROM COPY_HISTORY ch2, DATA_SET ds2, COPY_RULES cs2
WHERE ch2.DATA_ID = ds2.DATA_ID
AND ds2.LOCATION = cr2.LOCATION_FROM
AND cr2.LOCATION_TO = 2180)


However, this does not solve my issue of getting the list of statuses for all LOCATION_TO.



DESIRED OUTPUT:



LOCATION_TO | STATUS  | TIME  |
120 | open | 11:29 |
130 | open | 10:38 |


Valid rows in COPY_HISTORY would be line 3 and line 4



This is an attempt at an extreme simplification of the actual database structure, so there are risks of typos in this question.



DDL



create table #COPY_HISTORY (COPY_ID int, DATA_ID char(1), [STATUS] varchar(16), [TIME] time)
create table #DATA_SET (DATA_ID char(1), [LOCATION] int)
create table #COPY_RULES (LOCATION_FROM int, LOCATION_TO int)

insert into #COPY_HISTORY
values
(1,'A','open','10:34'),
(1,'A','locked','10:37'),
(2,'A','open','10:38'),
(3,'B','open','11:29'),
(4,'C','open','10:37'),
(5,'D','locked','09:34')

insert into #DATA_SET
values
('A',88),
('B',77),
('C',88),
('D',99),
('E',88),
('F',88)


insert into #COPY_RULES
values
(55,110),
(66,120),
(77,120),
(88,130),
(99,130)









share|improve this question




















  • 1




    The description of the logic isn't a bad thing, but it might help your question to show the exact expected output based on the sample tables you showed above.
    – Tim Biegeleisen
    Nov 7 at 15:06






  • 2




    Use ROW_NUMBER() OVER (PARTITION BY) to order your records by descending order and number them, then just grab each record where ROW NUMBER equals 1
    – Ryan Wilson
    Nov 7 at 15:07










  • I have specified the desired output more clearly. Thanks for the input.
    – KjetilNordin
    Nov 7 at 15:13










  • I think maybe I'm on to something by switching cr2.LOCATION_TO = 2180, to cr2.LOCATION_TO = cr.LOCATION_TO in the last example, but I have been struggling with this for so long now, that even though it seems to give the desired results, I don't feel confident I've done it right.
    – KjetilNordin
    Nov 7 at 15:17










  • @RyanWilson unfortunately I do not understand the context.
    – KjetilNordin
    Nov 7 at 15:20















up vote
0
down vote

favorite












I have three tables. All follow the CRUD-pattern for inserts and updates. I need to make a query qhich selects all latest updates in one table, based on unique values of another table, twice removed through joins: (I have left out the CRUD structure on two of the tables in the example)



table COPY_HISTORY



COPY_ID | DATA_ID | STATUS | TIME  |        
1 | A | open | 10:34 |
1 | A | locked | 10:37 |
2 | A | open | 10:38 |
3 | B | open | 11:29 |
4 | C | open | 10:37 |
5 | D | locked | 09:34 |


table DATA_SET



DATA_ID | LOCATION |    
A | 88 |
B | 77 |
C | 88 |
D | 99 |
E | 88 |
F | 88 |


table COPY_RULES



LOCATION_FROM | LOCATION_TO 
55 | 110
66 | 120
77 | 120
88 | 130
99 | 130


What I need to do, is get the status for each LOCATION_TO from the copy rules table. If a copy is executed, it will be registered in copy history, and a copy will always overwrite all data at a location (110,120,130).



In this case, location 110 has never had a copy happen, and 110 should not be included in the returned data at all. Even though it exists in the rules, that is not relevant. 120 will receive copies from data sets on 66 and 77. However, we have no data set on 66 yet, so only 77 is relevant. B is located at 77, and data set B had a copy history registration inserted at 11:29, with the status open. Since this is the only status belonging to 120, status is open for 120, and overwrite will be possible for location 120.



Location 130 however, receive copies from both 88 and 99. This means that data sets A, C, D, E and F all will copy into 130, as they are all located in 88 or 99. We have two copy history logs for A, and one each for C and D. Meaning I want the status of only the newest registration, in this case, 10:38.



Now, I have tried to select the maximum value from COPY_HISTORY, based on the LOCATION_TO, but I am at an impasse getting the LOCATION_TO to be unique



I try:



SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME FROM COPY_HISTORY ch
JOIN DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
WHERE ch.TIME = (SELECT MAX(TIME) FROM COPY_HISTORY
WHERE COPY_ID = ch.COPY_ID
AND ch.DATA_ID = ds.DATA_ID
AND ds.LOCATION = cr.LOCATION_FROM)


That select max time statement is incomplete. The two AND-lines does nothing at all. The only thing I can accomplish, is getting rid of the first line in COPY_HISTORY, meaning I can get all max times for each COPY_ID, but I can not figure out how to filter them to get only the maximum for each LOCATION_TO instead.



I managed to get the max value from one single LOCATION_TO, by doing this:



SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME FROM COPY_HISTORY ch
JOIN DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
WHERE ch.TIME = (SELECT MAX(TIME) FROM COPY_HISTORY ch2, DATA_SET ds2, COPY_RULES cs2
WHERE ch2.DATA_ID = ds2.DATA_ID
AND ds2.LOCATION = cr2.LOCATION_FROM
AND cr2.LOCATION_TO = 2180)


However, this does not solve my issue of getting the list of statuses for all LOCATION_TO.



DESIRED OUTPUT:



LOCATION_TO | STATUS  | TIME  |
120 | open | 11:29 |
130 | open | 10:38 |


Valid rows in COPY_HISTORY would be line 3 and line 4



This is an attempt at an extreme simplification of the actual database structure, so there are risks of typos in this question.



DDL



create table #COPY_HISTORY (COPY_ID int, DATA_ID char(1), [STATUS] varchar(16), [TIME] time)
create table #DATA_SET (DATA_ID char(1), [LOCATION] int)
create table #COPY_RULES (LOCATION_FROM int, LOCATION_TO int)

insert into #COPY_HISTORY
values
(1,'A','open','10:34'),
(1,'A','locked','10:37'),
(2,'A','open','10:38'),
(3,'B','open','11:29'),
(4,'C','open','10:37'),
(5,'D','locked','09:34')

insert into #DATA_SET
values
('A',88),
('B',77),
('C',88),
('D',99),
('E',88),
('F',88)


insert into #COPY_RULES
values
(55,110),
(66,120),
(77,120),
(88,130),
(99,130)









share|improve this question




















  • 1




    The description of the logic isn't a bad thing, but it might help your question to show the exact expected output based on the sample tables you showed above.
    – Tim Biegeleisen
    Nov 7 at 15:06






  • 2




    Use ROW_NUMBER() OVER (PARTITION BY) to order your records by descending order and number them, then just grab each record where ROW NUMBER equals 1
    – Ryan Wilson
    Nov 7 at 15:07










  • I have specified the desired output more clearly. Thanks for the input.
    – KjetilNordin
    Nov 7 at 15:13










  • I think maybe I'm on to something by switching cr2.LOCATION_TO = 2180, to cr2.LOCATION_TO = cr.LOCATION_TO in the last example, but I have been struggling with this for so long now, that even though it seems to give the desired results, I don't feel confident I've done it right.
    – KjetilNordin
    Nov 7 at 15:17










  • @RyanWilson unfortunately I do not understand the context.
    – KjetilNordin
    Nov 7 at 15:20













up vote
0
down vote

favorite









up vote
0
down vote

favorite











I have three tables. All follow the CRUD-pattern for inserts and updates. I need to make a query qhich selects all latest updates in one table, based on unique values of another table, twice removed through joins: (I have left out the CRUD structure on two of the tables in the example)



table COPY_HISTORY



COPY_ID | DATA_ID | STATUS | TIME  |        
1 | A | open | 10:34 |
1 | A | locked | 10:37 |
2 | A | open | 10:38 |
3 | B | open | 11:29 |
4 | C | open | 10:37 |
5 | D | locked | 09:34 |


table DATA_SET



DATA_ID | LOCATION |    
A | 88 |
B | 77 |
C | 88 |
D | 99 |
E | 88 |
F | 88 |


table COPY_RULES



LOCATION_FROM | LOCATION_TO 
55 | 110
66 | 120
77 | 120
88 | 130
99 | 130


What I need to do, is get the status for each LOCATION_TO from the copy rules table. If a copy is executed, it will be registered in copy history, and a copy will always overwrite all data at a location (110,120,130).



In this case, location 110 has never had a copy happen, and 110 should not be included in the returned data at all. Even though it exists in the rules, that is not relevant. 120 will receive copies from data sets on 66 and 77. However, we have no data set on 66 yet, so only 77 is relevant. B is located at 77, and data set B had a copy history registration inserted at 11:29, with the status open. Since this is the only status belonging to 120, status is open for 120, and overwrite will be possible for location 120.



Location 130 however, receive copies from both 88 and 99. This means that data sets A, C, D, E and F all will copy into 130, as they are all located in 88 or 99. We have two copy history logs for A, and one each for C and D. Meaning I want the status of only the newest registration, in this case, 10:38.



Now, I have tried to select the maximum value from COPY_HISTORY, based on the LOCATION_TO, but I am at an impasse getting the LOCATION_TO to be unique



I try:



SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME FROM COPY_HISTORY ch
JOIN DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
WHERE ch.TIME = (SELECT MAX(TIME) FROM COPY_HISTORY
WHERE COPY_ID = ch.COPY_ID
AND ch.DATA_ID = ds.DATA_ID
AND ds.LOCATION = cr.LOCATION_FROM)


That select max time statement is incomplete. The two AND-lines does nothing at all. The only thing I can accomplish, is getting rid of the first line in COPY_HISTORY, meaning I can get all max times for each COPY_ID, but I can not figure out how to filter them to get only the maximum for each LOCATION_TO instead.



I managed to get the max value from one single LOCATION_TO, by doing this:



SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME FROM COPY_HISTORY ch
JOIN DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
WHERE ch.TIME = (SELECT MAX(TIME) FROM COPY_HISTORY ch2, DATA_SET ds2, COPY_RULES cs2
WHERE ch2.DATA_ID = ds2.DATA_ID
AND ds2.LOCATION = cr2.LOCATION_FROM
AND cr2.LOCATION_TO = 2180)


However, this does not solve my issue of getting the list of statuses for all LOCATION_TO.



DESIRED OUTPUT:



LOCATION_TO | STATUS  | TIME  |
120 | open | 11:29 |
130 | open | 10:38 |


Valid rows in COPY_HISTORY would be line 3 and line 4



This is an attempt at an extreme simplification of the actual database structure, so there are risks of typos in this question.



DDL



create table #COPY_HISTORY (COPY_ID int, DATA_ID char(1), [STATUS] varchar(16), [TIME] time)
create table #DATA_SET (DATA_ID char(1), [LOCATION] int)
create table #COPY_RULES (LOCATION_FROM int, LOCATION_TO int)

insert into #COPY_HISTORY
values
(1,'A','open','10:34'),
(1,'A','locked','10:37'),
(2,'A','open','10:38'),
(3,'B','open','11:29'),
(4,'C','open','10:37'),
(5,'D','locked','09:34')

insert into #DATA_SET
values
('A',88),
('B',77),
('C',88),
('D',99),
('E',88),
('F',88)


insert into #COPY_RULES
values
(55,110),
(66,120),
(77,120),
(88,130),
(99,130)









share|improve this question















I have three tables. All follow the CRUD-pattern for inserts and updates. I need to make a query qhich selects all latest updates in one table, based on unique values of another table, twice removed through joins: (I have left out the CRUD structure on two of the tables in the example)



table COPY_HISTORY



COPY_ID | DATA_ID | STATUS | TIME  |        
1 | A | open | 10:34 |
1 | A | locked | 10:37 |
2 | A | open | 10:38 |
3 | B | open | 11:29 |
4 | C | open | 10:37 |
5 | D | locked | 09:34 |


table DATA_SET



DATA_ID | LOCATION |    
A | 88 |
B | 77 |
C | 88 |
D | 99 |
E | 88 |
F | 88 |


table COPY_RULES



LOCATION_FROM | LOCATION_TO 
55 | 110
66 | 120
77 | 120
88 | 130
99 | 130


What I need to do, is get the status for each LOCATION_TO from the copy rules table. If a copy is executed, it will be registered in copy history, and a copy will always overwrite all data at a location (110,120,130).



In this case, location 110 has never had a copy happen, and 110 should not be included in the returned data at all. Even though it exists in the rules, that is not relevant. 120 will receive copies from data sets on 66 and 77. However, we have no data set on 66 yet, so only 77 is relevant. B is located at 77, and data set B had a copy history registration inserted at 11:29, with the status open. Since this is the only status belonging to 120, status is open for 120, and overwrite will be possible for location 120.



Location 130 however, receive copies from both 88 and 99. This means that data sets A, C, D, E and F all will copy into 130, as they are all located in 88 or 99. We have two copy history logs for A, and one each for C and D. Meaning I want the status of only the newest registration, in this case, 10:38.



Now, I have tried to select the maximum value from COPY_HISTORY, based on the LOCATION_TO, but I am at an impasse getting the LOCATION_TO to be unique



I try:



SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME FROM COPY_HISTORY ch
JOIN DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
WHERE ch.TIME = (SELECT MAX(TIME) FROM COPY_HISTORY
WHERE COPY_ID = ch.COPY_ID
AND ch.DATA_ID = ds.DATA_ID
AND ds.LOCATION = cr.LOCATION_FROM)


That select max time statement is incomplete. The two AND-lines does nothing at all. The only thing I can accomplish, is getting rid of the first line in COPY_HISTORY, meaning I can get all max times for each COPY_ID, but I can not figure out how to filter them to get only the maximum for each LOCATION_TO instead.



I managed to get the max value from one single LOCATION_TO, by doing this:



SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME FROM COPY_HISTORY ch
JOIN DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
WHERE ch.TIME = (SELECT MAX(TIME) FROM COPY_HISTORY ch2, DATA_SET ds2, COPY_RULES cs2
WHERE ch2.DATA_ID = ds2.DATA_ID
AND ds2.LOCATION = cr2.LOCATION_FROM
AND cr2.LOCATION_TO = 2180)


However, this does not solve my issue of getting the list of statuses for all LOCATION_TO.



DESIRED OUTPUT:



LOCATION_TO | STATUS  | TIME  |
120 | open | 11:29 |
130 | open | 10:38 |


Valid rows in COPY_HISTORY would be line 3 and line 4



This is an attempt at an extreme simplification of the actual database structure, so there are risks of typos in this question.



DDL



create table #COPY_HISTORY (COPY_ID int, DATA_ID char(1), [STATUS] varchar(16), [TIME] time)
create table #DATA_SET (DATA_ID char(1), [LOCATION] int)
create table #COPY_RULES (LOCATION_FROM int, LOCATION_TO int)

insert into #COPY_HISTORY
values
(1,'A','open','10:34'),
(1,'A','locked','10:37'),
(2,'A','open','10:38'),
(3,'B','open','11:29'),
(4,'C','open','10:37'),
(5,'D','locked','09:34')

insert into #DATA_SET
values
('A',88),
('B',77),
('C',88),
('D',99),
('E',88),
('F',88)


insert into #COPY_RULES
values
(55,110),
(66,120),
(77,120),
(88,130),
(99,130)






sql-server






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 7 at 15:30

























asked Nov 7 at 15:02









KjetilNordin

1,40042968




1,40042968








  • 1




    The description of the logic isn't a bad thing, but it might help your question to show the exact expected output based on the sample tables you showed above.
    – Tim Biegeleisen
    Nov 7 at 15:06






  • 2




    Use ROW_NUMBER() OVER (PARTITION BY) to order your records by descending order and number them, then just grab each record where ROW NUMBER equals 1
    – Ryan Wilson
    Nov 7 at 15:07










  • I have specified the desired output more clearly. Thanks for the input.
    – KjetilNordin
    Nov 7 at 15:13










  • I think maybe I'm on to something by switching cr2.LOCATION_TO = 2180, to cr2.LOCATION_TO = cr.LOCATION_TO in the last example, but I have been struggling with this for so long now, that even though it seems to give the desired results, I don't feel confident I've done it right.
    – KjetilNordin
    Nov 7 at 15:17










  • @RyanWilson unfortunately I do not understand the context.
    – KjetilNordin
    Nov 7 at 15:20














  • 1




    The description of the logic isn't a bad thing, but it might help your question to show the exact expected output based on the sample tables you showed above.
    – Tim Biegeleisen
    Nov 7 at 15:06






  • 2




    Use ROW_NUMBER() OVER (PARTITION BY) to order your records by descending order and number them, then just grab each record where ROW NUMBER equals 1
    – Ryan Wilson
    Nov 7 at 15:07










  • I have specified the desired output more clearly. Thanks for the input.
    – KjetilNordin
    Nov 7 at 15:13










  • I think maybe I'm on to something by switching cr2.LOCATION_TO = 2180, to cr2.LOCATION_TO = cr.LOCATION_TO in the last example, but I have been struggling with this for so long now, that even though it seems to give the desired results, I don't feel confident I've done it right.
    – KjetilNordin
    Nov 7 at 15:17










  • @RyanWilson unfortunately I do not understand the context.
    – KjetilNordin
    Nov 7 at 15:20








1




1




The description of the logic isn't a bad thing, but it might help your question to show the exact expected output based on the sample tables you showed above.
– Tim Biegeleisen
Nov 7 at 15:06




The description of the logic isn't a bad thing, but it might help your question to show the exact expected output based on the sample tables you showed above.
– Tim Biegeleisen
Nov 7 at 15:06




2




2




Use ROW_NUMBER() OVER (PARTITION BY) to order your records by descending order and number them, then just grab each record where ROW NUMBER equals 1
– Ryan Wilson
Nov 7 at 15:07




Use ROW_NUMBER() OVER (PARTITION BY) to order your records by descending order and number them, then just grab each record where ROW NUMBER equals 1
– Ryan Wilson
Nov 7 at 15:07












I have specified the desired output more clearly. Thanks for the input.
– KjetilNordin
Nov 7 at 15:13




I have specified the desired output more clearly. Thanks for the input.
– KjetilNordin
Nov 7 at 15:13












I think maybe I'm on to something by switching cr2.LOCATION_TO = 2180, to cr2.LOCATION_TO = cr.LOCATION_TO in the last example, but I have been struggling with this for so long now, that even though it seems to give the desired results, I don't feel confident I've done it right.
– KjetilNordin
Nov 7 at 15:17




I think maybe I'm on to something by switching cr2.LOCATION_TO = 2180, to cr2.LOCATION_TO = cr.LOCATION_TO in the last example, but I have been struggling with this for so long now, that even though it seems to give the desired results, I don't feel confident I've done it right.
– KjetilNordin
Nov 7 at 15:17












@RyanWilson unfortunately I do not understand the context.
– KjetilNordin
Nov 7 at 15:20




@RyanWilson unfortunately I do not understand the context.
– KjetilNordin
Nov 7 at 15:20












2 Answers
2






active

oldest

votes

















up vote
4
down vote



accepted










I think this query will work for you. As Ryan mentioned in the comments, you can use the ROW_NUMBER analytical function to rank your location records (PARTITION BY) by the time (ORDER BY), then only return the first row for each location (RowOrder = 1).



SELECT * FROM (
SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME, ROW_NUMBER() OVER (PARTITION BY cr.LOCATION_TO ORDER BY ch.TIME desc) AS RowOrder
FROM #COPY_HISTORY ch
JOIN #DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN #COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
) ordered_set
WHERE RowOrder = 1





share|improve this answer





















  • This works! It really works. And it is simple, understandable and great. It even works on my live code, with far more complex structure. I am very happy now. Thanks a lot!
    – KjetilNordin
    Nov 7 at 15:34










  • @scratt Good job. +1
    – Ryan Wilson
    Nov 7 at 15:36












  • Thanks to you too @RyanWilson. I read your answers as you were editing them, and it was close. It was just the wrapping select that was missing I think
    – KjetilNordin
    Nov 7 at 15:37










  • @KjetilNordin No problem Glad to help. Yeah, I was trying to work out the query while programming something, lol. Doesn't work well for me.
    – Ryan Wilson
    Nov 7 at 15:38


















up vote
2
down vote













If I'm getting the logic correctly:



DECLARE @COPY_HISTORY TABLE
(
COPY_ID INT NOT NULL,
DATA_ID CHAR(1) NOT NULL,
[STATUS] VARCHAR(50) NOT NULL,
[TIME] TIME NOT NULL
);

DECLARE @DATA_SET TABLE
(
DATA_ID CHAR(1) NOT NULL,
LOCATION INT NOT NULL
);

DECLARE @COPY_RULES TABLE
(
LOCATION_FROM INT NOT NULL,
LOCATION_TO INT NOT NULL
);

INSERT INTO @COPY_HISTORY
VALUES (1,'A','open','10:34'),
(1,'A','locked','10:37'),
(2,'A','open','10:38'),
(3,'B','open','11:29'),
(4,'C','open','10:37'),
(5,'D','locked','09:34');

INSERT INTO @DATA_SET
VALUES ('A',88),
('B',77),
('C',88),
('D',99),
('E',88),
('F',88);

INSERT INTO @COPY_RULES
VALUES (55,110),
(66,120),
(77,120),
(88,130),
(99,130);

WITH CTE
AS
(
SELECT CR.LOCATION_TO,
CH.STATUS,
CH.TIME,
ROW_NUMBER() OVER(PARTITION BY CR.LOCATION_TO ORDER BY CASE WHEN CH.Status = 'OPEN' THEN 1 ELSE 0 END DESC, CH.TIME DESC) AS RN
FROM @COPY_HISTORY AS CH
INNER
JOIN @DATA_SET AS DS
ON CH.DATA_ID = DS.DATA_ID
INNER
JOIN @COPY_RULES AS CR
ON CR.LOCATION_FROM = DS.LOCATION
)
SELECT C.LOCATION_TO,
C.STATUS,
C.TIME
FROM CTE AS C
WHERE RN = 1;





share|improve this answer





















  • Nice touch with the Common Table Expression, +1
    – Ryan Wilson
    Nov 7 at 15:37










  • I'll have to read this more carefully too. Seems there is learning to get from here as well :)
    – KjetilNordin
    Nov 7 at 15:41










  • I'd added in the additional logic on the ROW_NUMBER sort as I wasn't sure if you wanted the latest ones with status of open to appear higher in preference than the locked
    – Dohsan
    Nov 7 at 15:44










  • latest have priority no matter. I just need to display if the locations have a data set which should not be overwritten at the current time. It is for displaying if test environments are occupied or available for others to import their own data into.
    – KjetilNordin
    Nov 7 at 15:52













Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














 

draft saved


draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53192101%2fhow-do-i-select-max-value-from-a-table-based-on-unique-value-on-joined-tables%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
4
down vote



accepted










I think this query will work for you. As Ryan mentioned in the comments, you can use the ROW_NUMBER analytical function to rank your location records (PARTITION BY) by the time (ORDER BY), then only return the first row for each location (RowOrder = 1).



SELECT * FROM (
SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME, ROW_NUMBER() OVER (PARTITION BY cr.LOCATION_TO ORDER BY ch.TIME desc) AS RowOrder
FROM #COPY_HISTORY ch
JOIN #DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN #COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
) ordered_set
WHERE RowOrder = 1





share|improve this answer





















  • This works! It really works. And it is simple, understandable and great. It even works on my live code, with far more complex structure. I am very happy now. Thanks a lot!
    – KjetilNordin
    Nov 7 at 15:34










  • @scratt Good job. +1
    – Ryan Wilson
    Nov 7 at 15:36












  • Thanks to you too @RyanWilson. I read your answers as you were editing them, and it was close. It was just the wrapping select that was missing I think
    – KjetilNordin
    Nov 7 at 15:37










  • @KjetilNordin No problem Glad to help. Yeah, I was trying to work out the query while programming something, lol. Doesn't work well for me.
    – Ryan Wilson
    Nov 7 at 15:38















up vote
4
down vote



accepted










I think this query will work for you. As Ryan mentioned in the comments, you can use the ROW_NUMBER analytical function to rank your location records (PARTITION BY) by the time (ORDER BY), then only return the first row for each location (RowOrder = 1).



SELECT * FROM (
SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME, ROW_NUMBER() OVER (PARTITION BY cr.LOCATION_TO ORDER BY ch.TIME desc) AS RowOrder
FROM #COPY_HISTORY ch
JOIN #DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN #COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
) ordered_set
WHERE RowOrder = 1





share|improve this answer





















  • This works! It really works. And it is simple, understandable and great. It even works on my live code, with far more complex structure. I am very happy now. Thanks a lot!
    – KjetilNordin
    Nov 7 at 15:34










  • @scratt Good job. +1
    – Ryan Wilson
    Nov 7 at 15:36












  • Thanks to you too @RyanWilson. I read your answers as you were editing them, and it was close. It was just the wrapping select that was missing I think
    – KjetilNordin
    Nov 7 at 15:37










  • @KjetilNordin No problem Glad to help. Yeah, I was trying to work out the query while programming something, lol. Doesn't work well for me.
    – Ryan Wilson
    Nov 7 at 15:38













up vote
4
down vote



accepted







up vote
4
down vote



accepted






I think this query will work for you. As Ryan mentioned in the comments, you can use the ROW_NUMBER analytical function to rank your location records (PARTITION BY) by the time (ORDER BY), then only return the first row for each location (RowOrder = 1).



SELECT * FROM (
SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME, ROW_NUMBER() OVER (PARTITION BY cr.LOCATION_TO ORDER BY ch.TIME desc) AS RowOrder
FROM #COPY_HISTORY ch
JOIN #DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN #COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
) ordered_set
WHERE RowOrder = 1





share|improve this answer












I think this query will work for you. As Ryan mentioned in the comments, you can use the ROW_NUMBER analytical function to rank your location records (PARTITION BY) by the time (ORDER BY), then only return the first row for each location (RowOrder = 1).



SELECT * FROM (
SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME, ROW_NUMBER() OVER (PARTITION BY cr.LOCATION_TO ORDER BY ch.TIME desc) AS RowOrder
FROM #COPY_HISTORY ch
JOIN #DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN #COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
) ordered_set
WHERE RowOrder = 1






share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 7 at 15:31









scratt

1718




1718












  • This works! It really works. And it is simple, understandable and great. It even works on my live code, with far more complex structure. I am very happy now. Thanks a lot!
    – KjetilNordin
    Nov 7 at 15:34










  • @scratt Good job. +1
    – Ryan Wilson
    Nov 7 at 15:36












  • Thanks to you too @RyanWilson. I read your answers as you were editing them, and it was close. It was just the wrapping select that was missing I think
    – KjetilNordin
    Nov 7 at 15:37










  • @KjetilNordin No problem Glad to help. Yeah, I was trying to work out the query while programming something, lol. Doesn't work well for me.
    – Ryan Wilson
    Nov 7 at 15:38


















  • This works! It really works. And it is simple, understandable and great. It even works on my live code, with far more complex structure. I am very happy now. Thanks a lot!
    – KjetilNordin
    Nov 7 at 15:34










  • @scratt Good job. +1
    – Ryan Wilson
    Nov 7 at 15:36












  • Thanks to you too @RyanWilson. I read your answers as you were editing them, and it was close. It was just the wrapping select that was missing I think
    – KjetilNordin
    Nov 7 at 15:37










  • @KjetilNordin No problem Glad to help. Yeah, I was trying to work out the query while programming something, lol. Doesn't work well for me.
    – Ryan Wilson
    Nov 7 at 15:38
















This works! It really works. And it is simple, understandable and great. It even works on my live code, with far more complex structure. I am very happy now. Thanks a lot!
– KjetilNordin
Nov 7 at 15:34




This works! It really works. And it is simple, understandable and great. It even works on my live code, with far more complex structure. I am very happy now. Thanks a lot!
– KjetilNordin
Nov 7 at 15:34












@scratt Good job. +1
– Ryan Wilson
Nov 7 at 15:36






@scratt Good job. +1
– Ryan Wilson
Nov 7 at 15:36














Thanks to you too @RyanWilson. I read your answers as you were editing them, and it was close. It was just the wrapping select that was missing I think
– KjetilNordin
Nov 7 at 15:37




Thanks to you too @RyanWilson. I read your answers as you were editing them, and it was close. It was just the wrapping select that was missing I think
– KjetilNordin
Nov 7 at 15:37












@KjetilNordin No problem Glad to help. Yeah, I was trying to work out the query while programming something, lol. Doesn't work well for me.
– Ryan Wilson
Nov 7 at 15:38




@KjetilNordin No problem Glad to help. Yeah, I was trying to work out the query while programming something, lol. Doesn't work well for me.
– Ryan Wilson
Nov 7 at 15:38












up vote
2
down vote













If I'm getting the logic correctly:



DECLARE @COPY_HISTORY TABLE
(
COPY_ID INT NOT NULL,
DATA_ID CHAR(1) NOT NULL,
[STATUS] VARCHAR(50) NOT NULL,
[TIME] TIME NOT NULL
);

DECLARE @DATA_SET TABLE
(
DATA_ID CHAR(1) NOT NULL,
LOCATION INT NOT NULL
);

DECLARE @COPY_RULES TABLE
(
LOCATION_FROM INT NOT NULL,
LOCATION_TO INT NOT NULL
);

INSERT INTO @COPY_HISTORY
VALUES (1,'A','open','10:34'),
(1,'A','locked','10:37'),
(2,'A','open','10:38'),
(3,'B','open','11:29'),
(4,'C','open','10:37'),
(5,'D','locked','09:34');

INSERT INTO @DATA_SET
VALUES ('A',88),
('B',77),
('C',88),
('D',99),
('E',88),
('F',88);

INSERT INTO @COPY_RULES
VALUES (55,110),
(66,120),
(77,120),
(88,130),
(99,130);

WITH CTE
AS
(
SELECT CR.LOCATION_TO,
CH.STATUS,
CH.TIME,
ROW_NUMBER() OVER(PARTITION BY CR.LOCATION_TO ORDER BY CASE WHEN CH.Status = 'OPEN' THEN 1 ELSE 0 END DESC, CH.TIME DESC) AS RN
FROM @COPY_HISTORY AS CH
INNER
JOIN @DATA_SET AS DS
ON CH.DATA_ID = DS.DATA_ID
INNER
JOIN @COPY_RULES AS CR
ON CR.LOCATION_FROM = DS.LOCATION
)
SELECT C.LOCATION_TO,
C.STATUS,
C.TIME
FROM CTE AS C
WHERE RN = 1;





share|improve this answer





















  • Nice touch with the Common Table Expression, +1
    – Ryan Wilson
    Nov 7 at 15:37










  • I'll have to read this more carefully too. Seems there is learning to get from here as well :)
    – KjetilNordin
    Nov 7 at 15:41










  • I'd added in the additional logic on the ROW_NUMBER sort as I wasn't sure if you wanted the latest ones with status of open to appear higher in preference than the locked
    – Dohsan
    Nov 7 at 15:44










  • latest have priority no matter. I just need to display if the locations have a data set which should not be overwritten at the current time. It is for displaying if test environments are occupied or available for others to import their own data into.
    – KjetilNordin
    Nov 7 at 15:52

















up vote
2
down vote













If I'm getting the logic correctly:



DECLARE @COPY_HISTORY TABLE
(
COPY_ID INT NOT NULL,
DATA_ID CHAR(1) NOT NULL,
[STATUS] VARCHAR(50) NOT NULL,
[TIME] TIME NOT NULL
);

DECLARE @DATA_SET TABLE
(
DATA_ID CHAR(1) NOT NULL,
LOCATION INT NOT NULL
);

DECLARE @COPY_RULES TABLE
(
LOCATION_FROM INT NOT NULL,
LOCATION_TO INT NOT NULL
);

INSERT INTO @COPY_HISTORY
VALUES (1,'A','open','10:34'),
(1,'A','locked','10:37'),
(2,'A','open','10:38'),
(3,'B','open','11:29'),
(4,'C','open','10:37'),
(5,'D','locked','09:34');

INSERT INTO @DATA_SET
VALUES ('A',88),
('B',77),
('C',88),
('D',99),
('E',88),
('F',88);

INSERT INTO @COPY_RULES
VALUES (55,110),
(66,120),
(77,120),
(88,130),
(99,130);

WITH CTE
AS
(
SELECT CR.LOCATION_TO,
CH.STATUS,
CH.TIME,
ROW_NUMBER() OVER(PARTITION BY CR.LOCATION_TO ORDER BY CASE WHEN CH.Status = 'OPEN' THEN 1 ELSE 0 END DESC, CH.TIME DESC) AS RN
FROM @COPY_HISTORY AS CH
INNER
JOIN @DATA_SET AS DS
ON CH.DATA_ID = DS.DATA_ID
INNER
JOIN @COPY_RULES AS CR
ON CR.LOCATION_FROM = DS.LOCATION
)
SELECT C.LOCATION_TO,
C.STATUS,
C.TIME
FROM CTE AS C
WHERE RN = 1;





share|improve this answer





















  • Nice touch with the Common Table Expression, +1
    – Ryan Wilson
    Nov 7 at 15:37










  • I'll have to read this more carefully too. Seems there is learning to get from here as well :)
    – KjetilNordin
    Nov 7 at 15:41










  • I'd added in the additional logic on the ROW_NUMBER sort as I wasn't sure if you wanted the latest ones with status of open to appear higher in preference than the locked
    – Dohsan
    Nov 7 at 15:44










  • latest have priority no matter. I just need to display if the locations have a data set which should not be overwritten at the current time. It is for displaying if test environments are occupied or available for others to import their own data into.
    – KjetilNordin
    Nov 7 at 15:52















up vote
2
down vote










up vote
2
down vote









If I'm getting the logic correctly:



DECLARE @COPY_HISTORY TABLE
(
COPY_ID INT NOT NULL,
DATA_ID CHAR(1) NOT NULL,
[STATUS] VARCHAR(50) NOT NULL,
[TIME] TIME NOT NULL
);

DECLARE @DATA_SET TABLE
(
DATA_ID CHAR(1) NOT NULL,
LOCATION INT NOT NULL
);

DECLARE @COPY_RULES TABLE
(
LOCATION_FROM INT NOT NULL,
LOCATION_TO INT NOT NULL
);

INSERT INTO @COPY_HISTORY
VALUES (1,'A','open','10:34'),
(1,'A','locked','10:37'),
(2,'A','open','10:38'),
(3,'B','open','11:29'),
(4,'C','open','10:37'),
(5,'D','locked','09:34');

INSERT INTO @DATA_SET
VALUES ('A',88),
('B',77),
('C',88),
('D',99),
('E',88),
('F',88);

INSERT INTO @COPY_RULES
VALUES (55,110),
(66,120),
(77,120),
(88,130),
(99,130);

WITH CTE
AS
(
SELECT CR.LOCATION_TO,
CH.STATUS,
CH.TIME,
ROW_NUMBER() OVER(PARTITION BY CR.LOCATION_TO ORDER BY CASE WHEN CH.Status = 'OPEN' THEN 1 ELSE 0 END DESC, CH.TIME DESC) AS RN
FROM @COPY_HISTORY AS CH
INNER
JOIN @DATA_SET AS DS
ON CH.DATA_ID = DS.DATA_ID
INNER
JOIN @COPY_RULES AS CR
ON CR.LOCATION_FROM = DS.LOCATION
)
SELECT C.LOCATION_TO,
C.STATUS,
C.TIME
FROM CTE AS C
WHERE RN = 1;





share|improve this answer












If I'm getting the logic correctly:



DECLARE @COPY_HISTORY TABLE
(
COPY_ID INT NOT NULL,
DATA_ID CHAR(1) NOT NULL,
[STATUS] VARCHAR(50) NOT NULL,
[TIME] TIME NOT NULL
);

DECLARE @DATA_SET TABLE
(
DATA_ID CHAR(1) NOT NULL,
LOCATION INT NOT NULL
);

DECLARE @COPY_RULES TABLE
(
LOCATION_FROM INT NOT NULL,
LOCATION_TO INT NOT NULL
);

INSERT INTO @COPY_HISTORY
VALUES (1,'A','open','10:34'),
(1,'A','locked','10:37'),
(2,'A','open','10:38'),
(3,'B','open','11:29'),
(4,'C','open','10:37'),
(5,'D','locked','09:34');

INSERT INTO @DATA_SET
VALUES ('A',88),
('B',77),
('C',88),
('D',99),
('E',88),
('F',88);

INSERT INTO @COPY_RULES
VALUES (55,110),
(66,120),
(77,120),
(88,130),
(99,130);

WITH CTE
AS
(
SELECT CR.LOCATION_TO,
CH.STATUS,
CH.TIME,
ROW_NUMBER() OVER(PARTITION BY CR.LOCATION_TO ORDER BY CASE WHEN CH.Status = 'OPEN' THEN 1 ELSE 0 END DESC, CH.TIME DESC) AS RN
FROM @COPY_HISTORY AS CH
INNER
JOIN @DATA_SET AS DS
ON CH.DATA_ID = DS.DATA_ID
INNER
JOIN @COPY_RULES AS CR
ON CR.LOCATION_FROM = DS.LOCATION
)
SELECT C.LOCATION_TO,
C.STATUS,
C.TIME
FROM CTE AS C
WHERE RN = 1;






share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 7 at 15:31









Dohsan

2768




2768












  • Nice touch with the Common Table Expression, +1
    – Ryan Wilson
    Nov 7 at 15:37










  • I'll have to read this more carefully too. Seems there is learning to get from here as well :)
    – KjetilNordin
    Nov 7 at 15:41










  • I'd added in the additional logic on the ROW_NUMBER sort as I wasn't sure if you wanted the latest ones with status of open to appear higher in preference than the locked
    – Dohsan
    Nov 7 at 15:44










  • latest have priority no matter. I just need to display if the locations have a data set which should not be overwritten at the current time. It is for displaying if test environments are occupied or available for others to import their own data into.
    – KjetilNordin
    Nov 7 at 15:52




















  • Nice touch with the Common Table Expression, +1
    – Ryan Wilson
    Nov 7 at 15:37










  • I'll have to read this more carefully too. Seems there is learning to get from here as well :)
    – KjetilNordin
    Nov 7 at 15:41










  • I'd added in the additional logic on the ROW_NUMBER sort as I wasn't sure if you wanted the latest ones with status of open to appear higher in preference than the locked
    – Dohsan
    Nov 7 at 15:44










  • latest have priority no matter. I just need to display if the locations have a data set which should not be overwritten at the current time. It is for displaying if test environments are occupied or available for others to import their own data into.
    – KjetilNordin
    Nov 7 at 15:52


















Nice touch with the Common Table Expression, +1
– Ryan Wilson
Nov 7 at 15:37




Nice touch with the Common Table Expression, +1
– Ryan Wilson
Nov 7 at 15:37












I'll have to read this more carefully too. Seems there is learning to get from here as well :)
– KjetilNordin
Nov 7 at 15:41




I'll have to read this more carefully too. Seems there is learning to get from here as well :)
– KjetilNordin
Nov 7 at 15:41












I'd added in the additional logic on the ROW_NUMBER sort as I wasn't sure if you wanted the latest ones with status of open to appear higher in preference than the locked
– Dohsan
Nov 7 at 15:44




I'd added in the additional logic on the ROW_NUMBER sort as I wasn't sure if you wanted the latest ones with status of open to appear higher in preference than the locked
– Dohsan
Nov 7 at 15:44












latest have priority no matter. I just need to display if the locations have a data set which should not be overwritten at the current time. It is for displaying if test environments are occupied or available for others to import their own data into.
– KjetilNordin
Nov 7 at 15:52






latest have priority no matter. I just need to display if the locations have a data set which should not be overwritten at the current time. It is for displaying if test environments are occupied or available for others to import their own data into.
– KjetilNordin
Nov 7 at 15:52




















 

draft saved


draft discarded



















































 


draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53192101%2fhow-do-i-select-max-value-from-a-table-based-on-unique-value-on-joined-tables%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







這個網誌中的熱門文章

Academy of Television Arts & Sciences

L'Équipe

1995 France bombings