为了获取客户购买的设备通道,需要编写一个视图,视图用到的数据表共7个,表及其关联关系如下表所示。
实体 | 名称 | 缩写 | 实体关系 | 关联关系 |
CUST | 客户 | C | ||
PU | 前端设备 | P | ||
CAPABILITY | 资源服务目录(业务能力) | CAP | ||
VIDEO_IN_CHANNEL | 视频输入通道 | V | 1、P | 1、V.PU_SEQUENCE_ID = P.PU_SEQUENCE_ID |
BUYCHANNEL | 客户购买通道 | BC | 1、C | 1、BC.CUST_ID = C.CUST_ID |
CORP_CUST_CAPABILITY | 企业客户业务能力 | CCC | 1、C | 1、CCC.CUST_ID = C.CUST_ID |
CAPABILITY_ATTR | 业务能力属性 | CA | 1、CCC | 1、CA.CUST_CAPABILITY_ID = CCC.CUST_CAPABILITY_ID |
在上表关联关系的指引下,很快搞定了视图,代码如下:
CREATE OR REPLACE VIEW QUERRY_DEVICEPORT_VIEW AS
SELECT C.CUST_CODE,
C.CUST_NAME,
BC.PU_EQUIP_CODE,
BC.OPERRIGHT,
P.DEVICE_NAME,
COALESCE(P.STATE, 113) AS STATE,
BC.VIDEO_IN_PORT,
V.VIDEO_IN_NAME,
COALESCE(C.ONLINE_STATE, '0') AS ONLINE_STATE,
C.EXTEND_PASSWORD_FLAG,
_gb2312'5' AS ORDER_STATE,
CA.ATTR_VALUE
FROM CUST C,
BUYCHANNEL BC,
PU P,
VIDEOINCHANNEL V,
CORP_CUST_CAPABILITY CCC,
CAPABILITY CAP,
CAPABILITY_ATTR CA
WHERE C.CUST_TYPE = '0'
AND C.STATE IN ('0', '1')
AND C.PARENT_ID IS NULL
AND C.CUST_CODE IS NOT NULL
AND BC.CUST_ID = C.CUST_ID
AND P.DEVICE_TYPE <> '04'
AND P.PU_SEQUENCE_ID = V.PU_SEQUENCE_ID
AND BC.PU_EQUIP_CODE = P.PU_EQUIP_CODE
AND BC.VIDEO_IN_PORT = V.VIDEO_IN_PORT
AND CAP.CAPABILITY_SEQUENCE = '5'
AND C.CUST_ID = CCC.CUST_ID
AND CCC.CUST_CAPABILITY_ID = CA.CUST_CAPABILITY_ID
AND CCC.CAPABILITY_ID = CAP.CAPABILITY_ID
UNION ALL
SELECT C.CUST_CODE,
C.CUST_NAME,
BC.PU_EQUIP_CODE,
BC.OPERRIGHT,
P.DEVICE_NAME,
COALESCE(P.STATE, 113) AS STATE,
BC.VIDEO_IN_PORT,
V.VIDEO_IN_NAME,
COALESCE(C.ONLINE_STATE, '0') AS ONLINE_STATE,
C.EXTEND_PASSWORD_FLAG,
_gb2312'5' AS ORDER_STATE,
CA.ATTR_VALUE
FROM CUST C,
CUST PC,
BUYCHANNEL BC,
PU P,
VIDEOINCHANNEL V,
CORP_CUST_CAPABILITY CCC,
CAPABILITY CAP,
CAPABILITY_ATTR CA
WHERE C.CUST_TYPE = '0'
AND C.STATE IN ('0', '1')
AND C.PARENT_ID IS NOT NULL
AND C.CUST_CODE IS NOT NULL
AND PC.CUST_TYPE = '0'
AND PC.STATE IN ('0', '1')
AND PC.PARENT_ID IS NULL
AND PC.CUST_CODE IS NOT NULL
AND C.PARENT_ID = PC.CUST_ID
AND BC.CUST_ID = PC.CUST_ID
AND P.DEVICE_TYPE <> '04'
AND P.PU_SEQUENCE_ID = V.PU_SEQUENCE_ID
AND BC.PU_EQUIP_CODE = P.PU_EQUIP_CODE
AND BC.VIDEO_IN_PORT = V.VIDEO_IN_PORT
AND CAP.CAPABILITY_SEQUENCE = '5'
AND PC.CUST_ID = CCC.CUST_ID
AND CCC.CUST_CAPABILITY_ID = CA.CUST_CAPABILITY_ID
AND CCC.CAPABILITY_ID = CAP.CAPABILITY_ID
心中那个得意啊,“我得意地笑,我得意地笑”,呵呵~~。赶紧把视图贴到海豚(MySQL的一个客户端,SQLyog)里面,F8刷入数据库。接下来,加设备,加客户,划设备,折腾了半天,终于轮到数据库中的视图小试牛刀了,俺心中那个期待啊~~,CTRL + T,在海豚里面打开一个新的查询窗口,瞬间敲入熟悉的SQL:
SELECT * FROM QUERRY_DEVICEPORT_VIEW
F8,期待结果列表的出现……1秒钟过去了,2秒钟过去了,……,平时欢快活跃的海豚,今天一反常态,变得出奇的安静!终于,结果出来了,81984条记录,用时3312毫秒!!!我不由倒抽一口凉气,如此的查询速度,等到结果出来,恐怕黄花菜都凉了,谁还愿意用呢?
CREATE OR REPLACE
ALGORITHM = MERGE
VIEW QUERRY_DEVICEPORT_VIEW AS
SELECT C.CUST_CODE,
C.CUST_NAME,
BC.PU_EQUIP_CODE,
BC.OPERRIGHT,
P.DEVICE_NAME,
COALESCE(P.STATE, 113) AS STATE,
BC.VIDEO_IN_PORT,
V.VIDEO_IN_NAME,
COALESCE(C.ONLINE_STATE, '0') AS ONLINE_STATE,
C.EXTEND_PASSWORD_FLAG,
_gb2312 '5' AS ORDER_STATE,
CA.ATTR_VALUE
FROM CAPABILITY CAP,
CORP_CUST_CAPABILITY CCC,
CAPABILITY_ATTR CA,
CUST C,
BUYCHANNEL BC,
PU P,
VIDEOINCHANNEL V
WHERE CAP.CAPABILITY_SEQUENCE = '5'
AND C.CUST_TYPE = '0'
AND C.STATE IN ('0', '1')
AND C.PARENT_ID IS NULL
AND C.CUST_CODE IS NOT NULL
AND P.DEVICE_TYPE <> '04'
AND CAP.CAPABILITY_ID = CCC.CAPABILITY_ID
AND CA.CUST_CAPABILITY_ID = CCC.CUST_CAPABILITY_ID
AND CCC.CUST_ID = C.CUST_ID
AND C.CUST_ID = BC.CUST_ID
AND BC.PU_EQUIP_CODE = P.PU_EQUIP_CODE
AND V.PU_SEQUENCE_ID = P.PU_SEQUENCE_ID
AND BC.VIDEO_IN_PORT = V.VIDEO_IN_PORT
UNION ALL
SELECT C.CUST_CODE,
C.CUST_NAME,
BC.PU_EQUIP_CODE,
BC.OPERRIGHT,
P.DEVICE_NAME,
COALESCE(P.STATE, 113) AS STATE,
BC.VIDEO_IN_PORT,
V.VIDEO_IN_NAME,
COALESCE(C.ONLINE_STATE, '0') AS ONLINE_STATE,
C.EXTEND_PASSWORD_FLAG,
_gb2312 '5' AS ORDER_STATE,
CA.ATTR_VALUE
FROM CAPABILITY CAP,
CORP_CUST_CAPABILITY CCC,
CAPABILITY_ATTR CA,
CUST PC,
CUST C,
BUYCHANNEL BC,
PU P,
VIDEOINCHANNEL V
WHERE CAP.CAPABILITY_SEQUENCE = '5'
AND PC.CUST_TYPE = '0'
AND PC.STATE IN ('0', '1')
AND PC.PARENT_ID IS NULL
AND PC.CUST_CODE IS NOT NULL
AND C.CUST_TYPE = '0'
AND C.STATE IN ('0', '1')
AND C.PARENT_ID IS NOT NULL
AND C.CUST_CODE IS NOT NULL
AND P.DEVICE_TYPE <> '04'
AND CAP.CAPABILITY_ID = CCC.CAPABILITY_ID
AND CCC.CUST_CAPABILITY_ID = CA.CUST_CAPABILITY_ID
AND CCC.CUST_ID = PC.CUST_ID
AND PC.CUST_ID = C.PARENT_ID
AND PC.CUST_ID = BC.CUST_ID
AND P.PU_EQUIP_CODE = BC.PU_EQUIP_CODE
AND P.PU_SEQUENCE_ID = V.PU_SEQUENCE_ID
AND BC.VIDEO_IN_PORT = V.VIDEO_IN_PORT
EXPLAIN、百度、谷歌……,经过一番折腾,性能的调整似乎有了眉目……
首先,对视图中关键的JOIN POINT建立索引,然后,调整表的查询顺序,记录少的表尽量往前提,再然后,把常量条件,能够减少记录的条件统统提到前面……
将修改过的视图刷入数据库,再次SELECT *测试,性能有所提高,时间缩短到1703毫秒。心中那个欣喜啊~~呵呵~无以言表,但是,转眼间,不免又紧张起来——查询的时间仍然大于1秒,这样的性能还是不符合要求的。
优化的初步成功,给我强烈地信心,仿佛问题的终极解决近在咫尺。把视图仔细的看了一遍,直觉告诉我问题的关键在于那个刺眼的UNION ALL操作,根据MySQL操作手册中的叙述,但凡包含UNION ALL的视图,MySQL几乎是无法做优化的,另外,UNION的第二部分SQL中对PC的关联似乎多余,视图可以进一步简化,甚至可以用一个SQL搞定。
基于上面的直觉,我决定乘胜追击,对视图再次进行调整,这里,要感谢特别原来做湖北电信经分项目开发过程中的知识积累,呵呵~
CREATE OR REPLACE
ALGORITHM = MERGE
VIEW QUERRY_DEVICEPORT_VIEW AS
SELECT C.CUST_CODE,
C.CUST_NAME,
BC.PU_EQUIP_CODE,
BC.OPERRIGHT,
P.DEVICE_NAME,
COALESCE(P.STATE, 113) AS STATE,
BC.VIDEO_IN_PORT,
V.VIDEO_IN_NAME,
COALESCE(C.ONLINE_STATE, '0') AS ONLINE_STATE,
C.EXTEND_PASSWORD_FLAG,
_gb2312 '5' AS ORDER_STATE,
CA.ATTR_VALUE
FROM CAPABILITY CAP,
CORP_CUST_CAPABILITY CCC,
CAPABILITY_ATTR CA,
CUST C,
PU P,
VIDEOINCHANNEL V,
BUYCHANNEL BC
WHERE CAP.CAPABILITY_SEQUENCE = '5'
AND C.CUST_TYPE = '0'
AND C.STATE IN ('0', '1')
AND C.CUST_CODE IS NOT NULL
AND CAP.CAPABILITY_ID = CCC.CAPABILITY_ID
AND CA.CUST_CAPABILITY_ID = CCC.CUST_CAPABILITY_ID
AND (CASE WHEN C.PARENT_ID IS NULL THEN CCC.CUST_ID = C.CUST_ID ELSE
CCC.CUST_ID = C.PARENT_ID END)
AND (CASE WHEN C.PARENT_ID IS NULL THEN C.CUST_ID = BC.CUST_ID ELSE
C.PARENT_ID = BC.CUST_ID END)
AND P.PU_SEQUENCE_ID = V.PU_SEQUENCE_ID
AND BC.PU_EQUIP_CODE = P.PU_EQUIP_CODE
AND BC.VIDEO_IN_PORT = V.VIDEO_IN_PORT
“没有最快,只有更快”,赶紧把视图刷入数据库,回到刚才的查询窗口,按下F8,执行SELECT *查询,
这次优化,主要是根据EXPLAIN来分析SQL语句的执行计划,对关键的条件加上索引。另外,MySQL操作手册中有一段话让我决定把小表放在JOIN的前面:“MySQL resolves all joins using a single-sweep multi-join method. This means that MySQL reads a row from the first table, and then finds a matching row in the second table, the third table, and so on. When all tables are processed, MySQL outputs the selected columns and backtracks through the table list until a table is found for which there are more matching rows. The next row is read from this table and the process continues with the next table.”。再有的就是,能够不用UNION、GROUP BY的地方,尽量不用。