[질문:MySQL] 사용자 로그 테이블에서, 각 사용자별 최신 로그만 1줄씩, 단 한 번의 쿼리로 가져오려면?

아래와 같은 DB 테이블이 있다. id_person과 id_class를 연결하는 relation 테이블이다. 반이 변하는 것을 기록해 누적한다.

보면 알 수 있겠지만, 1번 id_person은 2반이었다가 4반이었다가 1반이 됐다.

2번 id_person은 3반이었다가 1반이 됐다.

id id_person id_class date_written
53 1 1 2012-04-05 16:40
50 1 4 2012-04-05 16:02
1 1 2 2012-03-30 17:56
2 2 3 2012-03-30 17:56
51 2 1 2012-04-05 16:36
3 3 4 2012-03-30 17:56
52 3 1 2012-04-04 16:37
4 4 5 2012-03-30 17:56
5 5 6 2012-03-30 17:56
6 6 7 2012-03-30 17:56
7 7 1 2012-03-30 17:56
8 8 2 2012-03-30 17:56
9 9 3 2012-03-30 17:56

자, 그런데 지금 뽑으려는 데이터는 각 id_person 이 지금 몇 반인가 하는 거다.

서브쿼리로 하면 되지만…

이 테이블만을 바탕으로 그걸 뽑을 수 있을까? 있었다. 아래처럼 쿼리를 사용하는 거다.

SELECT p.*
FROM `person_class_relation_table` AS p
WHERE date_written=(SELECT MAX(date_written) FROM person_class_relation_table WHERE id_person = p.id_person)

이렇게 서브쿼리를 사용하면 구할 수 있다. 그런데 목록이 많아지면 심각한 성능 저하 현상이 벌어질 거다.

질문 – 서브쿼리를 사용하지 않고 구할 수 있는 방법은?

서브쿼리를 사용하지 않고 구할 수 있는 방법은 뭐가 있을까?

내가 시도한 방법은 아래 쿼리다.

SELECT *, MAX(date_written)
FROM person_class_relation_table
GROUP BY id_person

이렇게 하니까 친절하게 id_person으로 그룹이 지어져 나왔고, date_written 항목은 다른 데이터들과 아무런 상관없이 그냥 최고값만 나왔다. 즉, id_person 1번은 1반이어야 하는데 2반이라고 나왔다. 그런데 date_written은 최신값. OTL;; 역시 GROUP BY는 id_person을 기준으로 그룹만 지어 주는 놈이었던 것이야…

다음으로는 이렇게 해 봤는데 작동하지 않았다.

SELECT *
FROM person_class_relation_table
GROUP BY id_person
WHERE date_written = MAX(date_written)

4번 줄이 오류 표시됐다. 저렇게 못 쓴다는 에러가 나온 거다 그냥.

여튼간에, 그래서 고수분들께 질문드린다. 내가 MySQL에 정말 취약하다는 것을 새삼 느끼면서 질문한다.

이 경우 대체 어떻게 해야 할까?! (같은 내용을 PHP School에 질문했다.)

PHPSchool에서 풀잎 님이 해 준 답변

서브쿼리 없이 구하는 방법은 없을 것 같습니다.
차라리 상태필드를 하나 더 입력하여
insert시 새로운 데이타는 상태 필드 = 1 로 입력하고,
동일 id_person이 존재하면 과거 데이타의 상태필드 = 0으로  update 시키는 것이 바람직해 보입니다.
그럼 굳이 group by를 하지 않더라도 where 상태필드 = 1 만으로도 원하는 효과를 얻을 수 있으리라 봅니다.

물론 select가 더 빈번하게 일어나고 update?는 자주 일어나지 않는다는 조건하에서 이야기입니다.

이렇게 하는 게 나을 것 같다.

아래는 table 생성 쿼리다. 참고하면 된다.

--
-- 테이블 구조 `person_class_relation_table`
--

