如何优化 select * from TABLE_A

其实是标题党,吸引你进来,来做一次历时一周的sql优化。

实际的sql为(表名,列名与实际数据为虚构,如有雷同,纯属巧合):

select * from A where data =‘28-FEB-2025’ and ID = 1 and dep_id = 11022;

背景介绍:

  • 数据库:Oracle 19C
  • 并不是分区表
  • 表内数据大概在9亿左右
  • 索引建在column date,ID,DEP_ID
  • 表的统计更新状态正常
  • 索引状态正常
  • 当前状态的结果集数量为70万,全部返回的时间为55分钟。执行计划的cost接近20000
  • 开发同事希望全部返回的时间降到10秒-20秒

面对这样的背景,我沉默了很久。和他说,咱们先把它改成分区表吧。这张表这么大,你们之前是怎么忍过来的。

在和业务的同事开会讨论了三天之后,我们成功地把这张表改为了分区表。分区键为该查询的三个条件,即date,ID,dep_id.

有进行了一天的讨论后,一直认为,并没有做复合分区的必要。

所以,目前的表的状态为

  • 分区表
  • 分区键为date,ID,dep_id
  • 每一个partition 不存在 subpartition
  • 每一个分区的数据量为百万级别(80万-130万)
  • 每个分区的统计更新状态正常
  • 暂时没有新建索引
  • 工具执行当前查询的返回时间为0.1秒
  • 返回全部结果的时间为25-30分钟

第一阶段的优化后,我当时认为已经到达极限了。无法再继续优化了。因为此时的实际sql为:

select * from A partition(P1);

这与

select * from table_A;

没有区别。

不过,我还是接受了这样的疑问,为什么不加入parallel hint?

把sql变为

select /* + parallel (16)*/ from table_A;

我说,会有作用,但并不明显。原因在于当前,工具执行的时间为0.1 秒,代表着查询的速度其实是很快的,我们现在的瓶颈在print 结果上面。如果不增加查询条件,减少返回值。速度不会改变。

当然我们还是进行了测试,分别进行了并行度 8,16,32的测试。

测试结果与不加并行几乎无差别。

压力又回到了业务组的同事,你们真的需要这么多的数据嘛?

再又一轮的讨论中,我们得知,可以新加三个查询条件。city_code, int_code 和 AR。

不过,这三列里面的distinct value并不多。单独以某一列做b tree索引,并不是一个好的办法。

目前有这样两条方案:

1.把这三列绑定在一起做组合local索引。
2.把这三列绑在一起做位图local索引。

实测下来,执行计划中的cost均有明显降低,由5500降到了400左右.

但最后我们放弃位图的原因是,如果不在查询中加入指定index的hint,有时候,数据库的优化器并不会选择这个索引。
开发组的同事表示,这对后期的代码改动以及交接业务会带来一定的工作量。既然两个差不多,就选简单的吧。

建好索引,交差。

本以为这是事情的终章了,但是在夜深人静的时候,还是觉得隐隐不安,打开电脑,果然出现了新需求。

select * from A where data =‘28-FEB-2025’ and ID = 1 and dep_id = 11022 and city_code||‘.’||int_code in (‘122.9’,‘13.11’,‘6.3’);

这条查询为啥不走索引呢?

未完待续

