2012-10-08 2 views
0

UNION ALL에 의해 조인 된 두 개의 SELECT 문이 있습니다. 첫 번째 명령문에서 where 절은 이전에 사용자에게 표시된 행만 수집합니다. 두 번째 명령문은 사용자에게 표시되지 않은 모든 행을 수집하므로 결과는 처음에 볼 수 있고 결과는 볼 수 없습니다.두 개의 열 값이 하나의 그룹을 만드는 각 그룹의 행 수 선택

당연히 간단한 ORDER BY을 사용하는 동일한 select 문을 사용하면이 작업을 간단하게 수행 할 수 있습니다. 그러나 내가 수행하기를 바란다는 것을 알게 된 후에는 두 개의 개별 선택에 대한 이유가 간단합니다.

다음 구조와 데이터를 고려하십시오.

+----+------+-----+--------+------+ 
| id | from | to | viewed | data | 
+----+------+-----+--------+------+ 
| 1 | 1 | 10 | true | .... | 
| 2 | 10 | 1 | true | .... | 
| 3 | 1 | 10 | true | .... | 
| 4 | 6 | 8 | true | .... | 
| 5 | 1 | 10 | true | .... | 
| 6 | 10 | 1 | true | .... | 
| 7 | 8 | 6 | true | .... | 
| 8 | 10 | 1 | true | .... | 
| 9 | 6 | 8 | true | .... | 
| 10 | 2 | 3 | true | .... | 
| 11 | 1 | 10 | true | .... | 
| 12 | 8 | 6 | true | .... | 
| 13 | 10 | 1 | false | .... | 
| 14 | 1 | 10 | false | .... | 
| 15 | 6 | 8 | false | .... | 
| 16 | 10 | 1 | false | .... | 
| 17 | 8 | 6 | false | .... | 
| 18 | 3 | 2 | false | .... | 
+----+------+-----+--------+------+ 

은 기본적으로 내가 그이 viewed 열이 매우 단순하고 간단 true 또는 false입니다 날씨를 확인하여 수행됩니다, 이외의 모든 볼 행이 문에 의해 선택 받기를 원하시면, 아무것도 여기 걱정할 필요가 없습니다.

그러나 열이 이미 표시되어있는 경우 (열 viewed is TRUE) 해당 레코드의 경우 각 그룹에 대해 3 행만 반환하면됩니다.

이 인스턴스의 적절한 결과는 각 그룹의 가장 최근 행 3 개이어야합니다.

+----+------+-----+--------+------+ 
| id | from | to | viewed | data | 
+----+------+-----+--------+------+ 
| 6 | 10 | 1 | true | .... | 
| 7 | 8 | 6 | true | .... | 
| 8 | 10 | 1 | true | .... | 
| 9 | 6 | 8 | true | .... | 
| 10 | 2 | 3 | true | .... | 
| 11 | 1 | 10 | true | .... | 
| 12 | 8 | 6 | true | .... | 
+----+------+-----+--------+------+ 

이상적인 결과 집합에서 알 수 있듯이 세 가지 그룹이 있습니다. 따라서 조회 된 결과에 대한 원하는 조회에는 발견 된 각 그룹에 대해 최대 3 개의 행이 표시되어야합니다. 이 경우이 그룹은 10 개가 1이고 8 개가 6인데 둘 다 표시 할 행이 3 개가 있고 다른 그룹 2는 3 개만 표시 할 행이 하나 있습니다.

여기서 from = xto = yfrom = yto = x 인 것처럼 동일한 그룹을 만듭니다. 따라서 첫 번째 그룹화 (10을 1로 함)를 고려하면 과 to = 1from = 1to = 10 인 경우 동일한 그룹입니다.

전체 테이블에는 그룹의 수가 많아서 select 문에 가장 최근에 반환 된 그룹이 3 개만 있고 내 문제가 있습니다. 가장 효율적인 방법으로 어떻게 완료 할 수 있는지 확신하지 못합니다. 가능하다면 테이블에는 수천 가지의 레코드가있을 것입니다.

도움 주셔서 감사합니다.

참고 : 성능에 도움이 idfrom, toviewed이 색인 열.

추신 : 정확하게이 질문의 이름을 지정하는 방법에 대해 확신이 없습니다. 더 좋은 아이디어가 있다면 내 손님이되고 제목을 편집하십시오.

+0

