표:
UserId, Value, Date.
UserId, 각 UserId의 max (Date)에 대한 값을 가져오고 싶습니다. 즉, 최신 날짜를 가진 각 UserId의 값입니다. SQL로 간단하게 수행 할 수있는 방법이 있습니까? (바람직하게 오라클)
업데이트 : 모든 모호성에 대한 사과 : 모든 UserId를 가져와야합니다. 그러나 각 UserId에 대해 해당 사용자가 최신 날짜를 가진 행만 해당합니다.
표:
UserId, Value, Date.
UserId, 각 UserId의 max (Date)에 대한 값을 가져오고 싶습니다. 즉, 최신 날짜를 가진 각 UserId의 값입니다. SQL로 간단하게 수행 할 수있는 방법이 있습니까? (바람직하게 오라클)
업데이트 : 모든 모호성에 대한 사과 : 모든 UserId를 가져와야합니다. 그러나 각 UserId에 대해 해당 사용자가 최신 날짜를 가진 행만 해당합니다.
답변:
그러면 my_date 열 값이 해당 사용자 ID의 최대 my_date 값과 동일한 모든 행을 검색합니다. 최대 날짜가 여러 행에있는 사용자 ID에 대해 여러 행을 검색 할 수 있습니다.
select userid,
my_date,
...
from
(
select userid,
my_date,
...
max(my_date) over (partition by userid) max_my_date
from users
)
where my_date = max_my_date
"분석 기능 락"
편집 : 첫 번째 의견과 관련하여 ...
"분석 쿼리 및 자체 조인을 사용하면 분석 쿼리의 목적이 무효화됩니다."
이 코드에는 자체 조인이 없습니다. 대신 인라인 뷰 결과에 분석 함수가 포함 된 술어가 있습니다 (매우 다른 문제이며 완전히 표준 관행 임).
"Oracle의 기본 창은 파티션의 첫 번째 행에서 현재 행까지입니다."
windowing 절은 order by 절이있는 경우에만 적용 할 수 있습니다. order by 절이 없으면 기본적으로 windowing 절이 적용되지 않으며 명시 적으로 지정할 수 없습니다.
코드가 작동합니다.
MAX(...) OVER (...)
당신은 또한 사용할 수 있습니다 ROW_NUMBER() OVER (...)
(상단-N 당 그룹) 또는 RANK() OVER (...)
(최대-N 당 그룹).
많은 사람들이 하위 쿼리 또는 다른 공급 업체별 기능을 사용하여이 작업을 수행하는 것을 보았지만 종종 다음과 같은 방식으로 하위 쿼리없이 이러한 종류의 쿼리를 수행합니다. 일반 표준 SQL을 사용하므로 모든 RDBMS 브랜드에서 작동해야합니다.
SELECT t1.*
FROM mytable t1
LEFT OUTER JOIN mytable t2
ON (t1.UserId = t2.UserId AND t1."Date" < t2."Date")
WHERE t2.UserId IS NULL;
즉 t1
, 동일 UserId
하고 더 큰 날짜를 가진 다른 행이없는 곳 에서 행을 가져옵니다 .
(식별자 "Date"는 SQL 예약어이므로 구분 기호에 넣습니다.)
의 경우 t1."Date" = t2."Date"
두배가 나타납니다. 일반적으로 테이블에는 auto_inc(seq)
키가 있습니다 (예 :) id
. 이중화를 피하기 위해 다음을 사용할 수 있습니다.
SELECT t1.*
FROM mytable t1
LEFT OUTER JOIN mytable t2
ON t1.UserId = t2.UserId AND ((t1."Date" < t2."Date")
OR (t1."Date" = t2."Date" AND t1.id < t2.id))
WHERE t2.UserId IS NULL;
@ Farhan의 의견 :
자세한 설명은 다음과 같습니다.
외부 조인이 t1
와 조인을 시도합니다 t2
. 기본적으로 모든 결과 t1
가 반환되고 에 일치하는 항목이 있으면t2
반환됩니다. t2
의 지정된 행에 일치하는 항목이 없으면 t1
쿼리는 여전히의 행을 반환하고 모든 열의 자리 표시 자로 t1
사용합니다 . 이것이 외부 조인이 일반적으로 작동하는 방식입니다.NULL
t2
이 쿼리의 트릭 t2
은 동일 userid
하고 더 큰 일치해야하는 조인의 일치 조건을 디자인하는 것 date
입니다. 행이 존재하면되는 아이디어 t2
가 더 큰이 date
, 다음의 행 t1
이 비교있어 수없는 큰 일 date
이에 대한을 userid
. 그러나 일치하는 것이 없으면 (즉 , 행 t2
보다 큰 행이없는 경우) 행이 주어진 행에 대해 가장 큰 행 임을 알 수 있습니다.date
t1
t1
date
userid
이러한 경우의 열 (때 일치가 없다) t2
됩니다 NULL
에 지정된에도 열 조인 조건 -. 그래서 우리가 사용하는 이유는 주어진 WHERE t2.UserId IS NULL
행보다 큰 행을 찾을 수없는 경우를 찾고 있기 때문 입니다.date
userid
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
FROM table
GROUP BY userid
정확한 열 이름을 모르지만 다음과 같습니다.
사용자 ID, 값을 선택하십시오 사용자 u1에서 여기서 날짜 = (최대 선택 (날짜) 사용자 u2에서 여기서 u1.userid = u2.userid)
일하고 있지는 않지만 Oracle이 제공 해야하는 것은 아니지만 Oracle은 IN 절에서 여러 열을 일치시킬 수 있다는 것을 기억합니다. 생각.
아마도 이런 식일 것입니다 (열 목록을 괄호로 묶어야하는지 여부를 기억할 수 없습니다).
SELECT *
FROM MyTable
WHERE (User, Date) IN
( SELECT User, MAX(Date) FROM MyTable GROUP BY User)
편집 : 그냥 진짜 시도했습니다.
SQL> create table MyTable (usr char(1), dt date);
SQL> insert into mytable values ('A','01-JAN-2009');
SQL> insert into mytable values ('B','01-JAN-2009');
SQL> insert into mytable values ('A', '31-DEC-2008');
SQL> insert into mytable values ('B', '31-DEC-2008');
SQL> select usr, dt from mytable
2 where (usr, dt) in
3 ( select usr, max(dt) from mytable group by usr)
4 /
U DT
- ---------
A 01-JAN-09
B 01-JAN-09
다른 곳에서 언급 된 신기한 물건 중 일부가 더 성능이 좋을 수도 있지만 작동합니다.
Oracle을 요청했지만 SQL 2005에서는 다음을 사용합니다.
-- Single Value
;WITH ByDate
AS (
SELECT UserId, Value, ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) RowNum
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE RowNum = 1
-- Multiple values where dates match
;WITH ByDate
AS (
SELECT UserId, Value, RANK() OVER (PARTITION BY UserId ORDER BY Date DESC) Rnk
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE Rnk = 1
나는 그것을 테스트 할 오라클이 없지만 가장 효율적인 솔루션은 분석 쿼리를 사용하는 것입니다. 다음과 같이 보일 것입니다 :
SELECT DISTINCT
UserId
, MaxValue
FROM (
SELECT UserId
, FIRST (Value) Over (
PARTITION BY UserId
ORDER BY Date DESC
) MaxValue
FROM SomeTable
)
외부 쿼리를 제거하고 내부를 구별 할 수 있다고 생각하지만 확실하지 않습니다. 그 동안 나는 이것이 작동한다는 것을 안다.
분석 쿼리에 대해 배우려면 http://www.orafaq.com/node/55 및 http://www.akadia.com/services/ora_analytic_functions.html을 읽으십시오 . 다음은 간단한 요약입니다.
후드 분석 쿼리에서 전체 데이터 세트를 정렬 한 다음 순차적으로 처리하십시오. 처리 할 때 특정 기준에 따라 데이터 세트를 분할 한 다음 각 행에 대해 일부 창을보고 (기본값은 현재 행에 대한 파티션의 첫 번째 값-기본값은 가장 효율적 임) a를 사용하여 값을 계산할 수 있습니다 분석 함수 수 (목록은 집계 함수와 매우 유사 함)
이 경우 내부 쿼리의 기능은 다음과 같습니다. 전체 데이터 세트는 UserId, Date DESC로 정렬됩니다. 그런 다음 한 번에 처리합니다. 각 행에 대해 UserId 및 해당 UserId에 대해 표시된 첫 번째 날짜를 반환합니다 (날짜가 DESC로 정렬되므로 최대 날짜 임). 이렇게하면 중복 된 행으로 답을 얻을 수 있습니다. 그런 다음 외부 DISTINCT가 중복을 찌그러 뜨립니다.
이것은 특히 분석 쿼리의 화려한 예가 아닙니다. 훨씬 더 큰 승리를 거두려면 재정 영수증 표를 작성하고 각 사용자 및 영수증에 대해 계산 한 총액을 지불하십시오. 분석 쿼리는이를 효율적으로 해결합니다. 다른 솔루션은 효율성이 떨어집니다. 이것이 그들이 2003 SQL 표준의 일부인 이유입니다. (불행히도 Postgres에는 아직 없습니다. Grrr ...)
QUALIFY 절이 가장 단순하고 우수하지 않습니까?
select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1
문맥 상, Teradata에서이 크기의 테스트는이 QUALIFY 버전에서는 17 초, '인라인 뷰'/ Aldridge 솔루션 # 1에서는 23 초에 실행됩니다.
rank()
관계가있는 상황에서는 기능에 주의하십시오 . 둘 이상으로 끝날 수 rank=1
있습니다. row_number()
하나의 레코드 만 반환 하려면 사용 하는 것이 좋습니다.
QUALIFY
절은 Teradata에만 해당됩니다. Oracle에서는 (적어도) WHERE
래핑 select 문에 절을 사용하여 쿼리를 중첩하고 필터링해야합니다 (아마도 성능에 영향을 미칩니다).
에서 상위 n 쿼리를 분석 함수와 함께 Oracle 12c+
사용 하여 하위 쿼리 없이 매우 간결하게이를 달성 할 수 있습니다 .rank
select *
from your_table
order by rank() over (partition by user_id order by my_date desc)
fetch first 1 row with ties;
위의 내용은 사용자 당 최대 my_date를 가진 모든 행을 반환합니다.
당신이 최대 날짜가 하나 개의 행을 원하는 경우, 다음을 대체 rank
와 함께 row_number
:
select *
from your_table
order by row_number() over (partition by user_id order by my_date desc)
fetch first 1 row with ties;
사용 ROW_NUMBER()
하강 순위에 고유을 할당 할 Date
각 UserId
각의 첫 번째 행에 다음 필터를UserId
(즉, ROW_NUMBER
= 1).
SELECT UserId, Value, Date
FROM (SELECT UserId, Value, Date,
ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) rn
FROM users) u
WHERE rn = 1;
직장에서 "실시간"예제를 작성해야했습니다. :)
이것은 같은 날짜 에 UserId에 대한 여러 값을 지원 합니다.
열 : UserId, 값, 날짜
SELECT
DISTINCT UserId,
MAX(Date) OVER (PARTITION BY UserId ORDER BY Date DESC),
MAX(Values) OVER (PARTITION BY UserId ORDER BY Date DESC)
FROM
(
SELECT UserId, Date, SUM(Value) As Values
FROM <<table_name>>
GROUP BY UserId, Date
)
MAX 대신 FIRST_VALUE을 (를) 사용하고 Explain Plan에서 찾아 볼 수 있습니다. 나는 그것을 가지고 놀 시간이 없었다.
물론 거대한 테이블을 검색하는 경우 쿼리에 FULL 힌트를 사용하는 것이 좋습니다.
이런 식으로 생각합니다. (구문 실수로 나를 용서하십시오.이 시점에서 HQL을 사용하는 데 익숙합니다!)
편집 : 또한 질문을 잘못 읽으십시오! 검색어를 수정했습니다 ...
SELECT UserId, Value
FROM Users AS user
WHERE Date = (
SELECT MAX(Date)
FROM Users AS maxtest
WHERE maxtest.UserId = user.UserId
)
(T-SQL) 먼저 모든 사용자와 최대 날짜를 얻습니다. maxdates에서 사용자에 해당하는 값을 찾으려면 테이블과 결합하십시오.
create table users (userid int , value int , date datetime)
insert into users values (1, 1, '20010101')
insert into users values (1, 2, '20020101')
insert into users values (2, 1, '20010101')
insert into users values (2, 3, '20030101')
select T1.userid, T1.value, T1.date
from users T1,
(select max(date) as maxdate, userid from users group by userid) T2
where T1.userid= T2.userid and T1.date = T2.maxdate
결과 :
userid value date
----------- ----------- --------------------------
2 3 2003-01-01 00:00:00.000
1 2 2002-01-01 00:00:00.000
여기에 대한 답변은 Oracle입니다. 다음은 모든 SQL에서 조금 더 정교한 답변입니다.
전체적으로 가장 좋은 숙제 결과는 누구입니까 (최대 숙제 점수 합계)?
SELECT FIRST, LAST, SUM(POINTS) AS TOTAL
FROM STUDENTS S, RESULTS R
WHERE S.SID = R.SID AND R.CAT = 'H'
GROUP BY S.SID, FIRST, LAST
HAVING SUM(POINTS) >= ALL (SELECT SUM (POINTS)
FROM RESULTS
WHERE CAT = 'H'
GROUP BY SID)
그리고 더 어려운 예는 설명이 필요하며 시간이 부족합니다.
2008 년에 가장 인기있는 책, 즉 2008 년에 가장 많이 빌리는 책 (ISBN 및 제목)을 제공하십시오.
SELECT X.ISBN, X.title, X.loans
FROM (SELECT Book.ISBN, Book.title, count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan
ON Copy.copyId = Loan.copyId
GROUP BY Book.title) X
HAVING loans >= ALL (SELECT count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan
ON Copy.copyId = Loan.copyId
GROUP BY Book.title);
희망이 도움이 되길 바랍니다 .. :)
감사합니다, 거스
나는 파티에 상당히 늦었지만 다음과 같은 핵이 상관 하위 쿼리와 모든 분석 기능을 능가하지만 한 가지 제한이 있습니다. 값은 문자열로 변환해야합니다. 따라서 날짜, 숫자 및 기타 문자열에서 작동합니다. 코드는 잘 보이지 않지만 실행 프로필은 훌륭합니다.
select
userid,
to_number(substr(max(to_char(date,'yyyymmdd') || to_char(value)), 9)) as value,
max(date) as date
from
users
group by
userid
이 코드가 제대로 작동하는 이유는 테이블을 한 번만 스캔하면되기 때문입니다. 인덱스가 필요하지 않으며 가장 중요한 것은 대부분의 분석 기능이 수행하는 테이블을 정렬 할 필요가 없다는 것입니다. 단일 사용자 ID에 대한 결과를 필터링해야하는 경우 색인이 도움이됩니다.
먼저 최상위 답변에 따라 질문을 잘못 읽으십시오. 다음은 올바른 결과를 보여주는 완전한 예입니다.
CREATE TABLE table_name (id int, the_value varchar(2), the_date datetime);
INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'a','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'b','2/2/2002');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'c','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'d','3/3/2003');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'e','3/3/2003');
-
select id, the_value
from table_name u1
where the_date = (select max(the_date)
from table_name u2
where u1.id = u2.id)
-
id the_value
----------- ---------
2 d
2 e
1 b
(3 row(s) affected)
파티션 KEEP, DENSE_RANK 개념이없는 MySQL 용 솔루션.
select userid,
my_date,
...
from
(
select @sno:= case when @pid<>userid then 0
else @sno+1
end as serialnumber,
@pid:=userid,
my_Date,
...
from users order by userid, my_date
) a
where a.serialnumber=0
참조 : http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html
Postgres를 사용하는 경우 다음 array_agg
과 같이 사용할 수 있습니다
SELECT userid,MAX(adate),(array_agg(value ORDER BY adate DESC))[1] as value
FROM YOURTABLE
GROUP BY userid
Oracle에 익숙하지 않습니다. 이것이 내가 생각해 낸 것입니다
SELECT
userid,
MAX(adate),
SUBSTR(
(LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)),
0,
INSTR((LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)), ',')-1
) as value
FROM YOURTABLE
GROUP BY userid
두 쿼리 모두 허용 된 답변과 동일한 결과를 반환합니다. SQLFiddles를 참조하십시오.
(UserID, Date)가 고유 한 경우, 즉 동일한 사용자에 대해 날짜가 두 번 나타나지 않는 경우 :
select TheTable.UserID, TheTable.Value
from TheTable inner join (select UserID, max([Date]) MaxDate
from TheTable
group by UserID) UserMaxDate
on TheTable.UserID = UserMaxDate.UserID
TheTable.[Date] = UserMaxDate.MaxDate;
select UserId,max(Date) over (partition by UserId) value from users;