MySQLデータベースに2つのテーブルがあるとします。
表1:
ID Name
1 Jim
2 Bob
3 John
表2:
ID key value
1 address "X Street"
1 city "NY"
1 region "NY"
1 country "USA"
1 postal_code ""
1 phone "123456789"
データベースから行を選択するときに、2番目のテーブルの行を列として最初のテーブルに結合する方法はありますか?
MySQLクエリからの望ましい結果は次のとおりです。
ID Name address city region country postal_code phone
1 Jim X Street NY NY USA NULL 123456789
2 Bob NULL NULL NULL NULL NULL NULL
3 John NULL NULL NULL NULL NULL NULL
助けてくれてありがとう!
このタイプのデータ変換は、PIVOTとして知られています。 MySQLにはピボット関数がありませんが、CASE
式を使用した集計関数を使用して複製できます。
select t1.id,
t1.name,
max(case when t2.`key` = 'address' then t2.value end) address,
max(case when t2.`key` = 'city' then t2.value end) city,
max(case when t2.`key` = 'region' then t2.value end) region,
max(case when t2.`key` = 'country' then t2.value end) country,
max(case when t2.`key` = 'postal_code' then t2.value end) postal_code,
max(case when t2.`key` = 'phone' then t2.value end) phone
from table1 t1
left join table2 t2
on t1.id = t2.id
group by t1.id, t1.name
SQL Fiddle with Demo を参照してください。
これは、table2
で複数の結合を使用して記述することもでき、各key
の結合にフィルターを含めることができます。
select t1.id,
t1.name,
t2a.value address,
t2c.value city,
t2r.value region,
t2y.value country,
t2pc.value postal_code,
t2p.value phone
from table1 t1
left join table2 t2a
on t1.id = t2a.id
and t2a.`key` = 'address'
left join table2 t2c
on t1.id = t2c.id
and t2c.`key` = 'city'
left join table2 t2r
on t1.id = t2r.id
and t2c.`key` = 'region'
left join table2 t2y
on t1.id = t2y.id
and t2c.`key` = 'country'
left join table2 t2pc
on t1.id = t2pc.id
and t2pc.`key` = 'postal_code'
left join table2 t2p
on t1.id = t2p.id
and t2p.`key` = 'phone';
SQL Fiddle with Demo を参照してください。
上記の2つのバージョンは、key
値の数が限られている場合にうまく機能します。値の数が不明な場合は、プリペアドステートメントを使用して動的SQLを生成することを検討してください。
SET @sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when t2.`key` = ''',
`key`,
''' then t2.value end) AS `',
`key`, '`'
)
) INTO @sql
from Table2;
SET @sql
= CONCAT('SELECT t1.id, t1.name, ', @sql, '
from table1 t1
left join table2 t2
on t1.id = t2.id
group by t1.id, t1.name;');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SQL Fiddle with Demo を参照してください
すべてのバージョンで結果が得られます。
| ID | NAME | ADDRESS | CITY | REGION | COUNTRY | POSTAL_CODE | PHONE |
|----|------|----------|--------|--------|---------|-------------|-----------|
| 1 | Jim | X Street | NY | (null) | (null) | (null) | 123456789 |
| 2 | Bob | (null) | (null) | (null) | (null) | (null) | (null) |
| 3 | John | (null) | (null) | (null) | (null) | (null) | (null) |
2番目のテーブルにはentity-attribute-valueという構造があります。組み合わせを行うには2つの方法があります。集計方法の方が表現しやすいと思います。
select t1.name,
max(case when `key` = 'address' then value end) as address,
max(case when `key` = 'city' then value end) as city,
max(case when `key` = 'region' then value end) as region,
max(case when `key` = 'country' then value end) as country,
max(case when `key` = 'postal_code' then value end) as postal_code,
max(case when `key` = 'phone' then value end) as phone
from table1 t1 left join
table2 t2
on t1.id = t2.id
group by t1.name;
2番目の方法は、値ごとに個別の結合を行うことです。
select t1.name, address.value, city.value, . . .
from table1 t1 left join
table2 address
on t1.id = address.id and address.`key` = 'Address' left join
table2 city
on t1.id = city.id and city.`key` = 'City' . . .
データの構造にもよりますが、MySQLでは適切なインデックスを使用するとjoin
メソッドの方が実際に高速になる可能性があります。 (他のデータベースは集約のアルゴリズムであるため、group by
メソッドは、他のデータベースでもうまく機能することがよくあります。)