사용자 당 가장 최근 날짜가있는 행 선택


125

다음과 같은 사용자의 체크인 및 체크 아웃 시간 테이블 ( "lms_attendance")이 있습니다.

id  user    time    io (enum)
1   9   1370931202  out
2   9   1370931664  out
3   6   1370932128  out
4   12  1370932128  out
5   12  1370933037  in

"in"또는 "out"값을 제공하면서 사용자 ID 당 가장 최근 레코드 만 출력하는이 테이블의보기를 만들려고합니다.

id  user    time    io
2   9   1370931664  out
3   6   1370932128  out
5   12  1370933037  in

지금까지는 꽤 가깝지만 뷰가 하위 쿼리를 허용하지 않아 훨씬 더 어려워진다는 것을 깨달았습니다. 내가 얻은 가장 가까운 쿼리는 다음과 같습니다.

select 
    `lms_attendance`.`id` AS `id`,
    `lms_attendance`.`user` AS `user`,
    max(`lms_attendance`.`time`) AS `time`,
    `lms_attendance`.`io` AS `io` 
from `lms_attendance` 
group by 
    `lms_attendance`.`user`, 
    `lms_attendance`.`io`

그러나 내가 얻는 것은 :

id  user    time    io
3   6   1370932128  out
1   9   1370931664  out
5   12  1370933037  in
4   12  1370932128  out

가깝지만 완벽하지는 않습니다. 마지막 그룹이 거기에 있으면 안된다는 것을 알고 있지만 그것이 없으면 가장 최근 시간을 반환하지만 상대 IO 값은 반환하지 않습니다.

어떤 아이디어? 감사!



설명서로 돌아가십시오. 하위 쿼리가 있거나없는 (상관 및 비 연관)이 문제에 대한 솔루션을 제공한다는 것을 알 수 있습니다.
Strawberry

@Barmar, 기술적으로 내 대답에서 지적했듯이 이것은 그룹당 최대 태그가 포함 된 700 개의 질문을 모두 복제 한 것입니다 .
TMS

@Prodikl, 'io (열거 형)'는 무엇입니까?
Monica Heddneck

"in 또는 out"을 나타내는 "IO"라는 열이 있는데, "in"또는 "out"값이 가능한 열거 형 유형이었습니다. 이것은 사람들이 수업에 체크인 및 퇴실 한시기를 추적하는 데 사용되었습니다.
Keith

답변:


199

질문:

SQLFIDDLE 예

SELECT t1.*
FROM lms_attendance t1
WHERE t1.time = (SELECT MAX(t2.time)
                 FROM lms_attendance t2
                 WHERE t2.user = t1.user)

결과:

| ID | USER |       TIME |  IO |
--------------------------------
|  2 |    9 | 1370931664 | out |
|  3 |    6 | 1370932128 | out |
|  5 |   12 | 1370933037 |  in |

매번 작동하는 솔루션 :

SQLFIDDLE 예

SELECT t1.*
FROM lms_attendance t1
WHERE t1.id = (SELECT t2.id
                 FROM lms_attendance t2
                 WHERE t2.user = t1.user            
                 ORDER BY t2.id DESC
                 LIMIT 1)

2
와! 이 작업을 수행했을뿐만 아니라 하위 쿼리가 포함되어 있어도이 쿼리로 뷰를 만들 수있었습니다. 이전에는 하위 쿼리를 포함하는 뷰를 만들려고 할 때 허용되지 않았습니다. 이것이 허용되는 이유에 대한 규칙이 있지만 다른 규칙은 허용되지 않습니까?
키스

엄청 이상해. 정말 감사합니다! 내 하위 쿼리가 FROM을 선택한 의사 테이블이기 때문일 수 있습니다.이 예에서는 WHERE 절에 사용되었습니다.
키스

4
하위 쿼리가 필요 없습니다! 또한이 솔루션 은 정확히 같은 시간을 가진 두 개의 레코드가있는 경우 작동하지 않습니다 . 이것은 일반적인 문제이기 때문에 매번 바퀴를 재발 명 할 필요가 없습니다. 대신 이미 테스트되고 최적화 된 솔루션을 찾으십시오. @Prodikl은 내 대답을 참조하십시오.
TMS

