計画
セットアップ
create table users
(
id integer primary key not null,
username varchar(23) not null
-- some user data..
);
create table setting_types
(
id integer primary key not null,
name varchar(23) not null
);
create table user_settings
(
id integer primary key not null,
user_id integer not null,
setting_type_id integer not null,
value varchar(13) not null,
foreign key ( user_id ) references users( id ),
foreign key ( setting_type_id ) references setting_types ( id )
);
insert into users
( id, username )
values
( 1, 'Admin' ),
( 2, 'heresjonny' )
;
insert into setting_types
( id, name )
values
( 1, 'setting_type_1' ),
( 2, 'setting_type_2' ),
( 3, 'setting_type_3' ),
( 4, 'setting_type_4' ),
( 5, 'setting_type_5' ),
( 6, 'setting_type_6' ),
( 7, 'setting_type_7' ),
( 8, 'setting_type_8' )
;
insert into user_settings
( id, user_id, setting_type_id, value )
values
( 1, 1, 1, 'true' ),
( 2, 1, 2, 'false' ),
( 3, 1, 3, 'false' ),
( 4, 1, 4, 'false' ),
( 5, 2, 3, 'true' ),
( 6, 2, 4, 'true' ),
( 7, 2, 5, 'false' ),
( 8, 2, 6, 'true' ),
( 9, 2, 7, 'true' ),
( 10, 2, 8, 'true' )
;
ピボット
set @pivot_source = '(
select st.id as setting_id, st.name, users.id as user_id, users.username, coalesce(us.value, ''false'') as value
from setting_types st
cross join
(
select id, username
from users
) users
left join user_settings us
on users.id = us.user_id
and st.id = us.setting_type_id
)';
set @pivot_sql := replace('
select user_id, username,
#setting_aliases#
from
(
select #first_user_dets#,
#settings_fields#
from
#pivot_source# #first_alias#
inner join
#all_joins#
) q
order by user_id
;', '#pivot_source#', @pivot_source);
set @pivot_block := replace('
#pivot_source# #alias#
on #last_alias#.user_id = #alias#.user_id
and #last_alias#.setting_id < #alias#.setting_id
inner join #all_joins#', '#pivot_source#', @pivot_source)
;
select count(*) into @ignore
from
(
select
@pivot_sql := replace(@pivot_sql, '#all_joins#', replace(replace(@pivot_block, '#alias#', concat('sett', right_id)), '#last_alias#', concat('sett', left_id)))
from
(
select `left`.id as left_id, min(`right`.id) as right_id
from setting_types `left`
inner join setting_types `right`
on `left`.id < `right`.id
group by 1
) t
order by left_id
) `ignore`
;
select concat('sett', id) into @first_alias
from setting_types
order by id
limit 1
;
select concat(@first_alias, '.user_id,',@first_alias,'.username') into @first_user_dets;
select group_concat(concat('sett', id, '.value ', name) SEPARATOR ',') into @settings_fields
from setting_types
;
select group_concat(name SEPARATOR ',') into @setting_aliases
from setting_types
;
select count(*) into @ignore
from
(
select
@pivot_sql := replace(@pivot_sql, '#first_user_dets#', @first_user_dets),
@pivot_sql := replace(@pivot_sql, '#settings_fields#', @settings_fields),
@pivot_sql := replace(@pivot_sql, '#setting_aliases#', @setting_aliases),
@pivot_sql := replace(@pivot_sql, '#first_alias#', @first_alias),
@pivot_sql := replace(@pivot_sql, 'inner join #all_joins#', '')
) `ignore`
;
select @pivot_sql;
prepare pivot_sql from @pivot_sql;
EXECUTE pivot_sql;
deallocate prepare pivot_sql;
出力
+---------+------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+
| user_id | username | setting_type_1 | setting_type_2 | setting_type_3 | setting_type_4 | setting_type_5 | setting_type_6 | setting_type_7 | setting_type_8 |
+---------+------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+
| 1 | Admin | true | false | false | false | false | false | false | false |
| 2 | heresjonny | false | false | true | true | false | true | true | true |
+---------+------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+
注
アプリケーションコードでこのピボットを行うのがより一般的です。これを行う理由がパフォーマンスのためである場合は、これをphpでの類似のピボットに対してベンチマークして、実際に大幅に優れているかどうかをテストする必要があります。
動的列を使用したピボット パフォーマンスをベンチマークするためのphpコードの開発に役立ちます