MySQL将键/值对转换为列

时间:2018-02-07 07:25:22

标签: mysql

我们正在尝试构建用于Tableau的数据库的非规范化版本。我们面临的挑战之一是在我们的一个表中处理我们的键/值对。请参阅以下某些数据的简化版本。

Asset Table
Asset ID  Site ID   Make          Model
1         1         Toyota        Corolla
2         2         Honda         Civic
3         2         Suzuki        Swift

Asset Property Types Table
Site ID  Asset Property Name    Asset Property Type
1        Odometer               Numeric
1        Registration           Text
1        Expiry Date            Date
2        Odometer               Numeric
2        Registration           Text
2        Expiry Date            Date
2        Colour                 Text


Asset Properties Table
Asset ID  Key            Text Value   Numeric Value    Date Value
1         Odometer                    1234  
1         Registration   ABC123  
1         Expiry Date                                  2018-02-08  
2         Odometer                    1255  
2         Registration   ABC124
2         Colour         Red 
2         Expiry Date                                  2018-01-08  
3         Registration   ABC125
3         Odometer                    1266
3         Colour         Blue
3         Expiry Date                                  2018-03-25  

有一点需要注意。这是数据的简化版本。并非所有资产都具有相同的键/值对。网站将包含他们想要存储的各种数据类型,这些数据类型可能与其他网站不同。

最终,这是我们想要尝试和实现的目标:

Site 1 Asset Table
Asset ID   Make        Model    Odometer     Registration   Expiry Date
1          Toyota      Corolla  1234         ABC123         2018-02-08

Site 2 Asset Table
Asset ID   Make        Model    Odometer     Registration   Expiry Date   Colour
2          Honda       Civic    1255         ABC124         2018-01-08    Red
3          Suzuki      Swift    1266         ABC125         2018-03-25    Blue

我对此的一般处理如下:

  1. 仅使用Make&创建网站特定资产表建模并填充它们。
  2. 使用循环来替换为每个存在的资产属性添加列的表
  3. 使用相应列中的资产属性数据填充“网站特定资产”表
  4. 我希望通过MySQL过程完成所有这些操作,以便我可以安排它每小时自动运行并替换各种站点级别表。 CASE语句不起作用,因为这需要是动态的。

    我非常感谢有关如何实现这一目标的任何建议/帮助。虽然我对SQL很满意,但程序远非我的深度。

2 个答案:

答案 0 :(得分:1)

我想难以理解的是构建动态案例陈述。因此,给出您的数据(通过一些更改来删除空格和可能的关键字冲突)

create table Asset(Asset_ID int, Site_ID int,  Make varchar(20), Model varchar(20));
insert into asset values
(1  ,       1  ,       'Toyota' ,       'Corolla'),
(2  ,       2  ,       'Honda'  ,       'Civic'),
(3  ,       2  ,       'Suzuki' ,       'Swift');

drop table if exists Asset_Property_Types;
create table Asset_Property_Types (Site_ID int, Asset_Property_Name varchar(100),   Asset_Property_Type varchar(100));
insert into asset_property_types values
(1   ,    'Odometer'       ,        'Numeric_value'),
(1   ,    'Registration'   ,        'Text_value'),
(1   ,    'Expiry_Date'    ,        'Date_value'),
(2   ,    'Odometer'       ,        'Numeric_value'),
(2   ,    'Registration'   ,        'Text_value'),
(2   ,    'Expiry_Date'    ,         'Date_value'),
(2   ,    'Colour'         ,         'Text_value');


create table Asset_Properties(Asset_ID  int , asset_property_name varchar(30),Text_Value varchar(100),   Numeric_Value int ,   Date_Value date);
insert into asset_properties values
(1    ,     'Odometer'      , null      ,      1234  ,null),
(1    ,     'Registration'  , 'ABC123'  ,null        ,null),
(1    ,     'Expiry_Date'   , null      ,        null, '2018-02-08'),  
(2    ,     'Odometer'      , null      ,      1255  ,null),
(2    ,     'Registration'  , 'ABC124'  ,null        ,null),
(2    ,     'Colour'        , 'Red'     ,null        ,null),
(2    ,     'Expiry_Date'   , null      , null       ,'2018-01-08'),  
(3    ,     'Registration'  , 'ABC125'  ,null        ,null),
(3    ,     'Odometer'      , null      ,        1266,null),
(3    ,     'Colour'        , 'Blue'    ,null         ,null),
(3    ,     'Expiry_Date'   , null      , null        ,'2018-03-25');  

此代码

    set @sql = (select concat('select a.asset_id ,a.site_id,a.make,a.model,',
             group_concat(
            concat('max(case when asset_property_name  = ' ,char(39),asset_property_name,char(39), ' then ' ,asset_property_type ,' else null end) as ', asset_property_name)
            )
            ,' from asset a join asset_properties ap on ap.asset_id = a.asset_id group by a.site_id,a.asset_id;'
            )
    from 
    (select distinct asset_property_name,asset_property_type from asset_property_types) a
    )
    ;

构建此sql语句

select a.asset_id ,a.site_id,a.make,a.model,
        max(case when asset_property_name  = 'Odometer' then Numeric_value else null end) as Odometer,
        max(case when asset_property_name  = 'Registration' then Text_value else null end) as Registration,
        max(case when asset_property_name  = 'Expiry_Date' then Date_value else null end) as Expiry_Date,
        max(case when asset_property_name  = 'Colour' then Text_value else null end) as Colour 
from asset a join asset_properties ap on ap.asset_id = a.asset_id 
group by a.site_id,a.asset_id;

然后您可以将其提交给sql,如此

prepare sqlstmt from @sql;
execute sqlstmt;
deallocate prepare sqlstmt;

答案 1 :(得分:0)

您的问题类似于数据透视表问题。您可以谷歌搜索以找到解决它的更多变体。解决这个问题的一种方法是

A)获取列名称

`Select distinct key from assetPropertiesTable;`

B)使用动态SQL构建一个Select查询字符串,对于上一个查询中的每个键k,它都有一个使用子选择的列,如

(select textvalue from assetPropertiesTable t where t.id = outerselect.id and key = k) as column_k

C)执行完整查询并从存储过程返回它或执行create table xyz as /*select query here*/。 无需分两步创建和填充表格。

有更多区别且更高效的方法,例如加入assetPropertiesTable一次,并对要添加的每列的布尔条件key=k进行求和