[mysql] 그룹 별 MySQL 주문

여기에 비슷한 질문이 많이 있지만 질문에 적절하게 대답한다고 생각하지 않습니다.

나는 현재 가장 인기있는 질문 에서 계속하고 괜찮다면 예제를 사용합니다.

이 인스턴스의 작업은 데이터베이스의 각 작성자에 대한 최신 게시물을 얻는 것입니다.

예제 쿼리는 항상 최신 게시물이 반환되는 것은 아니므로 사용할 수없는 결과를 생성합니다.

SELECT wp_posts.* FROM wp_posts
    WHERE wp_posts.post_status='publish'
    AND wp_posts.post_type='post'
    GROUP BY wp_posts.post_author
    ORDER BY wp_posts.post_date DESC

현재 허용되는 답변은

SELECT
    wp_posts.*
FROM wp_posts
WHERE
    wp_posts.post_status='publish'
    AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
HAVING wp_posts.post_date = MAX(wp_posts.post_date) <- ONLY THE LAST POST FOR EACH AUTHOR
ORDER BY wp_posts.post_date DESC

불행히도이 대답은 명백하고 단순하지 않으며 많은 경우 원래 쿼리보다 덜 안정적인 결과를 생성합니다.

최선의 해결책은 양식의 하위 쿼리를 사용하는 것입니다

SELECT wp_posts.* FROM
(
    SELECT *
    FROM wp_posts
    ORDER BY wp_posts.post_date DESC
) AS wp_posts
WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author 

내 질문은 간단한 것입니다 :
하위 쿼리에 의지하지 않고 그룹화하기 전에 행을 주문해야합니까?

편집 :이 질문은 다른 질문에서 계속되었으며 내 상황의 세부 사항이 약간 다릅니다. 특정 게시물의 고유 식별자 인 wp_posts.id도 있다고 가정 할 수 있습니다.



답변

ORDER BY하위 쿼리에서 in을 사용하는 것이이 문제에 대한 최상의 솔루션은 아닙니다.

max(post_date)작성자가 얻을 수있는 가장 좋은 해결책 은 하위 쿼리를 사용하여 최대 날짜를 반환 한 다음 최대 날짜와 최대 날짜 모두에 테이블에 조인하는 것 post_author입니다.

해결책은 다음과 같아야합니다.

SELECT p1.*
FROM wp_posts p1
INNER JOIN
(
    SELECT max(post_date) MaxPostDate, post_author
    FROM wp_posts
    WHERE post_status='publish'
       AND post_type='post'
    GROUP BY post_author
) p2
  ON p1.post_author = p2.post_author
  AND p1.post_date = p2.MaxPostDate
WHERE p1.post_status='publish'
  AND p1.post_type='post'
order by p1.post_date desc

다음 샘플 데이터가있는 경우 :

CREATE TABLE wp_posts
    (`id` int, `title` varchar(6), `post_date` datetime, `post_author` varchar(3))
;

INSERT INTO wp_posts
    (`id`, `title`, `post_date`, `post_author`)
VALUES
    (1, 'Title1', '2013-01-01 00:00:00', 'Jim'),
    (2, 'Title2', '2013-02-01 00:00:00', 'Jim')
;

하위 쿼리는 최대 날짜와 작성자를 반환합니다.

MaxPostDate | Author
2/1/2013    | Jim

그런 다음 테이블에 다시 결합하기 때문에 두 값 모두 해당 게시물의 전체 세부 정보를 반환합니다.

Demo with SQL Fiddle을 참조하십시오 .

하위 쿼리를 사용 하여이 데이터를 정확하게 반환하는 것에 대한 의견을 확장합니다.

MySQL은 목록에 GROUP BY포함 시키는 모든 열로 강제하지 않습니다 SELECT. 결과적으로 GROUP BY하나의 열만 총 10 개의 열을 반환 하면 해당 열에 속하는 다른 열 값 post_author이 반환된다는 보장이 없습니다 . 열이 GROUP BYMySQL에 없으면 어떤 값을 반환할지 선택합니다.

집계 함수와 함께 하위 쿼리를 사용하면 매번 올바른 작성자 및 게시물이 반환됩니다.