나는 당신의 권리를 얻을 경우 "그룹은"'from'and'의 독특한 조합으로 정의된다 'values ​​{x, y} 값 순서에 관계없이. 당신은 아마도 이것을 처음에 더 분명하게해야합니다. 완전한 대답이 없으면 역순으로 정렬 된 쌍을 집계하는 DISTINCT, GROUP BY 및 UNION이 친구 여야한다고 생각합니다. – matthias

+1

@Matze가 말한 것 이외에도 "가장 최근의"는 "가장 높은 ID 값"을 의미한다고 생각합니다. 옳은? –

+0

@OllieJones : 물론 가능합니다. –

답변

3

무슨 털볼! 가장 최근의 것부터 가장 최근의 것, 가장 최근의 것, 가장 최근의 것에서 세 번째로 이동함에 따라 점점 더 어려워집니다.

우리가 필요로하는 ID 목록을 가져 와서합시다. 그런 다음 ID로 테이블에서 항목을 가져올 수 있습니다.

이것은 비교적 쉽게, 쿼리는 가장 최근의 항목

당신에게 ID를 얻을 수
SELECT id FROM 
    (SELECT max(id) id, fromitem, toitem 
     FROM stuff 
     WHERE viewed = 'true' 
     GROUP BY fromitem, toitem 
    )a 

바이올린 : http://sqlfiddle.com/#!2/f7045/27/0

다음, 우리는 두 번째 가장 최근 항목의 ID를 얻을 필요가있다. 이렇게하려면 자체 조인 스타일 쿼리가 필요합니다. 우리는 똑같은 요약을해야하지만 가장 최근의 항목을 생략 한 가상 테이블에서해야합니다.

select id from (
    select max(b.id) id, b.fromitem, b.toitem 
    from stuff a 
    join 
      (select id, fromitem, toitem 
      from stuff 
      where viewed = 'true' 
      ) b on ( a.fromitem = b.fromitem 
        and a.toitem = b.toitem 
        and b.id < a.id) 
    where a.viewed = 'true' 
    group by fromitem, toitem 
)c 

바이올린 : http://sqlfiddle.com/#!2/f7045/44/0

마지막으로, 우리는 세 번째 가장 최근 항목의 ID를 얻을 필요가있다. 자비! 방금받은 쿼리를 다시 테이블에 결합해야합니다.

select id from 
(
    select max(d.id) id, d.fromitem, d.toitem 
    from stuff d 
    join 
    (
     select max(b.id) id, b.fromitem, b.toitem 
      from stuff a 
      join 
      (
       select id, fromitem, toitem 
       from stuff 
       where viewed = 'true' 
      ) b on ( a.fromitem = b.fromitem 
        and a.toitem = b.toitem 
        and b.id < a.id) 
      where a.viewed = 'true' 
      group by fromitem, toitem 
    ) c on ( d.fromitem = c.fromitem 
      and d.toitem = c.toitem 
      and d.id < c.id) 
    where d.viewed='true' 
    group by d.fromitem, d.toitem 
) e 

바이올린 : http://sqlfiddle.com/#!2/f7045/45/0

그래서, 지금 우리는 모든 식별자의 조합을, 그리고 테이블에서 올바른 행을 잡기 위해 그들을 사용하고, 우리가 완료됩니다.

SELECT * 
    FROM STUFF 
WHERE ID IN 
(

SELECT id FROM 
    (SELECT max(id) id, fromitem, toitem 
     FROM stuff 
     WHERE viewed = 'true' 
     GROUP BY fromitem, toitem 
    )a 
UNION 
select id from (
    select max(b.id) id, b.fromitem, b.toitem 
    from stuff a 
    join 
      (select id, fromitem, toitem 
      from stuff 
      where viewed = 'true' 
      ) b on ( a.fromitem = b.fromitem 
        and a.toitem = b.toitem 
        and b.id < a.id) 
    where a.viewed = 'true' 
    group by fromitem, toitem 
)c 
UNION 
select id from 
(
    select max(d.id) id, d.fromitem, d.toitem 
    from stuff d 
    join 
    (
     select max(b.id) id, b.fromitem, b.toitem 
      from stuff a 
      join 
      (
       select id, fromitem, toitem 
       from stuff 
       where viewed = 'true' 
      ) b on ( a.fromitem = b.fromitem 
        and a.toitem = b.toitem 
        and b.id < a.id) 
      where a.viewed = 'true' 
      group by fromitem, toitem 
    ) c on ( d.fromitem = c.fromitem 
      and d.toitem = c.toitem 
      and d.id < c.id) 
    where d.viewed='true' 
    group by d.fromitem, d.toitem 
) e 
UNION 
select id from stuff where viewed='false' 
) 
order by viewed desc, fromitem, toitem, id desc 