아, 통찰력 주셔서 감사합니다! 내일 사무실에있을 때 새 코드를 사용해 보겠습니다.
키스

3
@TMS이 솔루션은 쿼리가 ID가 가장 큰 레코드를 찾기 때문에 레코드의 시간이 정확히 같은 경우 작동합니다. 이것은 테이블의 시간이 삽입 시간이라는 것을 의미하며 이는 좋은 가정이 아닐 수 있습니다. 대신 솔루션이 타임 스탬프를 비교하고 두 타임 스탬프가 동일하면 ID가 가장 큰 행도 반환합니다. 따라서 솔루션은이 테이블의 타임 스탬프가 삽입 순서와 관련이 있다고 가정합니다. 이는 두 쿼리 모두에서 가장 큰 결함입니다.
WebWanderer

73

이것은 그룹당 가장 큰 문제 이기 때문에 바퀴를 재발 명 할 필요가 없습니다 . 아주 좋은 해결책이 제시 됩니다.

하위 쿼리가없는 가장 단순한 솔루션 ( SQLFiddle, 업데이트 된 Justin 's 참조)을 선호합니다 (따라서 뷰에서 사용하기 쉽습니다).

SELECT t1.*
FROM lms_attendance AS t1
LEFT OUTER JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND (t1.time < t2.time 
         OR (t1.time = t2.time AND t1.Id < t2.Id))
WHERE t2.user IS NULL

이것은 또한 동일한 그룹 내에서 동일한 가장 큰 값을 가진 두 개의 다른 레코드가있는 경우에도 작동합니다 (t1.time = t2.time AND t1.Id < t2.Id). 내가 여기서하는 일은 동일한 사용자의 두 레코드가 같은 시간을 가질 때 하나만 선택되도록하는 것입니다. 기준이 Id다른 것인지 여부는 실제로 중요하지 않습니다. 기본적으로 고유하다고 보장되는 모든 기준이 여기서 작동합니다.


1
최대 사용 t1.time < t2.time과 최소는 t1.time > t2.time내 초기 직감과 반대입니다.
없음

1
숨겨진 암시 적 부정이 있기 때문에 @ J.Money : 조건이 적용되는 t2의 해당 레코드 가없는 t1의 모든 레코드를 선택합니다. t1.time < t2.time:-)
TMS

4
WHERE t2.user IS NULL조금 이상합니다. 이 라인은 어떤 역할을합니까?
tumultous_rooster 2015

1
Justin이 게시 한 수락 된 답변이 더 최적 일 수 있습니다. 허용되는 대답은 테이블의 기본 키에 대한 역방향 인덱스 스캔, 한계, 테이블의 시퀀스 스캔을 사용합니다. 따라서 허용되는 답변은 추가 색인으로 크게 최적화 될 수 있습니다. 이 쿼리는 두 개의 시퀀스 스캔을 수행하기 때문에 인덱스에 의해 최적화 될 수도 있지만 시퀀스 스캔 결과의 해시와 "해시 안티 조인"과 다른 시퀀스 스캔의 해시도 포함됩니다. 어떤 접근 방식이 진정으로 더 최적인지에 대한 설명에 관심이 있습니다.
WebWanderer

@TMS OR (t1.time = t2.time AND t1.Id < t2.Id))섹션을 명확히 해주 시겠습니까?
Oleg Kuts 2016

6

@TMS 답변을 기반으로 하위 쿼리가 필요하지 않기 때문에 좋아하지만 'OR'부분을 생략하면 이해하고 읽기가 훨씬 간단하고 충분할 것이라고 생각합니다 .

SELECT t1.*
FROM lms_attendance AS t1
LEFT JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND t1.time < t2.time
WHERE t2.user IS NULL

null 시간이있는 행에 관심이없는 경우 WHERE절 에서 필터링 할 수 있습니다 .

SELECT t1.*
FROM lms_attendance AS t1
LEFT JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND t1.time < t2.time
WHERE t2.user IS NULL and t1.time IS NOT NULL

OR두 레코드가 동일 할 수 있다면 부분을 생략하는 것은 정말 나쁜 생각 time입니다.
TMS

성능을 위해이 솔루션을 피할 것입니다. @OlegKuts가 언급했듯이 이것은 중대형 데이터 세트에서 매우 느려집니다.
Peter Meadley

