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)
|
show 2 more comments
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)
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
UseROW_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
|
show 2 more comments
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)
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)
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
UseROW_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
|
show 2 more comments
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
UseROW_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
|
show 2 more comments
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
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
add a comment |
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;
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
add a comment |
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
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
add a comment |
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
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
add a comment |
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
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
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
add a comment |
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
add a comment |
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;
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
add a comment |
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;
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
add a comment |
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;
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;
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
add a comment |
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
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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