[mysql] MySQL-행에서 열로

게시물을 검색하려고했지만 SQL Server / Access에 대한 솔루션 만 찾았습니다. MySQL (5.X) 솔루션이 필요합니다.

hostid, itemname, itemvalue라는 3 개의 열이있는 테이블 (히스토리라고 함)이 있습니다.
선택 ( select * from history)을하면 반환됩니다

   +--------+----------+-----------+
   | hostid | itemname | itemvalue |
   +--------+----------+-----------+
   |   1    |    A     |    10     |
   +--------+----------+-----------+
   |   1    |    B     |     3     |
   +--------+----------+-----------+
   |   2    |    A     |     9     |
   +--------+----------+-----------+
   |   2    |    c     |    40     |
   +--------+----------+-----------+

데이터베이스를 쿼리하여 다음과 같은 것을 반환하는 방법

   +--------+------+-----+-----+
   | hostid |   A  |  B  |  C  |
   +--------+------+-----+-----+
   |   1    |  10  |  3  |  0  |
   +--------+------+-----+-----+
   |   2    |   9  |  0  |  40 |
   +--------+------+-----+-----+



답변

이 문제를 해결하기 위해 취해야 할 단계에 대해 좀 더 길고 자세한 설명을 추가하겠습니다. 너무 길면 사과드립니다.


나는 당신이 제공 한 기초부터 시작하여이 포스트의 나머지 부분에 사용할 몇 가지 용어를 정의하는 데 사용할 것입니다. 이것은 기본 테이블입니다 .

select * from history;

+--------+----------+-----------+
| hostid | itemname | itemvalue |
+--------+----------+-----------+
|      1 | A        |        10 |
|      1 | B        |         3 |
|      2 | A        |         9 |
|      2 | C        |        40 |
+--------+----------+-----------+

이것이 우리의 목표, 예쁜 피벗 테이블입니다 .

select * from history_itemvalue_pivot;

+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 |    0 |
|      2 |    9 |    0 |   40 |
+--------+------+------+------+

의 값 history.hostid열이 될 것이다 y 값 피벗 테이블이다. 의 값 history.itemname열이 될 것입니다 x 값 (분명한 이유를).


피벗 테이블을 만드는 문제를 해결해야하는 경우 3 단계 프로세스 (선택적 4 단계)를 사용하여 문제를 해결합니다.

  1. 관심있는 열을 선택하십시오 (예 : y- 값x- 값)
  2. 추가 열을 사용하여 기본 테이블을 확장-각 x 값 마다 하나씩
  3. 확장 테이블 그룹화 및 집계-각 y- 값 에 대해 하나의 그룹
  4. (선택 사항) 집계 된 테이블을 확인

이 단계를 문제에 적용하고 우리가 얻는 것을 보자.

1 단계 : 관심의 선택 열 . 원하는 결과 hostid에서 y 값itemname제공하고 x 을 제공 합니다 .

2 단계 : 여분의 열이 기본 테이블을 확장 . 일반적으로 x- 값당 하나의 열이 필요합니다. x- 값 열은 itemname다음과 같습니다.

create view history_extended as (
  select
    history.*,
    case when itemname = "A" then itemvalue end as A,
    case when itemname = "B" then itemvalue end as B,
    case when itemname = "C" then itemvalue end as C
  from history
);

select * from history_extended;

+--------+----------+-----------+------+------+------+
| hostid | itemname | itemvalue | A    | B    | C    |
+--------+----------+-----------+------+------+------+
|      1 | A        |        10 |   10 | NULL | NULL |
|      1 | B        |         3 | NULL |    3 | NULL |
|      2 | A        |         9 |    9 | NULL | NULL |
|      2 | C        |        40 | NULL | NULL |   40 |
+--------+----------+-----------+------+------+------+

우리는 행 수를 변경하지 않았으며 추가 열을 추가했습니다. 또한 NULLs 의 패턴 – itemname = "A"새 열에 대해서는 널이 아닌 값을 가지며 A다른 새 열에 대해서는 널값을 갖는 행에 유의하십시오 .

3 단계 : 확장 테이블을 그룹화하고 집계합니다 . group by hostidy- 값을 제공하기 때문에을 수행 해야 합니다.

create view history_itemvalue_pivot as (
  select
    hostid,
    sum(A) as A,
    sum(B) as B,
    sum(C) as C
  from history_extended
  group by hostid
);

select * from history_itemvalue_pivot;

+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 | NULL |
|      2 |    9 | NULL |   40 |
+--------+------+------+------+

(y- 값당 하나의 행 이 생겼습니다 .) 이제 거의 다 왔습니다 ! 우리는 그 추악한 것들을 제거해야합니다 NULL.

4 단계 : prettify . 우리는 null 값을 0으로 대체하여 결과 집합을 더 잘 볼 수 있습니다.

create view history_itemvalue_pivot_pretty as (
  select
    hostid,
    coalesce(A, 0) as A,
    coalesce(B, 0) as B,
    coalesce(C, 0) as C
  from history_itemvalue_pivot
);

select * from history_itemvalue_pivot_pretty;

+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 |    0 |
|      2 |    9 |    0 |   40 |
+--------+------+------+------+

그리고 우리는 끝났습니다-우리는 MySQL을 사용하여 멋진 피벗 테이블을 만들었습니다.