4

이미 해결되었지만 기록을 위해 또 다른 접근 방식은 두 개의 뷰를 만드는 것입니다.

CREATE TABLE lms_attendance
(id int, user int, time int, io varchar(3));

CREATE VIEW latest_all AS
SELECT la.user, max(la.time) time
FROM lms_attendance la 
GROUP BY la.user;

CREATE VIEW latest_io AS
SELECT la.* 
FROM lms_attendance la
JOIN latest_all lall 
    ON lall.user = la.user
    AND lall.time = la.time;

INSERT INTO lms_attendance 
VALUES
(1, 9, 1370931202, 'out'),
(2, 9, 1370931664, 'out'),
(3, 6, 1370932128, 'out'),
(4, 12, 1370932128, 'out'),
(5, 12, 1370933037, 'in');

SELECT * FROM latest_io;

SQL Fiddle에서 실제 작동을 보려면 여기를 클릭하십시오.


1
후속 조치에 감사드립니다! 예, 더 쉬운 방법이 없다면 여러 뷰를 만들려고했습니다. 다시 한번 감사
키스

0
select b.* from 

    (select 
        `lms_attendance`.`user` AS `user`,
        max(`lms_attendance`.`time`) AS `time`
    from `lms_attendance` 
    group by 
        `lms_attendance`.`user`) a

join

    (select * 
    from `lms_attendance` ) b

on a.user = b.user
and a.time = b.time

감사. 하위 쿼리를 사용하여 할 수 있다는 것을 알고 있지만 이것을보기로 바꾸고 싶었고 AFAIK보기에서 하위 쿼리를 허용하지 않습니다. 각 하위 쿼리를 뷰 등으로 바꿔야합니까?
키스

join (select * from lms_attendance ) b= join lms_attendance b
azerafati 2016

0
 select result from (
     select vorsteuerid as result, count(*) as anzahl from kreditorenrechnung where kundeid = 7148
     group by vorsteuerid
 ) a order by anzahl desc limit 0,1

0

MySQL 8.0 이상에서는 Window 함수를 사용할 수 있습니다 .

질문:

DBFiddleExample

SELECT DISTINCT
FIRST_VALUE(ID) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS ID,
FIRST_VALUE(USER) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS USER,
FIRST_VALUE(TIME) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS TIME,
FIRST_VALUE(IO) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS IO
FROM lms_attendance;

결과:

| ID | USER |       TIME |  IO |
--------------------------------
|  2 |    9 | 1370931664 | out |
|  3 |    6 | 1370932128 | out |
|  5 |   12 | 1370933037 |  in |

Justin제안한 솔루션 을 사용하는 보다 내가 보는 이점 은 중간 뷰나 테이블 없이도 하위 쿼리에서도 사용자 별 (또는 ID 별 또는 기타별로) 최신 데이터가있는 행을 선택할 수 있다는 것입니다.

그리고 HANA를 실행하는 경우에도 ~ 7 배 더 빠릅니다 : D


-1

좋아, 이것은 해킹이거나 오류가 발생하기 쉬울 수 있지만 어떻게 든 잘 작동합니다.

SELECT id, MAX(user) as user, MAX(time) as time, MAX(io) as io FROM lms_attendance GROUP BY id;

-2

이 쿼리를 시도하십시오.

  select id,user, max(time), io 
  FROM lms_attendance group by user;

이것의 SQLFiddle을 만들어보십시오. idio에서 사용할 수없는 집계되지 않은 열을 찾을 수 있습니다 group by.
Dewi Morgan

1
id가 max (time) 인 id가 될 것이라는 보장은 없으며 그룹 내의 모든 id 일 수 있습니다. 이 문제 난 아직도 찾고 해결하기 위해 여기 온 것입니다
robisrob

-3

아마도 사용자별로 그룹화 한 다음 시간별로 주문할 수 있습니다. 아래와 같이

  SELECT * FROM lms_attendance group by user order by time desc;

-3

이것은 나를 위해 일했습니다.

SELECT user, time FROM 
(
    SELECT user, time FROM lms_attendance --where clause
) AS T 
WHERE (SELECT COUNT(0) FROM table WHERE user = T.user AND time > T.time) = 0
ORDER BY user ASC, time DESC
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.