GBase 8a 临时表在会话中间结果处理里的使用边界我最近整理 GBase 8a 的一些 SQL 写法时发现临时表这个对象很容易被低估。很多人提到中间结果第一反应是建一张普通表跑完再删也有人把临时表当成“随便用、不会留下痕迹”的工具。真正落到现场时这两种理解都不够稳。临时表确实适合承接会话内的中间结果但它不是无成本对象也不是所有临时加工场景都适合直接上。GBase 8a MPP Cluster 里临时表被限制在当前连接中连接关闭后会自动删除。这个特点对排查和开发都很有价值同名临时表可以在不同连接里同时存在也不会和已有同名普通表直接冲突。但这里有一个容易踩的点临时表会在当前连接里隐藏同名普通表。现场如果连接没有切干净或者脚本复用了连接很可能出现“我查的表不是我以为的那张表”。临时表适合解决什么问题我自己理解下来临时表更适合三类场景。第一类是复杂加工链路里的中间结果沉淀。比如一个月度结算任务需要先把交易明细按客户、机构、产品做几轮过滤再和维表做校验。如果每一步都写成一个超长 SQL后续排查会很难。把关键中间结果落到临时表里能把问题切小。第二类是会话级调试。排查数据差异时我经常会把可疑数据集保存到临时表然后反复做对比而不是每次重新跑原始查询。第三类是避免污染正式库对象。有些临时加工表只在一次会话里有意义建成普通表会留下清理负担表名管理也容易混乱。场景适合临时表吗原因注意点一次性数据核对适合只在当前连接内使用连接断开后结果消失多步骤加工调试适合便于拆分复杂 SQL控制数据量和字段数跨会话共享中间结果不适合临时表只属于当前连接用普通表或结果表需要备份保留不适合临时表不能被备份改用正式对象应用连接池复用谨慎临时对象生命周期和连接绑定必须显式清理这张表里我最关注“连接池复用”。很多开发调试时是一条连接跑完就退出但应用侧连接池不会这么做。连接如果被复用临时表就可能在同一个物理连接里继续存在影响下一段逻辑。同名对象隐藏是最容易被忽略的点GBase 8a 文档里提到两个不同连接可以使用同一个临时表名而不会冲突也不会与现有同名表冲突现有表会被隐藏直到临时表被删除。这个设计在功能上很方便但排查时需要格外小心。下面这个例子很贴近现场-- 正式业务表CREATETABLErpt_work.tmp_check_result(cust_idbigint,acct_monthvarchar(6),diff_amtdecimal(18,2))DISTRIBUTEDBY(cust_id);-- 某个排查会话里又创建了同名临时表CREATETEMPORARYTABLErpt_work.tmp_check_result(cust_idbigint,acct_monthvarchar(6),diff_amtdecimal(18,2),check_tagvarchar(32))DISTRIBUTEDBY(cust_id);INSERTINTOrpt_work.tmp_check_resultVALUES(1002003001,202604,18.50,session_check);SELECT*FROMrpt_work.tmp_check_result;在当前连接里查询到的是临时表不是正式表。换一个连接后同样的 SQL 可能查到正式表。这种现象如果没有提前意识到会让排查过程变得非常混乱。我实际处理时一般会给临时表加明显前缀例如tmp_sess_、tmp_chk_并避免和正式表命名空间重叠。命名方式风险建议tmp_check_result容易和正式对象撞名加会话或任务标识result_202604看不出是否临时对象不建议tmp_sess_recon_0427可读性较好适合手工排查tmp_app_stage应用复用时有残留风险配合显式 DROP临时表不是免治理对象临时表会话结束后自动删除但这不代表使用过程没有资源成本。GBase 8a 是 MPP 架构临时表如果承接大量数据仍然会占用节点上的存储、执行资源和网络传输资源。尤其是把大表过滤结果直接塞进临时表时不能只想着“反正会自动删”。我一般会从三个角度控制临时表使用。第一字段只保留后续要用的列。不要SELECT *直接落临时表。第二尽量在写入临时表前完成必要过滤。中间表越宽、越大后续 Join、聚合成本越高。第三分布方式要服务后续操作。临时表也可以指定DISTRIBUTED BY或REPLICATED不能默认随便建。CREATETEMPORARYTABLEtmp_sess_pay_202604(cust_idbigint,org_idvarchar(16),pay_monthvarchar(6),pay_amtdecimal(18,2))DISTRIBUTEDBY(cust_id);INSERTINTOtmp_sess_pay_202604SELECTcust_id,org_id,pay_month,SUM(pay_amt)ASpay_amtFROMods_pay_detailWHEREpay_month202604ANDpay_statusSGROUPBYcust_id,org_id,pay_month;这里没有把明细全塞进去而是先按后续需要的粒度聚合。临时表不是垃圾桶它更像一次排查里的工作台。工作台上放什么直接影响后面处理效率。分布方式要跟后续使用对齐临时表的分布方式经常被忽略。很多人建临时表时只关心字段没想过后续要怎么关联。GBase 8a 场景里如果临时表后续要和大事实表按客户号关联我会优先考虑按客户号分布如果临时表数据很小、后续要和多张大表关联可以考虑复制表思路但要控制数据量。临时表用途推荐分布思路原因风险和大表按客户关联DISTRIBUTED BY (cust_id)降低数据重分布cust_id 倾斜会影响节点小维表临时补充REPLICATED避免多次广播数据量不能大仅当前会话单表过滤按主要过滤或关联列服务后续查询不要盲目随机临时汇总结果按汇总主键分布便于二次关联汇总键过少可能倾斜我不建议把临时表分布方式当成固定模板。现场要看后续 SQL 怎么写。如果只是单次查看结果分布方式影响不大如果后续还有 Join、聚合、导出分布方式就会变成关键细节。应用脚本里要显式清理虽然临时表会在连接关闭时自动删除但我在脚本里仍然倾向于显式DROP TEMPORARY TABLE。原因很简单不是所有脚本都能保证连接马上关闭也不是所有异常分支都能自然走到退出。一个比较稳的写法是开始前先尝试清理再创建临时表结束后再清理一次。DROPTEMPORARYTABLEIFEXISTStmp_sess_order_delta;CREATETEMPORARYTABLEtmp_sess_order_delta(order_idbigint,cust_idbigint,order_daydate,delta_amtdecimal(18,2))DISTRIBUTEDBY(cust_id);INSERTINTOtmp_sess_order_deltaSELECTorder_id,cust_id,order_day,pay_amt-refund_amtASdelta_amtFROMdwd_order_dayWHEREorder_day2026-04-01ANDorder_day2026-05-01ANDpay_amtrefund_amt;SELECTcust_id,SUM(delta_amt)ASsum_deltaFROMtmp_sess_order_deltaGROUPBYcust_idHAVINGSUM(delta_amt)0;DROPTEMPORARYTABLEIFEXISTStmp_sess_order_delta;Shell 脚本里也可以把清理逻辑放到退出路径中#!/bin/bashset-eDB_HOST192.0.2.41DB_USERetl_checkDB_NAMErisk_dwgbase -h${DB_HOST}-u${DB_USER}${DB_NAME}SQL DROP TEMPORARY TABLE IF EXISTS tmp_sess_risk_order; CREATE TEMPORARY TABLE tmp_sess_risk_order ( cust_id bigint, risk_day date, risk_score decimal(10,4) ) DISTRIBUTED BY (cust_id); INSERT INTO tmp_sess_risk_order SELECT cust_id, risk_day, risk_score FROM ads_risk_score_day WHERE risk_day 2026-04-30 AND risk_score 0.85; SELECT COUNT(*) AS high_risk_cnt FROM tmp_sess_risk_order; DROP TEMPORARY TABLE IF EXISTS tmp_sess_risk_order; SQL我自己更倾向于把临时表看成脚本内部变量而不是数据库长期对象。变量用完要释放临时表也是一样。常见坑和处理建议常见坑现场表现原因建议临时表和正式表同名同一 SQL 不同连接结果不同当前连接隐藏正式表临时表命名加专用前缀应用连接池复用下一次请求看到上次临时表连接未关闭开始和结束都显式 DROPSELECT *建临时表中间结果过宽过大字段未裁剪只保留后续必要字段不指定分布方式后续 Join 成本高没贴合关联键按后续访问方式设计把临时表当结果表连接断开数据丢失生命周期误判需要保留时用普通表期望备份临时表备份中找不到临时表不参与备份不承载长期结果结尾总结GBase 8a 临时表很适合承接会话内中间结果也很适合排查复杂 SQL 时把问题拆小。但它的边界也很清楚生命周期绑定连接不能备份可能隐藏同名正式表数据量大时仍然会带来资源成本。我最近整理下来觉得临时表用得好不好关键不在语法而在对象边界。命名要和正式表拉开分布方式要和后续 SQL 对齐脚本里要显式清理应用连接池场景要格外谨慎。把这些细节处理好临时表会是很实用的排查和加工工具如果只把它当成“临时凑一下”的对象后面反而容易制造新的现场问题。参考资料[1] GBase 8a DDL语法 CREATE TEMPORARY TABLE https://www.gbase.cn/docs/gbase-8a/%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8C/dm-database-management-guide/dm-sql-reference/dm-syntax-ddl [2] GBase 8a 系统表 https://www.gbase.cn/docs/gbase-8a/%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8C/dm-database-management-guide/dm-system-table/ [3] GBase 社区优质文章区 https://www.gbase.cn/community/section/11
GBase 8a 临时表在会话中间结果处理里的使用边界
GBase 8a 临时表在会话中间结果处理里的使用边界我最近整理 GBase 8a 的一些 SQL 写法时发现临时表这个对象很容易被低估。很多人提到中间结果第一反应是建一张普通表跑完再删也有人把临时表当成“随便用、不会留下痕迹”的工具。真正落到现场时这两种理解都不够稳。临时表确实适合承接会话内的中间结果但它不是无成本对象也不是所有临时加工场景都适合直接上。GBase 8a MPP Cluster 里临时表被限制在当前连接中连接关闭后会自动删除。这个特点对排查和开发都很有价值同名临时表可以在不同连接里同时存在也不会和已有同名普通表直接冲突。但这里有一个容易踩的点临时表会在当前连接里隐藏同名普通表。现场如果连接没有切干净或者脚本复用了连接很可能出现“我查的表不是我以为的那张表”。临时表适合解决什么问题我自己理解下来临时表更适合三类场景。第一类是复杂加工链路里的中间结果沉淀。比如一个月度结算任务需要先把交易明细按客户、机构、产品做几轮过滤再和维表做校验。如果每一步都写成一个超长 SQL后续排查会很难。把关键中间结果落到临时表里能把问题切小。第二类是会话级调试。排查数据差异时我经常会把可疑数据集保存到临时表然后反复做对比而不是每次重新跑原始查询。第三类是避免污染正式库对象。有些临时加工表只在一次会话里有意义建成普通表会留下清理负担表名管理也容易混乱。场景适合临时表吗原因注意点一次性数据核对适合只在当前连接内使用连接断开后结果消失多步骤加工调试适合便于拆分复杂 SQL控制数据量和字段数跨会话共享中间结果不适合临时表只属于当前连接用普通表或结果表需要备份保留不适合临时表不能被备份改用正式对象应用连接池复用谨慎临时对象生命周期和连接绑定必须显式清理这张表里我最关注“连接池复用”。很多开发调试时是一条连接跑完就退出但应用侧连接池不会这么做。连接如果被复用临时表就可能在同一个物理连接里继续存在影响下一段逻辑。同名对象隐藏是最容易被忽略的点GBase 8a 文档里提到两个不同连接可以使用同一个临时表名而不会冲突也不会与现有同名表冲突现有表会被隐藏直到临时表被删除。这个设计在功能上很方便但排查时需要格外小心。下面这个例子很贴近现场-- 正式业务表CREATETABLErpt_work.tmp_check_result(cust_idbigint,acct_monthvarchar(6),diff_amtdecimal(18,2))DISTRIBUTEDBY(cust_id);-- 某个排查会话里又创建了同名临时表CREATETEMPORARYTABLErpt_work.tmp_check_result(cust_idbigint,acct_monthvarchar(6),diff_amtdecimal(18,2),check_tagvarchar(32))DISTRIBUTEDBY(cust_id);INSERTINTOrpt_work.tmp_check_resultVALUES(1002003001,202604,18.50,session_check);SELECT*FROMrpt_work.tmp_check_result;在当前连接里查询到的是临时表不是正式表。换一个连接后同样的 SQL 可能查到正式表。这种现象如果没有提前意识到会让排查过程变得非常混乱。我实际处理时一般会给临时表加明显前缀例如tmp_sess_、tmp_chk_并避免和正式表命名空间重叠。命名方式风险建议tmp_check_result容易和正式对象撞名加会话或任务标识result_202604看不出是否临时对象不建议tmp_sess_recon_0427可读性较好适合手工排查tmp_app_stage应用复用时有残留风险配合显式 DROP临时表不是免治理对象临时表会话结束后自动删除但这不代表使用过程没有资源成本。GBase 8a 是 MPP 架构临时表如果承接大量数据仍然会占用节点上的存储、执行资源和网络传输资源。尤其是把大表过滤结果直接塞进临时表时不能只想着“反正会自动删”。我一般会从三个角度控制临时表使用。第一字段只保留后续要用的列。不要SELECT *直接落临时表。第二尽量在写入临时表前完成必要过滤。中间表越宽、越大后续 Join、聚合成本越高。第三分布方式要服务后续操作。临时表也可以指定DISTRIBUTED BY或REPLICATED不能默认随便建。CREATETEMPORARYTABLEtmp_sess_pay_202604(cust_idbigint,org_idvarchar(16),pay_monthvarchar(6),pay_amtdecimal(18,2))DISTRIBUTEDBY(cust_id);INSERTINTOtmp_sess_pay_202604SELECTcust_id,org_id,pay_month,SUM(pay_amt)ASpay_amtFROMods_pay_detailWHEREpay_month202604ANDpay_statusSGROUPBYcust_id,org_id,pay_month;这里没有把明细全塞进去而是先按后续需要的粒度聚合。临时表不是垃圾桶它更像一次排查里的工作台。工作台上放什么直接影响后面处理效率。分布方式要跟后续使用对齐临时表的分布方式经常被忽略。很多人建临时表时只关心字段没想过后续要怎么关联。GBase 8a 场景里如果临时表后续要和大事实表按客户号关联我会优先考虑按客户号分布如果临时表数据很小、后续要和多张大表关联可以考虑复制表思路但要控制数据量。临时表用途推荐分布思路原因风险和大表按客户关联DISTRIBUTED BY (cust_id)降低数据重分布cust_id 倾斜会影响节点小维表临时补充REPLICATED避免多次广播数据量不能大仅当前会话单表过滤按主要过滤或关联列服务后续查询不要盲目随机临时汇总结果按汇总主键分布便于二次关联汇总键过少可能倾斜我不建议把临时表分布方式当成固定模板。现场要看后续 SQL 怎么写。如果只是单次查看结果分布方式影响不大如果后续还有 Join、聚合、导出分布方式就会变成关键细节。应用脚本里要显式清理虽然临时表会在连接关闭时自动删除但我在脚本里仍然倾向于显式DROP TEMPORARY TABLE。原因很简单不是所有脚本都能保证连接马上关闭也不是所有异常分支都能自然走到退出。一个比较稳的写法是开始前先尝试清理再创建临时表结束后再清理一次。DROPTEMPORARYTABLEIFEXISTStmp_sess_order_delta;CREATETEMPORARYTABLEtmp_sess_order_delta(order_idbigint,cust_idbigint,order_daydate,delta_amtdecimal(18,2))DISTRIBUTEDBY(cust_id);INSERTINTOtmp_sess_order_deltaSELECTorder_id,cust_id,order_day,pay_amt-refund_amtASdelta_amtFROMdwd_order_dayWHEREorder_day2026-04-01ANDorder_day2026-05-01ANDpay_amtrefund_amt;SELECTcust_id,SUM(delta_amt)ASsum_deltaFROMtmp_sess_order_deltaGROUPBYcust_idHAVINGSUM(delta_amt)0;DROPTEMPORARYTABLEIFEXISTStmp_sess_order_delta;Shell 脚本里也可以把清理逻辑放到退出路径中#!/bin/bashset-eDB_HOST192.0.2.41DB_USERetl_checkDB_NAMErisk_dwgbase -h${DB_HOST}-u${DB_USER}${DB_NAME}SQL DROP TEMPORARY TABLE IF EXISTS tmp_sess_risk_order; CREATE TEMPORARY TABLE tmp_sess_risk_order ( cust_id bigint, risk_day date, risk_score decimal(10,4) ) DISTRIBUTED BY (cust_id); INSERT INTO tmp_sess_risk_order SELECT cust_id, risk_day, risk_score FROM ads_risk_score_day WHERE risk_day 2026-04-30 AND risk_score 0.85; SELECT COUNT(*) AS high_risk_cnt FROM tmp_sess_risk_order; DROP TEMPORARY TABLE IF EXISTS tmp_sess_risk_order; SQL我自己更倾向于把临时表看成脚本内部变量而不是数据库长期对象。变量用完要释放临时表也是一样。常见坑和处理建议常见坑现场表现原因建议临时表和正式表同名同一 SQL 不同连接结果不同当前连接隐藏正式表临时表命名加专用前缀应用连接池复用下一次请求看到上次临时表连接未关闭开始和结束都显式 DROPSELECT *建临时表中间结果过宽过大字段未裁剪只保留后续必要字段不指定分布方式后续 Join 成本高没贴合关联键按后续访问方式设计把临时表当结果表连接断开数据丢失生命周期误判需要保留时用普通表期望备份临时表备份中找不到临时表不参与备份不承载长期结果结尾总结GBase 8a 临时表很适合承接会话内中间结果也很适合排查复杂 SQL 时把问题拆小。但它的边界也很清楚生命周期绑定连接不能备份可能隐藏同名正式表数据量大时仍然会带来资源成本。我最近整理下来觉得临时表用得好不好关键不在语法而在对象边界。命名要和正式表拉开分布方式要和后续 SQL 对齐脚本里要显式清理应用连接池场景要格外谨慎。把这些细节处理好临时表会是很实用的排查和加工工具如果只把它当成“临时凑一下”的对象后面反而容易制造新的现场问题。参考资料[1] GBase 8a DDL语法 CREATE TEMPORARY TABLE https://www.gbase.cn/docs/gbase-8a/%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8C/dm-database-management-guide/dm-sql-reference/dm-syntax-ddl [2] GBase 8a 系统表 https://www.gbase.cn/docs/gbase-8a/%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8C/dm-database-management-guide/dm-system-table/ [3] GBase 社区优质文章区 https://www.gbase.cn/community/section/11