티 희입니다. SQL이 너무 많습니다. Fiddle : http://sqlfiddle.com/#!2/f7045/47/0

이제 우리는 마지막 요구 사항 인 그래프가 정렬되지 않은 요구 사항에 대처해야합니다. 즉, = n에서 = m까지는 = m에서 = n까지와 같습니다.

이렇게하려면 실제 테이블 대신 가상 테이블이 필요합니다. 이것은 트릭을 할 것입니다.

SELECT id, least(fromitem, toitem) fromitem, greatest(fromitem,toitem) toitem, data 
    FROM stuff 

이제이 가상 테이블,이보기를 사용하여 실제 테이블을 표시하는 데 사용해야합니다. 이를 위해보기를 사용합시다.

CREATE VIEW 
AS 
SELECT id, 
     LEAST(fromitem, toitem) fromitem, 
     GREATEST (fromitem, toitem) toitem, 
     viewed, 
     data; 

그래서, 우리의 궁극적 인 쿼리는 다음과 같습니다

SELECT * 
     FROM stuff 
    WHERE ID IN 
    (

    SELECT id FROM 
     (SELECT max(id) id, fromitem, toitem 
      FROM STUFF_UNORDERED 
      WHERE viewed = 'true' 
      GROUP BY fromitem, toitem 
     )a 
    UNION 
    SELECT id FROM (
     SELECT max(b.id) id, b.fromitem, b.toitem 
     FROM STUFF_UNORDERED a 
     JOIN 
       (SELECT id, fromitem, toitem 
       FROM STUFF_UNORDERED 
       WHERE viewed = 'true' 
       ) b ON ( a.fromitem = b.fromitem 
         AND a.toitem = b.toitem 
         AND b.id < a.id) 
     WHERE a.viewed = 'true' 
     GROUP BY fromitem, toitem 
    )c 
    UNION 
    SELECT id FROM 
    (
     SELECT max(d.id) id, d.fromitem, d.toitem 
     FROM STUFF_UNORDERED d 
     JOIN 
     (
      SELECT max(b.id) id, b.fromitem, b.toitem 
       FROM STUFF_UNORDERED a 
       JOIN 
       (
        SELECT id, fromitem, toitem 
        FROM STUFF_UNORDERED 
        WHERE viewed = 'true' 
       ) b ON ( a.fromitem = b.fromitem 
         AND a.toitem = b.toitem 
         AND b.id < a.id) 
       WHERE a.viewed = 'true' 
       GROUP BY fromitem, toitem 
     ) c ON ( d.fromitem = c.fromitem 
       AND d.toitem = c.toitem 
       AND d.id < c.id) 
     WHERE d.viewed='true' 
     GROUP BY d.fromitem, d.toitem 
    ) e 
    UNION 
    SELECT id FROM STUFF_UNORDERED WHERE viewed='false' 
    ) 
    ORDER BY viewed DESC, 
      least(fromitem, toitem), 
      greatest(fromitem, toitem), 
      id DESC 

바이올린 : http://sqlfiddle.com/#!2/8c154/4/0

+1

당신의 열정으로 나는 당신이 시간을내어 그것을 분해하고 그것이 어떻게 작동 하는지를 좋아하는 것보다 더 즐겁게 지낸 기분을 느낍니다. 나는 당신의 접근 방식에 상당히 감명을 받았습니다. 물론 그것을 올바르게 표시 할 것입니다. 저는이 방법에 대해이 아이디어를 얻었습니다. 나는 그것을 직접 시도해보고 작동하는지 확인합니다. 그것은 내가 원하는 마지막 3, 4, 5 또는 몇 개를 얻을 수 있어야합니다. 어쨌든, 당신의 대답은 제가 걱정하는 한 위대합니다. 많은 사람들이 투표를하기를 바랄뿐입니다. 고마워. –

+0

내 접근 방식의 문제점은 네 번째, 다섯 번째 등의 항목에 비해 크게 저조한 점입니다. 오라클'ROWNUM' 기능이나 postgreSQL'RANK' 윈도우 기능으로는 쉽게 접근 할 수있는 또 다른 접근법이 있지만 MySQL은 이러한 종류의 작업에는별로 좋지 않습니다. 비오는 날 오후에 해킹을 할 수있는 기회를 주셔서 감사합니다. –

관련 문제