<think> 我们正在处理用户关于SQL语句`insert into table_name select * from table_name1`的查询。根据提供的引用,我们需要解释其语法、使用场景、注意事项等。 引用中提到了两种主要语句: 1. INSERT INTO SELECT:将查询结果插入到已存在的表中。 2. SELECT INTO FROM:创建新表并将查询结果插入到新表中(注意:MySQL不支持,但其他数据库如SQL Server支持)。 用户询问的语句属于第一种:`INSERT INTO ... SELECT ...`。 根据引用[1]和[2]: - 语法形式:`INSERT INTO target_table (column1, column2, ...) SELECT column1, column2, ... FROM source_table [WHERE condition];` - 也可以不指定列名,但要求两个表的列结构一致(顺序和类型兼容)。 用户给出的语句:`insert into table_name select * from table_name1`,其中没有指定列名,要求table_name和table_name1的结构必须一致。 引用[4]指出:只复制希望的列插入到另一个已存在的表中,可以通过指定列名实现。 注意事项(引用[1]、[2]、[3]、[4]): - 目标表必须已经存在(与SELECT INTO不同,后者会创建新表)。 - 字段的类型和顺序必须匹配(如果不指定列名)。 - 如果需要指定列,则目标表中指定的列和源表查询的列的数量和类型要匹配。 - 在MySQL中,不支持SELECT INTO(创建新表),但可以使用`CREATE TABLE ... SELECT ...`来代替(如引用[2]所示)。 - 引用[5]提到了在PgSQL中批量插入或更新(使用ON CONFLICT子句处理冲突),但用户当前语句不涉及更新。 使用场景: - 将一个表中的数据复制到另一个已存在的表中(部分或全部数据)。 - 将多个表的数据合并后插入到一个表中(通过SELECT语句连接多个表)。 - 备份表数据。 - 数据迁移。 因此,我们可以这样组织回答: 1. 语法解释:该语句用于将源表(table_name1)的所有数据插入到目标表(table_name)中。要求两个表的结构相同(列数、列顺序、列类型兼容)。 2. 如果不希望复制所有列,或者列顺序不一致,可以指定列名。 3. 注意事项:目标表必须存在;注意主键冲突(如果目标表有主键或唯一约束,重复的数据会导致插入失败);在大数据量时注意性能。 4. 拓展:在支持ON CONFLICT的数据库(如PostgreSQL)中,可以添加冲突处理(如引用[5]所示),但标准SQL中不支持。 由于用户没有指定数据库类型,我们以通用SQL为基础,同时提及一些数据库的差异(如MySQL不支持SELECT INTO,但支持INSERT INTO SELECT)。 另外,引用[3]提到了一种特殊的用法:通过添加一个永远不成立的条件(如WHERE 1=0)来复制表结构(不复制数据),但这是SELECT INTO的用法(用于创建新表),与当前语句无关。 因此,我们针对用户的问题详细解释如下: </think> ### SQL语句 `INSERT INTO table_name SELECT * FROM table_name1` 详解 #### 一、语法解释 该语句用于**将源表的数据批量插入到目标表**: ```sql INSERT INTO 目标表 (可选列名) SELECT 列名或表达式 FROM 源表 [WHERE 条件]; ``` - **目标表** `table_name`:必须是**已存在的表** - **源表** `table_name1`:提供数据的表或子查询 - `SELECT *`:复制源表所有列(也可指定具体列) - 若不指定列名:要求两表结构**完全兼容**(列数、顺序、类型一致) #### 二、核心作用 1. **数据迁移** 将数据从旧表迁移到新表(如归档历史数据) ```sql INSERT INTO orders_2023 SELECT * FROM orders WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31'; ``` 2. **表备份** 快速创建数据副本(需先建表): ```sql CREATE TABLE employees_backup LIKE employees; -- 先建表 INSERT INTO employees_backup SELECT * FROM employees; ``` 3. **数据聚合** 合并多个表数据: ```sql INSERT INTO sales_summary SELECT region, SUM(amount) FROM sales_data GROUP BY region; ``` #### 三、关键注意事项 1. **表必须存在** 目标表需预先创建(与 `SELECT INTO` 创建新表不同[^3])。 2. **列兼容性** - 若指定列名:顺序和类型需匹配 - 若用 `SELECT *`:两表结构必须完全一致 3. **主键/唯一约束冲突** 可能触发错误(如重复主键): ```sql -- PostgreSQL 冲突处理 INSERT INTO users SELECT * FROM temp_users ON CONFLICT (com_id, user_number) DO UPDATE SET name = EXCLUDED.name, score = EXCLUDED.score; [^5] ``` 4. **性能优化** 大数据量时建议: - 添加 `WHERE` 条件分批插入 - 事务中禁用索引(插入后重建) #### 四、与 `SELECT INTO` 的区别 | **特性** | `INSERT INTO SELECT` | `SELECT INTO` | |------------------|-----------------------------------|-----------------------------------| | 目标表要求 | 必须已存在 | 自动创建新表 | | 数据库兼容性 | 所有SQL数据库支持 | MySQL不支持(需改用 `CREATE TABLE ... SELECT`[^2]) | | 典型用途 | 数据追加到现有表 | 快速创建表并复制数据 | | 语法示例 | `INSERT INTO t2 SELECT * FROM t1` | `SELECT * INTO t2 FROM t1` | #### 五、应用场景 1. **跨服务器数据同步** (配合数据库链接) ```sql INSERT INTO local_db.customers SELECT * FROM remote_server.prod_db.customers; ``` 2. **数据转换加载(ETL)** 清洗后导入目标表: ```sql INSERT INTO clean_data SELECT TRIM(name), ABS(amount), CURRENT_TIMESTAMP FROM raw_data WHERE amount > 0; ``` 3. **分表数据合并** 聚合多个分表数据: ```sql INSERT INTO annual_report SELECT * FROM sales_q1 UNION ALL SELECT * FROM sales_q2; ``` > **重要提示**:MySQL中若需自动建表,应使用组合语句: > ```sql > CREATE TABLE new_table AS SELECT * FROM source_table; [^2] > ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值