참고로 MySQL에서는 ORDER BY하위 쿼리에서 in 을 사용할 수 GROUP BY있으며 SELECT목록의 모든 열에 적용 할 수 는 없지만 SQL Server를 포함한 다른 데이터베이스에서는이 동작이 허용되지 않습니다.


답변

솔루션은 일부 필드 (이 경우에는 ) 별로 그룹화 할 수있는 GROUP BY 절의 확장을 사용합니다 post_author.

GROUP BY wp_posts.post_author

집계되지 않은 열을 선택하십시오.

SELECT wp_posts.*

group by 절에 나열되지 않거나 집계 함수 (MIN, MAX, COUNT 등)에 사용되지 않는

GROUP BY 절에 대한 확장의 올바른 사용법

이는 집계되지 않은 열의 모든 값이 모든 행에 대해 동일한 경우에 유용합니다.

예를 들어, 테이블이 있다고 가정 GardensFlowers( name정원의 flower정원에서 성장) :

INSERT INTO GardensFlowers VALUES
('Central Park',       'Magnolia'),
('Hyde Park',          'Tulip'),
('Gardens By The Bay', 'Peony'),
('Gardens By The Bay', 'Cherry Blossom');

여러 꽃이 자라는 정원에서 자라는 모든 꽃을 추출하려고합니다. 그런 다음 하위 쿼리를 사용해야합니다. 예를 들어 다음을 사용할 수 있습니다.

SELECT GardensFlowers.*
FROM   GardensFlowers
WHERE  name IN (SELECT   name
                FROM     GardensFlowers
                GROUP BY name
                HAVING   COUNT(DISTINCT flower)>1);

가드에서 유일한 꽃인 모든 꽃을 추출해야하는 경우 HAVING 조건을로 변경하면 HAVING COUNT(DISTINCT flower)=1되지만 MySql에서는 다음을 사용할 수도 있습니다.

SELECT   GardensFlowers.*
FROM     GardensFlowers
GROUP BY name
HAVING   COUNT(DISTINCT flower)=1;

하위 쿼리는없고 표준 SQL은 아니지만 더 단순합니다.

GROUP BY 절의 확장을 잘못 사용함

그러나 모든 행에 대해 동일하지 않은 집계되지 않은 열을 선택하면 어떻게됩니까? MySql이 해당 열에 대해 선택하는 값은 무엇입니까?

MySql은 항상 FIRST 값을 선택하는 것처럼 보입니다 .

첫 번째 값이 원하는 값인지 정확히 확인하려면 GROUP BY정렬 된 쿼리에 a 를 적용 해야하므로 하위 쿼리를 사용해야합니다. 그렇지 않으면 할 수 없습니다.

MySql이 항상 첫 번째 행을 선택한다고 가정하면 GROUP BY 전에 행을 올바르게 정렬합니다. 그러나 불행히도 설명서를주의 깊게 읽으면이 가정이 사실이 아님을 알 수 있습니다.

항상 같지 않은 집계되지 않은 열을 선택할 때 MySql은 값을 자유롭게 선택할 수 있으므로 실제로 표시되는 결과 값은 미정 입니다.

집계되지 않은 열의 첫 번째 값을 얻는이 트릭은 많이 사용되며 일반적으로 / 거의 항상 작동하므로 때로는 내 자신의 위험으로 사용합니다. 그러나 문서화되어 있지 않으므로이 동작에 의존 할 수 없습니다.

이 링크 (ypercube 덕분에!) GROUP BY 트릭이 최적화되었습니다 . 동일한 쿼리가 MySql과 MariaDB간에 다른 최적화 엔진으로 인해 다른 결과를 반환하는 상황을 보여줍니다.

따라서이 방법이 효과가 있으면 운이 좋을뿐입니다.

다른 문제에 허용 대답은 나에게 잘못 같습니다 :

HAVING wp_posts.post_date = MAX(wp_posts.post_date)

wp_posts.post_date은 집계되지 않은 열이며 그 값은 공식적으로 결정되지 않지만 처음 post_date발견 될 수 있습니다 . 그러나 GROUP BY 트릭이 정렬되지 않은 테이블에 적용되기 때문에 어느 것이 첫 번째인지 확실하지 않습니다 post_date.