CREATE TABLE IF NOT EXISTS `person_class_relation_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `id_person` int(11) NOT NULL COMMENT 'id_person',
  `id_class` int(11) NOT NULL COMMENT 'id_class',
  `date_written` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '입력일시',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=54 ;

--
-- 테이블의 덤프 데이터 `person_class_relation_table`
--

INSERT INTO `person_class_relation_table` (`id`, `id_person`, `id_class`, `date_written`) VALUES
(1, 1, 2, '2012-03-30 17:56:58'),
(2, 2, 3, '2012-03-30 17:56:58'),
(3, 3, 4, '2012-03-30 17:56:58'),
(4, 4, 5, '2012-03-30 17:56:58'),
(5, 5, 6, '2012-03-30 17:56:58'),
(6, 6, 7, '2012-03-30 17:56:58'),
(7, 7, 1, '2012-03-30 17:56:58'),
(8, 8, 2, '2012-03-30 17:56:58'),
(9, 9, 3, '2012-03-30 17:56:58'),
(10, 10, 4, '2012-03-30 17:56:58'),
(11, 11, 5, '2012-03-30 17:56:58'),
(12, 12, 6, '2012-03-30 17:56:58'),
(13, 13, 7, '2012-03-30 17:56:58'),
(14, 14, 1, '2012-03-30 17:56:58'),
(15, 15, 2, '2012-03-30 17:56:58'),
(16, 16, 3, '2012-03-30 17:56:58'),
(17, 17, 4, '2012-03-30 17:56:58'),
(18, 18, 5, '2012-03-30 17:56:58'),
(19, 19, 6, '2012-03-30 17:56:58'),
(20, 20, 7, '2012-03-30 17:56:58'),
(21, 21, 1, '2012-03-30 17:56:58'),
(22, 22, 2, '2012-03-30 17:56:58'),
(23, 23, 3, '2012-03-30 17:56:58'),
(24, 24, 4, '2012-03-30 17:56:58'),
(25, 25, 5, '2012-03-30 17:56:58'),
(26, 26, 6, '2012-03-30 17:56:58'),
(27, 27, 7, '2012-03-30 17:56:58'),
(28, 28, 1, '2012-03-30 17:56:58'),
(29, 29, 2, '2012-03-30 17:56:58'),
(30, 30, 3, '2012-03-30 17:56:58'),
(31, 31, 4, '2012-03-30 17:56:58'),
(32, 32, 5, '2012-03-30 17:56:58'),
(33, 33, 6, '2012-03-30 17:56:58'),
(34, 34, 7, '2012-03-30 17:56:58'),
(35, 35, 1, '2012-03-30 17:56:58'),
(36, 36, 2, '2012-03-30 17:56:58'),
(37, 37, 3, '2012-03-30 17:56:58'),
(38, 38, 4, '2012-03-30 17:56:58'),
(39, 39, 5, '2012-03-30 17:56:58'),
(40, 40, 6, '2012-03-30 17:56:58'),
(41, 41, 7, '2012-03-30 17:56:58'),
(42, 42, 1, '2012-03-30 17:56:58'),
(43, 43, 2, '2012-03-30 17:56:58'),
(44, 44, 3, '2012-03-30 17:56:58'),
(45, 45, 4, '2012-03-30 17:56:58'),
(46, 46, 5, '2012-03-30 17:56:58'),
(47, 47, 6, '2012-03-30 17:56:58'),
(48, 48, 7, '2012-03-30 17:56:58'),
(49, 49, 1, '2012-03-30 17:56:58'),
(50, 1, 4, '2012-04-05 16:02:07'),
(51, 2, 1, '2012-04-05 16:36:49'),
(52, 3, 1, '2012-04-04 16:37:09'),
(53, 1, 1, '2012-04-05 16:40:25');

 

카테고리 글 목록 👉

대표글

“[질문:MySQL] 사용자 로그 테이블에서, 각 사용자별 최신 로그만 1줄씩, 단 한 번의 쿼리로 가져오려면?”에 대한 2개의 응답

  1. 저도 이거 때문에 고생 꽤나 해서 그냥 지나칠 수가 없네요.

    select distinct(id_person) as id_person, max(id_class) as id_class,
    max(date_written) as date_written from person_class_relation_table group by id_person;

    이렇게 하시면 원하는 결과가 나올겁니다.
    서브쿼리를 쓰는 것 보다 10배 이상 빠르고 테이블 서너개 조인해도
    부하 안걸리고 잘 나옵니다.

    1. 와우 완전 감사한데요, 나중에 테스트해 보고 결과 알려드릴 게요! ^^

댓글 남기기