sql >> データベース >  >> RDS >> Mysql

MySQL動的クロス集計クエリ:追加の列として子レコードを選択する

    計画

    セットアップ

    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           |
    +---------+------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+
    

    sqlfiddle

    アプリケーションコードでこのピボットを行うのがより一般的です。これを行う理由がパフォーマンスのためである場合は、これをphpでの類似のピボットに対してベンチマークして、実際に大幅に優れているかどうかをテストする必要があります。

    動的列を使用したピボット パフォーマンスをベンチマークするためのphpコードの開発に役立ちます




    1. 高可用性のためにChamiloMySQLデータベースをデプロイする方法

    2. MySQLはデータベースをインポートしますが、特定のテーブルを無視します

    3. SQLのカーソルとは何ですか?それを実装する方法は?

    4. MySQL ISNULL()の説明