아마도 단일 저자의 유일한 게시물 인 게시물을 반환 할 것이지만 항상 확실하지는 않습니다.

가능한 해결책

이것이 가능한 해결책이라고 생각합니다.

SELECT wp_posts.*
FROM   wp_posts
WHERE  id IN (
  SELECT max(id)
  FROM wp_posts
  WHERE (post_author, post_date) = (
    SELECT   post_author, max(post_date)
    FROM     wp_posts
    WHERE    wp_posts.post_status='publish'
             AND wp_posts.post_type='post'
    GROUP BY post_author
  ) AND wp_posts.post_status='publish'
    AND wp_posts.post_type='post'
  GROUP BY post_author
)

내부 쿼리에서 모든 저자의 최대 게시 날짜를 반환합니다. 그런 다음 동일한 저자가 이론적으로 동시에 두 개의 게시물을 가질 수 있다는 사실을 고려하고 있으므로 최대 ID 만 얻습니다. 그런 다음 최대 ID를 가진 모든 행을 반환합니다. IN 절 대신 조인을 사용하여 더 빠르게 만들 수 있습니다.

(확실히 ID증가하고 있다고 확신 하면 ID1 > ID2을 의미하는 경우 post_date1 > post_date2쿼리를 훨씬 간단하게 만들 수 있지만 이것이 사실인지 확실하지 않습니다).


답변

당신이 읽을 내용은 다소 해킹이므로 집에서 시도하지 마십시오!

SQL에서 일반적으로 귀하의 질문에 대한 답변은 NO 이지만 GROUP BY( @bluefeet에 의해 언급 된 ) 완화 모드로 인해 MySQL 의 대답은 입니다.

(post_status, post_type, post_author, post_date)에 BTREE 인덱스가 있다고 가정하십시오. 후드 아래의 인덱스는 어떻게 보입니까?

(post_status = ‘publish’, post_type = ‘post’, post_author = ‘user A’, post_date = ‘2012-12-01’) (post_status = ‘publish’, post_type = ‘post’, post_author = ‘user A’, post_date = ‘2012-12-31’) (post_status = ‘publish’, post_type = ‘post’, post_author = ‘user B’, post_date = ‘2012-10-01’) (post_status = ‘publish’, post_type = ‘ post ‘, post_author =’사용자 B ‘, post_date =’2012-12-01 ‘)

즉, 데이터는 모든 필드를 기준으로 오름차순으로 정렬됩니다.

GROUP BY기본적으로 작업을 수행 하면 그룹화 필드 ( post_author, 우리의 경우 post_status, post_type이 WHERE절에 필요함)별로 데이터를 정렬하고 일치하는 인덱스가 있으면 첫 번째 레코드마다 오름차순으로 데이터를 가져옵니다. 즉, 쿼리는 다음을 가져옵니다 (각 사용자의 첫 번째 게시물).

(post_status = ‘publish’, post_type = ‘post’, post_author = ‘user A’, post_date = ‘2012-12-01’) (post_status = ‘publish’, post_type = ‘post’, post_author = ‘user B’, post_date = ‘2012-10-01’)

그러나 GROUP BYMySQL에서는 순서를 명시 적으로 지정할 수 있습니다. 그리고 post_user내림차순으로 요청하면 색인이 반대 순서로 진행되어 실제로 마지막 인 각 그룹의 첫 번째 레코드를 계속 사용합니다.

그건

...
WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author DESC

우리에게 줄 것이다

(post_status = ‘publish’, post_type = ‘post’, post_author = ‘user B’, post_date = ‘2012-12-01’) (post_status = ‘publish’, post_type = ‘post’, post_author = ‘user A’, post_date = ‘2012-12-31’)

이제 post_date로 그룹화 결과를 주문하면 원하는 데이터를 얻을 수 있습니다.

SELECT wp_posts.*
FROM wp_posts
WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author DESC
ORDER BY wp_posts.post_date DESC;

NB :

이것은이 특정 쿼리에 권장하지 않습니다. 이 경우 @bluefeet가 제안한 약간 수정 된 버전을 사용 합니다. 그러나이 기술은 매우 유용 할 수 있습니다. 내 대답을 여기에서보십시오 : 각 그룹의 마지막 레코드 검색

