Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Consulta de tabela cruzada dinâmica do MySQL:selecionando registros filho como colunas adicionais


plano

configuração
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' )
;

pivô
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;

saída
+---------+------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+
| 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           |
+---------+------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+

sqlfiddle

observação

é mais comum fazer isso pivotando no código do aplicativo. se o seu motivo para fazer isso é para desempenho, deve comparar isso com a pivotagem análoga em php para testar se é realmente significativamente melhor ..

pode encontrar minha resposta anterior em girando com colunas dinâmicas útil para desenvolver seu código php para desempenho de benchmarking