문제

테이블:

UserId, Value, Date.

각 userID에 대한 userID, max (날짜)의 값을 얻고 싶습니다. 즉, 최신 날짜가있는 각 userID의 값입니다. SQL에서 단순히 이것을 할 수있는 방법이 있습니까? (바람직하게는 오라클)

업데이트: 모든 모호성에 대한 사과 : 모든 userids를 가져와야합니다. 그러나 각 userID에 대해 해당 사용자가 최신 날짜가있는 행 만 가능합니다.

도움이 되었습니까?

해결책

이것은 my_date 열 값이 해당 userID의 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의 기본 창은 파티션의 첫 번째 행에서 현재까지입니다."

윈도우 조항은 순서 별 조항이있을 때만 적용됩니다. 절차에 의한 순서가 없으면 기본적으로 창문 조항이 적용되지 않으며 명시 적으로 지정할 수 없습니다.

코드가 작동합니다.

다른 팁

나는 많은 사람들이 하위 쿼리 또는 벤더 별 기능을 사용하여이를 수행하는 것을 본다. 그러나 나는 종종 다음과 같은 방법으로 이런 종류의 쿼리를한다. 평범한 표준 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 그리고 더 큰 날짜.

(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의 RE Comment :

더 자세한 설명은 다음과 같습니다.

외부 결합 시도 가입 t1 ~와 함께 t2. 기본적으로 모든 결과 t1 반환되고 만약에 일치가 있습니다 t2, 또한 반환됩니다. 일치하지 않는 경우 t2 주어진 행의 경우 t1, 쿼리는 여전히 행을 반환합니다 t1, 그리고 사용 NULL 모든 것을위한 자리 표시 자로서 t2의 열. 바로 외부 결합이 일반적으로 작동하는 방식입니다.

이 쿼리의 속임수는 조인의 일치 조건을 설계하는 것입니다. t2 일치해야합니다 같은 userid, a 보다 큰 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

정확한 열 이름을 모르지만 다음과 같은 것입니다.

    select userid, value
      from users u1
     where date = (select max(date)
                     from users u2
                    where u1.userid = u2.userid)

직장에 있지 않아서 오라클이 없지만 Oracle은 여러 열이 IN 절에 일치 할 수 있다는 것을 기억하는 것 같습니다. 이는 적어도 상관 된 하위 쿼리를 사용하는 옵션을 피해야합니다. 아이디어.

이와 같은 것, 아마도 열 목록이 괄호 안에 있는지 여부를 기억할 수 없음) : :

SELECT * 
FROM MyTable
WHERE (User, Date) IN
  ( SELECT User, MAX(Date) FROM MyTable GROUP BY User)

편집 : 방금 Real을 위해 시도했습니다.

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 userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1

문맥의 경우, Teradata에서 여기 에서이 규모의 적절한 크기 테스트는이 자격 버전으로 17 대, '인라인보기'/Aldridge 솔루션 #1을 사용하여 23대로 실행됩니다.

테스트 할 Oracle이 없지만 가장 효율적인 솔루션은 분석 쿼리를 사용하는 것입니다. 다음과 같이 보일 것입니다.

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. 다음은 짧은 요약입니다.

후드 분석 쿼리 아래 전체 데이터 세트를 정렬 한 다음 순차적으로 처리합니다. 처리 할 때 특정 기준에 따라 데이터 세트를 분할 한 다음 각 행에 대해 일부 창을 살펴 봅니다 (현재 행의 파티션의 첫 번째 값에 대한 기본값 - 기본값도 가장 효율적입니다). 분석 함수 수 (목록은 집계 함수와 매우 유사 함).

이 경우 여기에는 내부 쿼리가하는 일이 있습니다. 전체 데이터 세트는 userID에 의해 정렬 된 다음 DESC를 정렬합니다. 그런 다음 한 번의 패스로 처리합니다. 각 행에 대해 userID를 반환하고 해당 userID의 첫 번째 날짜를 반환합니다 (날짜는 desc 정렬이므로 최대 날짜입니다). 이것은 복제 된 행으로 답변을 제공합니다. 그런 다음 외부 별개의 스쿼시가 복제됩니다.

이것은 특히 분석 쿼리의 장관의 예가 아닙니다. 훨씬 더 큰 승리의 경우 금융 영수증 테이블을 가져 와서 각 사용자 및 영수증을 계산하는 것을 고려하십시오. 분석 쿼리는이를 효율적으로 해결합니다. 다른 솔루션은 덜 효율적입니다. 그렇기 때문에 2003 SQL 표준의 일부입니다. (불행히도 Postgres는 아직 가지고 있지 않습니다. Grrr ...)

~ 안에 Oracle 12c+, 당신이 사용할 수있는 상단 n 분석 기능과 함께 쿼리 rank 이것을 매우 간결하게 달성합니다 없이 하위 쿼리 :