함정 : 접근 방식의 단점은

  • 쿼리 결과는 SQL의 정신에 위배되는 인덱스에 따라 다릅니다 (인덱스는 쿼리 속도를 높여야합니다).
  • 인덱스는 쿼리에 미치는 영향에 대해 아무것도 알지 못합니다 (나중에 누군가 다른 사람이 인덱스를 너무 많이 소비하고 인덱스를 변경하여 성능뿐만 아니라 쿼리 결과를 깨뜨릴 수 있음)
  • 쿼리의 작동 방식을 이해하지 못하는 경우 한 달 안에 설명을 잊어 버릴 수 있으며 쿼리가 사용자와 동료를 혼란스럽게 할 것입니다.

장점은 어려운 경우의 성능입니다. 이 경우 정렬과 관련된 데이터 양 때문에 모든 쿼리가 @bluefeet의 쿼리와 동일해야합니다 (모든 데이터는 임시 테이블에로드 된 후 정렬됩니다. btw, 쿼리에는 (post_status, post_type, post_author, post_date)인덱스도 필요함 ). .

내가 제안하는 것 :

내가 말했듯이, 이러한 쿼리는 MySQL 낭비 시간을 잠재적으로 대량의 데이터를 임시 테이블에서 정렬하게합니다. 페이징이 필요한 경우 (즉, LIMIT 관련) 대부분의 데이터가 폐기됩니다. 내가 할 일은 정렬 된 데이터의 양을 최소화하는 것입니다. 즉, 하위 쿼리에서 최소 데이터를 정렬하고 제한 한 다음 전체 테이블로 다시 조인합니다.

SELECT *
FROM wp_posts
INNER JOIN
(
  SELECT max(post_date) post_date, post_author
  FROM wp_posts
  WHERE post_status='publish' AND post_type='post'
  GROUP BY post_author
  ORDER BY post_date DESC
  -- LIMIT GOES HERE
) p2 USING (post_author, post_date)
WHERE post_status='publish' AND post_type='post';

위에서 설명한 접근 방식을 사용하는 동일한 쿼리 :

SELECT *
FROM (
  SELECT post_id
  FROM wp_posts
  WHERE post_status='publish' AND post_type='post'
  GROUP BY post_author DESC
  ORDER BY post_date DESC
  -- LIMIT GOES HERE
) as ids
JOIN wp_posts USING (post_id);

에 자신의 실행 계획과 그 모든 쿼리 SQLFiddle .


답변

이거 한번 해봐. 각 작성자로부터 최신 게시물 날짜 목록을 얻으십시오 . 그게 다야

SELECT wp_posts.* FROM wp_posts WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post' AND wp_posts.post_date IN(SELECT MAX(wp_posts.post_date) FROM wp_posts GROUP BY wp_posts.post_author) 


답변

아니요. 그룹화가 결과 세트를 변경하므로 그룹화하기 전에 레코드를 정렬하는 것은 의미가 없습니다. 하위 쿼리 방식이 선호되는 방식입니다. 이 작업이 너무 느리면 각 작성자의 마지막 게시물 ID를 별도의 테이블에 저장하거나 각 작성자에게 자신의 게시물 중 마지막 게시물을 나타내는 부울 열을 표시하여 테이블 디자인을 변경해야합니다. 하나.


답변

최대 기능과 그룹 기능을 사용하십시오.

    select max(taskhistory.id) as id from taskhistory
            group by taskhistory.taskid
            order by taskhistory.datum desc


답변

요약하자면 표준 솔루션은 상관되지 않은 하위 쿼리를 사용하며 다음과 같습니다.

SELECT x.*
  FROM my_table x
  JOIN (SELECT grouping_criteria,MAX(ranking_criterion) max_n FROM my_table GROUP BY grouping_criteria) y
    ON y.grouping_criteria = x.grouping_criteria
   AND y.max_n = x.ranking_criterion;

고대 버전의 MySQL 또는 상당히 작은 데이터 세트를 사용하는 경우 다음 방법을 사용할 수 있습니다.

SELECT x.*
  FROM my_table x
  LEFT
  JOIN my_table y
    ON y.joining_criteria = x.joining_criteria
   AND y.ranking_criteria < x.ranking_criteria
 WHERE y.some_non_null_column IS NULL;