Mysql縱表轉(zhuǎn)換為橫表的方法及優(yōu)化教程
1、縱表與橫表
縱表:表中字段與字段的值采用key—value形式,即表中定義兩個(gè)字段,其中一個(gè)字段里存放的是字段名稱,另一個(gè)字段中存放的是這個(gè)字段名稱代表的字段的值。
例如,下面這張ats_item_record表,其中field_code表示字段,后面的record_value表示這個(gè)字段的值
優(yōu)缺點(diǎn):
橫表:表結(jié)構(gòu)更加的清晰明了,關(guān)聯(lián)查詢的一些sql語(yǔ)句也更容易,方便易于后續(xù)開(kāi)發(fā)人員的接手,但是如果字段不夠,需要新增字段,會(huì)改動(dòng)表結(jié)構(gòu)。
縱表:擴(kuò)展性更高,如果要增加一個(gè)字段,不需要改變表結(jié)構(gòu),但是一些關(guān)聯(lián)查詢會(huì)更加麻煩,也不便于維護(hù)與后續(xù)人員接手。
平常開(kāi)發(fā),盡量能用橫表就不要用縱表,維護(hù)成本比較高昂,而且一些關(guān)聯(lián)查詢也很麻煩。
2、縱表轉(zhuǎn)換為橫表
(1)第一步,我們先把這些字段名以及相應(yīng)字段的值從縱表中取出來(lái)
select r.original_record_id,r.did,r.device_sn,r.mac_address,r.record_time, r.updated_time updated_time, (case r.field_code when 'accumulated_cooking_time' then r.record_value else '' end ) accumulated_cooking_time, (case r.field_code when 'data_version' then r.record_value else '' end) data_version, (case r.field_code when 'loop_num' then r.record_value else '' end) loop_num, (case r.field_code when 'status' then r.record_value else '' end) status from ats_item_record r where item_code = 'GONGMO_AGING'
結(jié)果:
通過(guò) case 語(yǔ)句,成功把字段從縱表中取出,但是此時(shí)仍算不上一個(gè)橫表,我們這里的original_record_id 是記錄同一行數(shù)據(jù)的唯一ID,我們這里可以通過(guò)這個(gè)字段把上面這四行合成一行記錄。
注意:這里需要取出每一個(gè)字段,都要case一下,有多少個(gè)字段,就需要多少次case語(yǔ)句。因?yàn)橐粋€(gè)case語(yǔ)句,遇到符合條件的when語(yǔ)句之后,后面的會(huì)不再執(zhí)行。
(2)分組,合并相同行,生成橫表
select * from ( select r.original_record_id, max(r.did) did, max(r.device_sn) device_sn, max(r.mac_address) mac_address, max(r.record_time) record_time, max(r.updated_time) updated_time, max((case r.field_code when 'accumulated_cooking_time' then r.record_value else '' end )) accumulated_cooking_time, max((case r.field_code when 'data_version' then r.record_value else '' end)) data_version, max((case r.field_code when 'loop_num' then r.record_value else '' end)) loop_num, max((case r.field_code when 'status' then r.record_value else '' end)) status from ats_item_record r where item_code = 'GONGMO_AGING' group by r.original_record_id ) m order by m.updated_time desc;
查詢的結(jié)果:
注意:這里采用group by 分組的時(shí)候,需要給字段加上max函數(shù)。用group by 分組的時(shí)候,一般搭配聚合函數(shù)使用,常見(jiàn)的聚合函數(shù):
- AVG() 求平均數(shù)
- COUNT() 求列的總數(shù)
- MAX() 求最大值
- MIN() 求最小值
- SUM() 求和
大家注意一下,我把縱表同一條記錄的公共字段 r.original_record_id 放到了group by里面,這個(gè)字段在縱表中同一條記錄相同、唯一,且永遠(yuǎn)不會(huì)改變(相當(dāng)于以前橫表的主鍵ID),然后把其他字段放到 max 中(因?yàn)槠渌侄我词窍嗤?,要么是取最大的就可以,要么是只有一個(gè)縱表記錄有數(shù)值其他記錄為空,所以這三種情況都可以直接用max),四條記錄取最大的更新時(shí)間作為同一條記錄的更新時(shí)間,在邏輯上也是合適的。然后我們把縱表字段 field_code 和 record_value 做了 max() 操作,因?yàn)橥粭l記錄里面他們都是唯一存在的,不會(huì)發(fā)生同一條數(shù)據(jù)有兩個(gè)相同的 field_code 記錄,所以這樣做 max() 也是沒(méi)有任何問(wèn)題的。
優(yōu)化點(diǎn):
最后這個(gè)SQL是可以優(yōu)化一下的,我們可以把模板字段(r.original_record_id,r.did,r.device_sn,r.mac_address,r.record_time 等),從專門(mén)存放模板字段表中全部取出來(lái)(同一個(gè)邏輯縱表的字段全部取出),然后再代碼里面拼接好我們的 max() 部分,作為參數(shù)拼接進(jìn)去執(zhí)行,這樣可以做到通用,每次如果新增加模板字段,我們不需要更改這個(gè)SQL語(yǔ)句了(中國(guó)移動(dòng)他們存放手機(jī)的參數(shù)數(shù)據(jù)就是這么干的)。
優(yōu)化后的業(yè)務(wù)層(組裝 SQL 模板的代碼),代碼如下:
@Override public PageInfo<AtsAgingItemRecordVo> getAgingItemList(AtsItemRecordQo qo) { //1、獲取工模老化字段模板 LambdaQueryWrapper<AtsItemFieldPo> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(AtsItemFieldPo::getItemCode, AtsItemCodeConstant.GONGMO_AGING.getCode()); List<AtsItemFieldPo> fieldPoList = atsItemFieldDao.selectList(queryWrapper); //2、組裝查詢條件 List<String> tplList = Lists.newArrayList(), conditionList = Lists.newArrayList(), validList = Lists.newArrayList(); if (!CollectionUtils.isEmpty(fieldPoList)) { //3、組裝動(dòng)態(tài)max查詢字段 for (AtsItemFieldPo itemFieldPo : fieldPoList) { tplList.add("max((case r.field_code when '" + itemFieldPo.getFieldCode() + "' then r.record_value else '' end )) " + itemFieldPo.getFieldCode()); validList.add(itemFieldPo.getFieldCode()); } qo.setTplList(tplList); //4、組裝動(dòng)態(tài)where查詢條件 if (StringUtils.isNotBlank(qo.getDid())) { conditionList.add("AND did like CONCAT('%'," + qo.getDid() + ",'%')"); } if (validList.contains("batch_code") && StringUtils.isNotBlank(qo.getBatchCode())) { conditionList.add("AND batch_code like CONCAT('%'," + qo.getBatchCode() + ",'%')"); } qo.setConditionList(conditionList); } qo.setItemCode(AtsItemCodeConstant.GONGMO_AGING.getCode()); //4、獲取老化自動(dòng)化測(cè)試項(xiàng)記錄 PageHelper.startPage(qo.getPageNo(), qo.getPageSize()); List<Map<String, Object>> dataList = atsItemRecordDao.selectItemRecordListByCondition(qo); PageInfo pageInfo = new PageInfo(dataList); //5、組裝返回結(jié)果 List<AtsAgingItemRecordVo> recordVoList = null; if (!CollectionUtils.isEmpty(dataList)) { recordVoList = JSONUtils.copy(dataList, AtsAgingItemRecordVo.class); } pageInfo.setList(recordVoList); return pageInfo; }
優(yōu)化后的Dao層,代碼如下:
public interface AtsItemRecordDao extends BaseMapper<AtsItemRecordPo> { List<Map<String, Object>> selectItemRecordListByCondition(AtsItemRecordQo qo); }
優(yōu)化后的SQL語(yǔ)句,代碼如下:
<select id="selectItemRecordListByCondition" resultType="java.util.HashMap" parameterType="com.galanz.iot.ops.restapi.model.qo.AtsItemRecordQo"> SELECT * FROM ( SELECT r.original_record_id id, max(r.did) did, max(r.device_sn) device_sn, max(r.updated_time) updated_time, max(r.record_time) record_time, <if test="tplList != null and tplList.size() > 0"> <foreach collection="tplList" item="tpl" index="index" separator=","> ${tpl} </foreach> </if> FROM ats_item_record r WHERE item_code = #{itemCode} GROUP BY r.original_record_id ) m <where> <if test="conditionList != null and conditionList.size() > 0"> <foreach collection="conditionList" item="condition" index="index"> ${condition} </foreach> </if> </where> ORDER BY m.updated_time DESC </select>
模板字段表結(jié)構(gòu)(ats_item_field 表),如下所示:
字段名 | 類型 | 長(zhǎng)度 | 注釋 |
id | bigint | 20 | 主鍵ID |
field_code | varchar | 32 | 字段編碼 |
field_name | varchar | 32 | 字段名稱 |
remark | varchar | 512 | 備注 |
created_by | bigint | 20 | 創(chuàng)建人ID |
created_time | datetime | 0 | 創(chuàng)建時(shí)間 |
updated_by | bigint | 20 | 更新人ID |
updated_time | datetime | 0 | 更新時(shí)間 |
記錄表結(jié)構(gòu)(ats_item_record 表),如下所示:
字段名 | 類型 | 長(zhǎng)度 | 注釋 |
id | bigint | 20 | 主鍵ID |
did | varchar | 64 | 設(shè)備唯一ID |
device_sn | varchar | 32 | 設(shè)備sn |
mac_address | varchar | 32 | 設(shè)備Mac地址 |
field_code | varchar | 32 | 字段編碼 |
original_record_id | varchar | 64 | 原始記錄ID |
record_value | varchar | 32 | 記錄值 |
created_by | bigint | 20 | 創(chuàng)建人ID |
created_time | datetime | 0 | 創(chuàng)建時(shí)間 |
updated_by | bigint | 20 | 更新人ID |
updated_time | datetime | 0 | 更新時(shí)間 |
注:original_record_id 是縱轉(zhuǎn)橫表后,每條記錄的唯一ID,可以看做我們普通橫表的主鍵ID一樣的東西
到此 Mysql 縱表轉(zhuǎn)換為橫表介紹完成。
總結(jié)
到此這篇關(guān)于Mysql縱表轉(zhuǎn)換為橫表的文章就介紹到這了,更多相關(guān)Mysql縱表轉(zhuǎn)換為橫表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- mysql 行列動(dòng)態(tài)轉(zhuǎn)換的實(shí)現(xiàn)(列聯(lián)表,交叉表)
- MySQL橫縱表相互轉(zhuǎn)化操作實(shí)現(xiàn)方法
- MySQL普通表如何轉(zhuǎn)換成分區(qū)表
- MYSQL表中某字段所有值大小寫(xiě)轉(zhuǎn)換
- MySQL普通表轉(zhuǎn)換為分區(qū)表實(shí)戰(zhàn)指南
- MySQL表轉(zhuǎn)移數(shù)據(jù)的三種方式小結(jié)
- Mysql分區(qū)表和普通表互轉(zhuǎn)的實(shí)現(xiàn)示例
- mysql使用mysql.help_topic表實(shí)現(xiàn)一行轉(zhuǎn)多行的實(shí)現(xiàn)示例
相關(guān)文章
mysql 松散的索引掃描(Loose index scan)
今天讀《High Performance MySQL》,發(fā)現(xiàn)一個(gè)“Loose index scan”,之前完全沒(méi)有聽(tīng)說(shuō)過(guò)。網(wǎng)上查了些資料,這個(gè)叫松散的索引掃描(Loose index scan)2016-05-05十個(gè)節(jié)省時(shí)間的MySQL命令小結(jié)
編者在工作中積累起來(lái)了一些MySQL命令行客戶端技巧,這些技巧或多或少會(huì)幫助您節(jié)省大量的時(shí)間。2011-03-03簡(jiǎn)單談?wù)凪ySQL的loose index scan
各種資料關(guān)于loose index scan的解釋很拗口,其實(shí)等同于oracle的index skip scan。今天我們就來(lái)詳細(xì)看看loose index scan的使用2015-12-12SQL重復(fù)記錄查詢 查詢多個(gè)字段、多表查詢、刪除重復(fù)記錄的方法
下面小編就為大家?guī)?lái)一篇SQL重復(fù)記錄查詢 查詢多個(gè)字段、多表查詢、刪除重復(fù)記錄的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09mysql使用報(bào)錯(cuò)1142(42000)的問(wèn)題及解決
這篇文章主要介紹了mysql使用報(bào)錯(cuò)1142(42000)的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08