이 절차를 적용 할 때 고려해야 할 사항 :

  • 추가 열에 사용할 값 itemvalue이 예제에서 사용 했습니다
  • 추가 열에 사용할 “중립”값 나는 사용 NULL했지만 정확한 상황에 따라 0또는 ""일 수도 있습니다.
  • 그룹화 할 때 사용할 집계 함수 내가 사용 sum하지만, count그리고 max종종 사용된다 (max 여러 행에 걸쳐 확산 있었다 하나의 행 “객체”를 구축 할 때 자주 사용된다)
  • y- 값에 여러 열을 사용합니다. 이 솔루션은 y 값에 단일 열을 사용하는 것으로 제한되지 않습니다-여분의 열을 group by절에 연결하십시오 (그리고 잊지 마십시오 select).

알려진 제한 사항 :

  • 이 솔루션은 피벗 테이블에서 n 개의 열을 허용하지 않습니다. 기본 테이블을 확장 할 때 각 피벗 열을 수동으로 추가해야합니다. 따라서 5 또는 10 x 값의 경우이 솔루션이 좋습니다. 100에 대해서는 그렇게 좋지 않습니다. 쿼리를 생성하는 저장 프로 시저가있는 몇 가지 솔루션이 있지만 추악하고 제대로 이해하기가 어렵습니다. 현재 피벗 테이블에 많은 열이 필요할 때이 문제를 해결하는 좋은 방법을 모릅니다.

답변

SELECT
    hostid,
    sum( if( itemname = 'A', itemvalue, 0 ) ) AS A,
    sum( if( itemname = 'B', itemvalue, 0 ) ) AS B,
    sum( if( itemname = 'C', itemvalue, 0 ) ) AS C
FROM
    bob
GROUP BY
    hostid;


답변

피벗해야 할 항목이 많은 경우 특히 유용한 또 다른 옵션은 mysql이 쿼리를 작성하도록하는 것입니다.

SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'ifnull(SUM(case when itemname = ''',
      itemname,
      ''' then itemvalue end),0) AS `',
      itemname, '`'
    )
  ) INTO @sql
FROM
  history;
SET @sql = CONCAT('SELECT hostid, ', @sql, '
                  FROM history
                   GROUP BY hostid');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

FIDDLE
작동하는지 확인하기 위해 추가 값을 추가했습니다

GROUP_CONCAT 기본값이 1000이므로 쿼리가 너무 큰 경우 실행하기 전에이 매개 변수를 변경하십시오.

SET SESSION group_concat_max_len = 1000000;

테스트:

DROP TABLE IF EXISTS history;
CREATE TABLE history
(hostid INT,
itemname VARCHAR(5),
itemvalue INT);

INSERT INTO history VALUES(1,'A',10),(1,'B',3),(2,'A',9),
(2,'C',40),(2,'D',5),
(3,'A',14),(3,'B',67),(3,'D',8);

  hostid    A     B     C      D
    1     10      3     0      0
    2     9       0    40      5
    3     14     67     0      8


답변

문제를 해결하는 데 도움이 된 Matt Fenwick의 아이디어를 활용하여 (고마워) 하나의 쿼리로만 줄 이겠습니다.

select
    history.*,
    coalesce(sum(case when itemname = "A" then itemvalue end), 0) as A,
    coalesce(sum(case when itemname = "B" then itemvalue end), 0) as B,
    coalesce(sum(case when itemname = "C" then itemvalue end), 0) as C
from history
group by hostid


답변

하위 쿼리에서 Agung Sagita 의 답변을 편집 하여 참여합니다. 이 두 가지 방법의 차이점이 확실하지 않지만 다른 참조 용입니다.

SELECT  hostid, T2.VALUE AS A, T3.VALUE AS B, T4.VALUE AS C
FROM TableTest AS T1
LEFT JOIN TableTest T2 ON T2.hostid=T1.hostid AND T2.ITEMNAME='A'
LEFT JOIN TableTest T3 ON T3.hostid=T1.hostid AND T3.ITEMNAME='B'
LEFT JOIN TableTest T4 ON T4.hostid=T1.hostid AND T4.ITEMNAME='C'


답변

하위 쿼리 사용

SELECT  hostid,
    (SELECT VALUE FROM TableTest WHERE ITEMNAME='A' AND hostid = t1.hostid) AS A,
    (SELECT VALUE FROM TableTest WHERE ITEMNAME='B' AND hostid = t1.hostid) AS B,
    (SELECT VALUE FROM TableTest WHERE ITEMNAME='C' AND hostid = t1.hostid) AS C
FROM TableTest AS T1
GROUP BY hostid

그러나 하위 쿼리가 행보다 많은 결과를 얻는 경우 하위 쿼리에서 추가 집계 함수를 사용하면 문제가됩니다.


답변

내 해결책 :

select h.hostid, sum(ifnull(h.A,0)) as A, sum(ifnull(h.B,0)) as B, sum(ifnull(h.C,0)) as  C from (
select
hostid,
case when itemName = 'A' then itemvalue end as A,
case when itemName = 'B' then itemvalue end as B,
case when itemName = 'C' then itemvalue end as C
  from history
) h group by hostid

제출 된 사례에서 예상 결과를 생성합니다.