select *
from your_table
order by rank() over (partition by user_id order by my_date desc)
fetch first 1 row with ties;

위는 사용자 당 max 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; 

PostgreSQL 8.4 이상을 사용하면 다음을 사용할 수 있습니다.

select user_id, user_value_1, user_value_2
  from (select user_id, user_value_1, user_value_2, row_number()
          over (partition by user_id order by user_date desc) 
        from users) as r
  where r.row_number=1

사용 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;
Select  
   UserID,  
   Value,  
   Date  
From  
   Table,  
   (  
      Select  
          UserID,  
          Max(Date) as MDate  
      From  
          Table  
      Group by  
          UserID  
    ) as subQuery  
Where  
   Table.UserID = subQuery.UserID and  
   Table.Date = subQuery.mDate  

직장에서 "살아있는"예제를 작성해야했습니다 :)

이것은 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를 사용하여 설명 계획에서 찾을 수 있습니다. 나는 그것을 가지고 놀 시간이 없었습니다.

물론, 거대한 테이블을 검색하는 경우 쿼리에서 전체 힌트를 사용하는 것이 더 좋습니다.

select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))

나는 이런 것 같아요. (구문 실수를 용서하십시오.이 시점에서 HQL을 사용하는 데 익숙합니다!)

편집 : 또한 질문을 잘못 읽으십시오! 쿼리 수정 ...

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)

나는 당신이 Shuold 이이 변형을 이전 쿼리로 만듭니다.

SELECT UserId, Value FROM Users U1 WHERE 
Date = ( SELECT MAX(Date)    FROM Users where UserId = U1.UserId)

(T-SQL) 먼저 모든 사용자와 MaxDate를 얻습니다. 테이블과 결합하여 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)

그리고 시간 ATM이없는 설명이 필요한 더 어려운 예입니다.

2008 년에 가장 인기있는이 책 (ISBN 및 제목)은 2008 년에 가장 자주 빌려줍니다.

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);

이것이 도움이되기를 바랍니다 (누구든지) .. :)

안부, 기스

주어진 userID에 대해 날짜가 고유하다고 가정하면 다음은 TSQL입니다.

SELECT 
    UserTest.UserID, UserTest.Value
FROM UserTest
INNER JOIN
(
    SELECT UserID, MAX(Date) MaxDate
    FROM UserTest
    GROUP BY UserID
) Dates
ON UserTest.UserID = Dates.UserID
AND UserTest.Date = Dates.MaxDate 

나는 파티에 꽤 늦었지만 다음 해킹은 상관 된 하위 쿼리와 모든 분석 기능을 모두 능가하지만 하나의 제한 사항이 있습니다. 값은 문자열로 변환해야합니다. 날짜, 숫자 및 기타 문자열에 대해 작동합니다. 코드는 좋아 보이지 않지만 실행 프로파일은 훌륭합니다.

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

이 코드가 잘 작동하는 이유는 테이블을 한 번만 스캔하면됩니다. 인덱스가 필요하지 않으며 가장 중요한 것은 대부분의 분석 기능이하는 테이블을 정렬 할 필요가 없습니다. 단일 userID의 결과를 필터링 해야하는 경우 색인이 도움이됩니다.

select userid, value, date
  from thetable t1 ,
       ( select t2.userid, max(t2.date) date2 
           from thetable t2 
          group by t2.userid ) t3
 where t3.userid t1.userid and
       t3.date2 = t1.date

IMHO 이것은 작동합니다. HTH

이것이 작동해야한다고 생각합니까?

Select
T1.UserId,
(Select Top 1 T2.Value From Table T2 Where T2.UserId = T1.UserId Order By Date Desc) As 'Value'
From
Table T1
Group By
T1.UserId
Order By
T1.UserId

첫 번째 시도는 질문을 잘못 읽었습니다. 최고 답변에 따라 올바른 결과가있는 완전한 예입니다.

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)

또한 복제물을 처리합니다 (각 사용자 _id에 대해 하나의 행을 반환) :

SELECT *
FROM (
  SELECT u.*, FIRST_VALUE(u.rowid) OVER(PARTITION BY u.user_id ORDER BY u.date DESC) AS last_rowid
  FROM users u
) u2
WHERE u2.rowid = u2.last_rowid

방금 테스트했는데 벌목 테이블에서 작동하는 것 같습니다.

select ColumnNames, max(DateColumn) from log  group by ColumnNames order by 1 desc

이것은 단순해야합니다.

SELECT UserId, Value
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)

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를 참조하십시오 :

  1. 받아 들여진 답변
  2. Postgres와 함께 내 솔루션
  3. Oracle과 함께 내 솔루션

if (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;

파티션 개념이없는 MySQL 용 솔루션, Dense_Rank.

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 hith-have-maxmin-value-in.html

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top