|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
1. Oracle数据库查询语言概述
Oracle数据库作为企业级数据库管理系统的领导者,其查询语言SQL(Structured Query Language)是数据管理与分析的核心工具。掌握Oracle SQL不仅可以高效地检索和操作数据,还能通过高级技巧解决复杂的业务问题,显著提升工作效率。
Oracle SQL在标准SQL的基础上进行了扩展,提供了丰富的功能,包括强大的分析函数、层次查询、高级聚合操作等。这些功能使Oracle SQL成为处理企业级复杂数据查询与分析的理想选择。
2. Oracle SQL基础语法
2.1 基本SELECT语句
SELECT语句是SQL中最基本也是最常用的语句,用于从数据库中检索数据。
- -- 基本语法
- SELECT column1, column2, ...
- FROM table_name
- [WHERE condition]
- [ORDER BY column1 [ASC|DESC], column2 [ASC|DESC], ...];
- -- 示例:查询员工表中的员工ID、姓名和薪资
- SELECT employee_id, first_name, last_name, salary
- FROM employees
- WHERE department_id = 10
- ORDER BY salary DESC;
复制代码
2.2 数据过滤与条件查询
WHERE子句用于过滤记录,只返回满足特定条件的记录。
- -- 使用比较运算符
- SELECT * FROM employees
- WHERE salary > 5000;
- -- 使用逻辑运算符
- SELECT * FROM employees
- WHERE department_id = 10 OR department_id = 20;
- -- 使用BETWEEN运算符
- SELECT * FROM employees
- WHERE salary BETWEEN 3000 AND 6000;
- -- 使用IN运算符
- SELECT * FROM employees
- WHERE department_id IN (10, 20, 30);
- -- 使用LIKE运算符进行模糊匹配
- SELECT * FROM employees
- WHERE last_name LIKE 'S%';
- -- 使用IS NULL检查空值
- SELECT * FROM employees
- WHERE commission_pct IS NULL;
复制代码
2.3 聚合函数与分组
聚合函数对一组值执行计算并返回单个值,常与GROUP BY子句一起使用。
- -- 常用聚合函数
- SELECT COUNT(*) AS total_employees,
- AVG(salary) AS avg_salary,
- MAX(salary) AS max_salary,
- MIN(salary) AS min_salary,
- SUM(salary) AS total_salary
- FROM employees;
- -- 使用GROUP BY进行分组
- SELECT department_id,
- COUNT(*) AS employee_count,
- AVG(salary) AS avg_salary
- FROM employees
- GROUP BY department_id;
- -- 使用HAVING过滤分组结果
- SELECT department_id,
- COUNT(*) AS employee_count,
- AVG(salary) AS avg_salary
- FROM employees
- GROUP BY department_id
- HAVING COUNT(*) > 5;
复制代码
2.4 多表连接查询
连接查询用于从多个表中检索相关数据。
- -- 内连接(INNER JOIN)
- SELECT e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- INNER JOIN departments d ON e.department_id = d.department_id;
- -- 左连接(LEFT JOIN)
- SELECT e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- LEFT JOIN departments d ON e.department_id = d.department_id;
- -- 右连接(RIGHT JOIN)
- SELECT e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- RIGHT JOIN departments d ON e.department_id = d.department_id;
- -- 全连接(FULL JOIN)
- SELECT e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- FULL JOIN departments d ON e.department_id = d.department_id;
- -- 自连接
- SELECT e1.employee_id, e1.first_name, e1.last_name,
- e2.first_name AS manager_name, e2.last_name AS manager_last_name
- FROM employees e1
- LEFT JOIN employees e2 ON e1.manager_id = e2.employee_id;
复制代码
3. 中级查询技术
3.1 子查询
子查询是嵌套在其他SQL语句中的SELECT语句,可以用于WHERE子句、FROM子句或SELECT列表中。
- -- 在WHERE子句中使用子查询
- SELECT employee_id, first_name, last_name, salary
- FROM employees
- WHERE salary > (SELECT AVG(salary) FROM employees);
- -- 在FROM子句中使用子查询(内联视图)
- SELECT d.department_name, avg_sal.avg_salary
- FROM departments d
- JOIN (SELECT department_id, AVG(salary) AS avg_salary
- FROM employees
- GROUP BY department_id) avg_sal
- ON d.department_id = avg_sal.department_id;
- -- 在SELECT列表中使用子查询
- SELECT e.employee_id, e.first_name, e.last_name,
- (SELECT department_name FROM departments d WHERE d.department_id = e.department_id) AS department_name
- FROM employees e;
- -- 使用EXISTS和NOT EXISTS
- SELECT d.department_id, d.department_name
- FROM departments d
- WHERE EXISTS (SELECT 1 FROM employees e WHERE e.department_id = d.department_id);
- SELECT d.department_id, d.department_name
- FROM departments d
- WHERE NOT EXISTS (SELECT 1 FROM employees e WHERE e.department_id = d.department_id);
复制代码
3.2 集合操作
Oracle提供了三种主要的集合操作:UNION、INTERSECT和MINUS。
- -- UNION:合并两个查询结果,去除重复行
- SELECT employee_id, first_name, last_name FROM employees WHERE department_id = 10
- UNION
- SELECT employee_id, first_name, last_name FROM employees WHERE salary > 5000;
- -- UNION ALL:合并两个查询结果,保留所有行,包括重复行
- SELECT employee_id, first_name, last_name FROM employees WHERE department_id = 10
- UNION ALL
- SELECT employee_id, first_name, last_name FROM employees WHERE salary > 5000;
- -- INTERSECT:返回两个查询结果的交集
- SELECT employee_id, first_name, last_name FROM employees WHERE department_id = 10
- INTERSECT
- SELECT employee_id, first_name, last_name FROM employees WHERE salary > 5000;
- -- MINUS:返回在第一个查询结果中但不在第二个查询结果中的行
- SELECT employee_id, first_name, last_name FROM employees WHERE department_id = 10
- MINUS
- SELECT employee_id, first_name, last_name FROM employees WHERE salary > 5000;
复制代码
3.3 层次查询
层次查询用于处理具有层次结构的数据,如组织结构、产品分类等。
- -- 基本层次查询语法
- SELECT [LEVEL], column, expr...
- FROM table_name
- [WHERE condition]
- START WITH condition
- CONNECT BY [NOCYCLE] condition;
- -- 示例:查询员工组织结构
- SELECT LEVEL,
- LPAD(' ', 2*(LEVEL-1)) || employee_id AS emp_id,
- first_name || ' ' || last_name AS employee_name,
- manager_id
- FROM employees
- START WITH manager_id IS NULL
- CONNECT BY PRIOR employee_id = manager_id;
- -- 使用CONNECT_BY_ROOT查找根节点
- SELECT CONNECT_BY_ROOT employee_id AS root_manager_id,
- employee_id, first_name, last_name
- FROM employees
- WHERE LEVEL > 1
- START WITH manager_id IS NULL
- CONNECT BY PRIOR employee_id = manager_id;
- -- 使用CONNECT_BY_ISLEAF识别叶子节点
- SELECT employee_id, first_name, last_name,
- CONNECT_BY_ISLEAF AS is_leaf
- FROM employees
- START WITH manager_id IS NULL
- CONNECT BY PRIOR employee_id = manager_id;
复制代码
3.4 分析函数(Analytic Functions)
分析函数是Oracle SQL的强大功能,允许在结果集上执行复杂计算,同时保持原始行的完整性。
- -- ROW_NUMBER():为结果集中的每一行分配一个唯一的序号
- SELECT employee_id, first_name, last_name, department_id, salary,
- ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS salary_rank
- FROM employees;
- -- RANK()和DENSE_RANK():为结果集中的行分配排名
- SELECT employee_id, first_name, last_name, department_id, salary,
- RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rank,
- DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dense_rank
- FROM employees;
- -- LEAD()和LAG():访问结果集中后续行或前导行的数据
- SELECT employee_id, first_name, last_name, salary,
- LAG(salary, 1, 0) OVER (ORDER BY salary) AS prev_salary,
- LEAD(salary, 1, 0) OVER (ORDER BY salary) AS next_salary
- FROM employees;
- -- 窗口函数:计算移动平均、累计总和等
- SELECT employee_id, first_name, last_name, hire_date, salary,
- SUM(salary) OVER (ORDER BY hire_date) AS cumulative_salary,
- AVG(salary) OVER (ORDER BY hire_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
- FROM employees;
复制代码
4. 高级查询技巧
4.1 高级连接技术
- -- 使用USING子句简化连接条件
- SELECT employee_id, first_name, last_name, department_name
- FROM employees
- JOIN departments USING (department_id);
- -- 自然连接(NATURAL JOIN):自动匹配两个表中具有相同名称和数据类型的列
- SELECT employee_id, first_name, last_name, department_name
- FROM employees
- NATURAL JOIN departments;
- -- 交叉连接(CROSS JOIN):返回两个表的笛卡尔积
- SELECT e.first_name, e.last_name, d.department_name
- FROM employees e
- CROSS JOIN departments d;
- -- 分区外连接(Partitioned Outer Join):处理稀疏数据
- SELECT d.department_id, d.department_name, t.time_id, t.sales_amount
- FROM departments d
- LEFT OUTER JOIN sales t PARTITION BY (d.department_id)
- ON d.department_id = t.department_id
- ORDER BY d.department_id, t.time_id;
复制代码
4.2 递归子查询
Oracle 11g引入了递归子查询,使用WITH子句实现层次查询。
- -- 使用WITH子句实现递归查询
- WITH emp_hierarchy (employee_id, first_name, last_name, manager_id, level, path) AS (
- -- 基础查询
- SELECT employee_id, first_name, last_name, manager_id, 1 AS level,
- first_name || ' ' || last_name AS path
- FROM employees
- WHERE manager_id IS NULL
-
- UNION ALL
-
- -- 递归查询
- SELECT e.employee_id, e.first_name, e.last_name, e.manager_id, h.level + 1,
- h.path || ' -> ' || e.first_name || ' ' || e.last_name
- FROM employees e
- JOIN emp_hierarchy h ON e.manager_id = h.employee_id
- )
- SELECT employee_id, first_name, last_name, LPAD(' ', 2*(level-1)) || path AS org_path
- FROM emp_hierarchy
- ORDER BY level, path;
复制代码
4.3 PIVOT和UNPIVOT操作
PIVOT和UNPIVOT是Oracle 11g引入的功能,用于数据透视和逆透视操作。
- -- PIVOT:将行转换为列
- SELECT *
- FROM (
- SELECT department_id, job_id, salary
- FROM employees
- )
- PIVOT (
- AVG(salary) AS avg_salary
- FOR job_id IN ('AC_ACCOUNT' AS AC_ACCOUNT,
- 'AC_MGR' AS AC_MGR,
- 'AD_ASST' AS AD_ASST,
- 'AD_PRES' AS AD_PRES,
- 'AD_VP' AS AD_VP,
- 'FI_ACCOUNT' AS FI_ACCOUNT)
- )
- ORDER BY department_id;
- -- UNPIVOT:将列转换为行
- WITH dept_salaries AS (
- SELECT department_id,
- SUM(CASE WHEN job_id = 'AC_ACCOUNT' THEN salary ELSE 0 END) AS AC_ACCOUNT,
- SUM(CASE WHEN job_id = 'AC_MGR' THEN salary ELSE 0 END) AS AC_MGR,
- SUM(CASE WHEN job_id = 'AD_ASST' THEN salary ELSE 0 END) AS AD_ASST,
- SUM(CASE WHEN job_id = 'AD_PRES' THEN salary ELSE 0 END) AS AD_PRES,
- SUM(CASE WHEN job_id = 'AD_VP' THEN salary ELSE 0 END) AS AD_VP,
- SUM(CASE WHEN job_id = 'FI_ACCOUNT' THEN salary ELSE 0 END) AS FI_ACCOUNT
- FROM employees
- GROUP BY department_id
- )
- SELECT department_id, job_id, total_salary
- FROM dept_salaries
- UNPIVOT (
- total_salary FOR job_id IN (AC_ACCOUNT, AC_MGR, AD_ASST, AD_PRES, AD_VP, FI_ACCOUNT)
- )
- ORDER BY department_id, job_id;
复制代码
4.4 模式匹配(MATCH_RECOGNIZE)
Oracle 12c引入了MATCH_RECOGNIZE子句,用于在有序数据中识别模式。
- -- 示例:识别股票价格连续3天上涨的模式
- SELECT *
- FROM stock_prices
- MATCH_RECOGNIZE (
- PARTITION BY symbol
- ORDER BY trade_date
- MEASURES
- FIRST(trade_date) AS start_date,
- LAST(trade_date) AS end_date,
- COUNT(*) AS days_up
- ONE ROW PER MATCH
- AFTER MATCH SKIP PAST LAST ROW
- PATTERN (STRICT_UP+)
- DEFINE
- STRICT_UP AS price > PREV(price)
- );
复制代码
5. 查询优化方法
5.1 执行计划分析
执行计划是Oracle优化器为SQL语句生成的操作步骤,分析执行计划是优化查询的关键。
- -- 使用EXPLAIN PLAN生成执行计划
- EXPLAIN PLAN FOR
- SELECT e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- JOIN departments d ON e.department_id = d.department_id
- WHERE e.salary > 5000;
- -- 查看执行计划
- SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
- -- 使用AUTOTRACE查看执行计划和统计信息
- SET AUTOTRACE ON EXPLAIN STATISTICS
- SELECT e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- JOIN departments d ON e.department_id = d.department_id
- WHERE e.salary > 5000;
- SET AUTOTRACE OFF
复制代码
5.2 索引优化
索引是提高查询性能的重要工具,合理使用索引可以显著减少查询时间。
- -- 创建B树索引
- CREATE INDEX idx_emp_salary ON employees(salary);
- -- 创建复合索引
- CREATE INDEX idx_emp_dept_sal ON employees(department_id, salary);
- -- 创建函数索引
- CREATE INDEX idx_emp_upper_name ON employees(UPPER(last_name));
- -- 创建位图索引(适用于低基数列)
- CREATE BITMAP INDEX idx_emp_gender ON employees(gender);
- -- 监控索引使用情况
- ALTER INDEX idx_emp_salary MONITORING USAGE;
- SELECT * FROM V$OBJECT_USAGE;
- ALTER INDEX idx_emp_salary NOMONITORING USAGE;
- -- 重建索引以提高性能
- ALTER INDEX idx_emp_salary REBUILD;
复制代码
5.3 查询重写技巧
查询重写是通过改变SQL语句的结构而不改变其语义来提高性能的方法。
- -- 使用EXISTS替代IN
- -- 原始查询
- SELECT * FROM employees
- WHERE department_id IN (SELECT department_id FROM departments WHERE location_id = 1700);
- -- 重写后的查询
- SELECT * FROM employees e
- WHERE EXISTS (SELECT 1 FROM departments d
- WHERE d.department_id = e.department_id AND d.location_id = 1700);
- -- 使用JOIN替代子查询
- -- 原始查询
- SELECT e.employee_id, e.first_name, e.last_name,
- (SELECT department_name FROM departments d WHERE d.department_id = e.department_id) AS department_name
- FROM employees e;
- -- 重写后的查询
- SELECT e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- JOIN departments d ON e.department_id = d.department_id;
- -- 使用UNION ALL替代UNION(如果确定没有重复行)
- -- 原始查询
- SELECT employee_id, first_name, last_name FROM employees WHERE department_id = 10
- UNION
- SELECT employee_id, first_name, last_name FROM employees WHERE salary > 5000;
- -- 重写后的查询(如果确定两个结果集没有重复行)
- SELECT employee_id, first_name, last_name FROM employees WHERE department_id = 10
- UNION ALL
- SELECT employee_id, first_name, last_name FROM employees WHERE salary > 5000;
复制代码
5.4 统计信息收集
统计信息是Oracle优化器做出正确决策的基础,定期收集统计信息对查询性能至关重要。
- -- 收集表统计信息
- EXEC DBMS_STATS.GATHER_TABLE_STATS('HR', 'EMPLOYEES');
- -- 收集索引统计信息
- EXEC DBMS_STATS.GATHER_INDEX_STATS('HR', 'IDX_EMP_SALARY');
- -- 收集模式统计信息
- EXEC DBMS_STATS.GATHER_SCHEMA_STATS('HR');
- -- 收集数据库统计信息
- EXEC DBMS_STATS.GATHER_DATABASE_STATS;
- -- 查看表的统计信息
- SELECT table_name, num_rows, blocks, avg_row_len
- FROM user_tables
- WHERE table_name = 'EMPLOYEES';
- -- 查看列的统计信息
- SELECT table_name, column_name, num_distinct, density, num_nulls
- FROM user_tab_columns
- WHERE table_name = 'EMPLOYEES';
复制代码
6. 实际企业应用案例
6.1 销售数据分析
- -- 月度销售趋势分析
- SELECT
- TO_CHAR(order_date, 'YYYY-MM') AS month,
- COUNT(*) AS order_count,
- SUM(order_total) AS total_sales,
- AVG(order_total) AS avg_order_value,
- SUM(CASE WHEN status = 'SHIPPED' THEN 1 ELSE 0 END) AS shipped_orders,
- SUM(CASE WHEN status = 'PENDING' THEN 1 ELSE 0 END) AS pending_orders
- FROM orders
- WHERE order_date BETWEEN ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -11) AND TRUNC(SYSDATE, 'MM')
- GROUP BY TO_CHAR(order_date, 'YYYY-MM')
- ORDER BY month;
- -- 产品类别销售排名
- SELECT
- p.category_id,
- c.category_name,
- COUNT(DISTINCT o.order_id) AS order_count,
- SUM(oi.quantity * oi.unit_price) AS total_sales,
- RANK() OVER (ORDER BY SUM(oi.quantity * oi.unit_price) DESC) AS sales_rank
- FROM order_items oi
- JOIN products p ON oi.product_id = p.product_id
- JOIN categories c ON p.category_id = c.category_id
- JOIN orders o ON oi.order_id = o.order_id
- WHERE o.order_date BETWEEN ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -11) AND TRUNC(SYSDATE, 'MM')
- GROUP BY p.category_id, c.category_name
- ORDER BY total_sales DESC;
- -- 客户购买行为分析
- SELECT
- customer_id,
- COUNT(DISTINCT order_id) AS order_count,
- SUM(order_total) AS total_spent,
- AVG(order_total) AS avg_order_value,
- MIN(order_date) AS first_order_date,
- MAX(order_date) AS last_order_date,
- MAX(order_date) - MIN(order_date) AS customer_lifetime_days,
- CASE
- WHEN COUNT(DISTINCT order_id) > 10 THEN 'High Frequency'
- WHEN COUNT(DISTINCT order_id) > 5 THEN 'Medium Frequency'
- ELSE 'Low Frequency'
- END AS purchase_frequency,
- CASE
- WHEN SUM(order_total) > 10000 THEN 'High Value'
- WHEN SUM(order_total) > 5000 THEN 'Medium Value'
- ELSE 'Low Value'
- END AS customer_value
- FROM orders
- GROUP BY customer_id
- HAVING COUNT(DISTINCT order_id) > 1
- ORDER BY total_spent DESC;
复制代码
6.2 库存管理分析
- -- 库存周转率分析
- SELECT
- p.product_id,
- p.product_name,
- c.category_name,
- i.quantity_on_hand,
- i.reorder_level,
- CASE
- WHEN i.quantity_on_hand <= i.reorder_level THEN 'Reorder Required'
- ELSE 'Sufficient Stock'
- END AS stock_status,
- COALESCE(s.total_sold, 0) AS total_sold,
- CASE
- WHEN i.quantity_on_hand > 0 THEN (COALESCE(s.total_sold, 0) / i.quantity_on_hand)
- ELSE NULL
- END AS turnover_ratio,
- p.list_price,
- i.quantity_on_hand * p.list_price AS inventory_value
- FROM products p
- JOIN categories c ON p.category_id = c.category_id
- JOIN inventories i ON p.product_id = i.product_id
- LEFT JOIN (
- SELECT product_id, SUM(quantity) AS total_sold
- FROM order_items
- JOIN orders ON order_items.order_id = orders.order_id
- WHERE orders.order_date BETWEEN ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -11) AND TRUNC(SYSDATE, 'MM')
- GROUP BY product_id
- ) s ON p.product_id = s.product_id
- ORDER BY turnover_ratio DESC NULLS LAST;
- -- 库存老化分析
- WITH product_lifecycle AS (
- SELECT
- p.product_id,
- p.product_name,
- i.warehouse_id,
- i.quantity_on_hand,
- i.last_stock_date,
- TRUNC(SYSDATE) - i.last_stock_date AS days_in_stock,
- CASE
- WHEN TRUNC(SYSDATE) - i.last_stock_date > 365 THEN 'Obsolete'
- WHEN TRUNC(SYSDATE) - i.last_stock_date > 180 THEN 'Slow Moving'
- WHEN TRUNC(SYSDATE) - i.last_stock_date > 90 THEN 'Aging'
- ELSE 'Current'
- END AS stock_status,
- c.category_name
- FROM products p
- JOIN inventories i ON p.product_id = i.product_id
- JOIN categories c ON p.category_id = c.category_id
- )
- SELECT
- category_name,
- stock_status,
- COUNT(*) AS product_count,
- SUM(quantity_on_hand) AS total_quantity,
- AVG(days_in_stock) AS avg_days_in_stock
- FROM product_lifecycle
- GROUP BY category_name, stock_status
- ORDER BY category_name,
- CASE stock_status
- WHEN 'Obsolete' THEN 1
- WHEN 'Slow Moving' THEN 2
- WHEN 'Aging' THEN 3
- ELSE 4
- END;
复制代码
6.3 人力资源分析
- -- 员工流失率分析
- SELECT
- TO_CHAR(hire_date, 'YYYY') AS hire_year,
- COUNT(*) AS hired_count,
- SUM(CASE WHEN termination_date IS NOT NULL THEN 1 ELSE 0 END) AS terminated_count,
- ROUND(SUM(CASE WHEN termination_date IS NOT NULL THEN 1 ELSE 0 END) * 100 / COUNT(*), 2) AS turnover_rate,
- ROUND(AVG(CASE WHEN termination_date IS NOT NULL
- THEN termination_date - hire_date
- ELSE NULL END), 0) AS avg_tenure_days
- FROM employees
- GROUP BY TO_CHAR(hire_date, 'YYYY')
- ORDER BY hire_year;
- -- 部门人力成本分析
- SELECT
- d.department_id,
- d.department_name,
- COUNT(e.employee_id) AS employee_count,
- SUM(e.salary) AS total_salary_cost,
- SUM(e.salary * 12) AS annual_salary_cost,
- AVG(e.salary) AS avg_salary,
- SUM(CASE WHEN e.commission_pct IS NOT NULL THEN e.salary * e.commission_pct ELSE 0 END) AS total_commission,
- SUM(e.salary * 12 + CASE WHEN e.commission_pct IS NOT NULL THEN e.salary * 12 * e.commission_pct ELSE 0 END) AS total_annual_cost,
- SUM(CASE WHEN e.hire_date BETWEEN ADD_MONTHS(TRUNC(SYSDATE, 'YYYY'), -12) AND TRUNC(SYSDATE, 'YYYY')
- THEN 1 ELSE 0 END) AS hires_last_year,
- SUM(CASE WHEN e.termination_date BETWEEN ADD_MONTHS(TRUNC(SYSDATE, 'YYYY'), -12) AND TRUNC(SYSDATE, 'YYYY')
- THEN 1 ELSE 0 END) AS terminations_last_year
- FROM departments d
- LEFT JOIN employees e ON d.department_id = e.department_id
- GROUP BY d.department_id, d.department_name
- ORDER BY total_annual_cost DESC;
- -- 薪资分布分析
- SELECT
- CASE
- WHEN salary < 3000 THEN 'Under 3K'
- WHEN salary BETWEEN 3000 AND 5000 THEN '3K - 5K'
- WHEN salary BETWEEN 5001 AND 8000 THEN '5K - 8K'
- WHEN salary BETWEEN 8001 AND 12000 THEN '8K - 12K'
- WHEN salary > 12000 THEN 'Over 12K'
- END AS salary_range,
- COUNT(*) AS employee_count,
- ROUND(COUNT(*) * 100 / SUM(COUNT(*)) OVER (), 2) AS percentage,
- d.department_name
- FROM employees e
- JOIN departments d ON e.department_id = d.department_id
- GROUP BY
- CASE
- WHEN salary < 3000 THEN 'Under 3K'
- WHEN salary BETWEEN 3000 AND 5000 THEN '3K - 5K'
- WHEN salary BETWEEN 5001 AND 8000 THEN '5K - 8K'
- WHEN salary BETWEEN 8001 AND 12000 THEN '8K - 12K'
- WHEN salary > 12000 THEN 'Over 12K'
- END,
- d.department_name
- ORDER BY
- CASE
- WHEN salary < 3000 THEN 1
- WHEN salary BETWEEN 3000 AND 5000 THEN 2
- WHEN salary BETWEEN 5001 AND 8000 THEN 3
- WHEN salary BETWEEN 8001 AND 12000 THEN 4
- WHEN salary > 12000 THEN 5
- END,
- d.department_name;
复制代码
7. 性能调优技巧
7.1 SQL提示(Hints)的使用
SQL提示是指导Oracle优化器选择特定执行路径的指令。
- -- 使用INDEX提示强制使用特定索引
- SELECT /*+ INDEX(e idx_emp_department_id) */
- e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- JOIN departments d ON e.department_id = d.department_id
- WHERE e.department_id = 10;
- -- 使用FIRST_ROWS提示优化快速返回前几行结果
- SELECT /*+ FIRST_ROWS(10) */
- employee_id, first_name, last_name, salary
- FROM employees
- WHERE department_id = 10
- ORDER BY salary DESC;
- -- 使用ALL_ROWS提示优化整体查询性能
- SELECT /*+ ALL_ROWS */
- e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- JOIN departments d ON e.department_id = d.department_id
- WHERE e.salary > 5000;
- -- 使用LEADING提示指定连接顺序
- SELECT /*+ LEADING(e d) */
- e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- JOIN departments d ON e.department_id = d.department_id
- WHERE e.salary > 5000;
- -- 使用USE_HASH提示指定哈希连接
- SELECT /*+ USE_HASH(e d) */
- e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- JOIN departments d ON e.department_id = d.department_id
- WHERE e.salary > 5000;
复制代码
7.2 查询执行计划优化
- -- 使用SQL Trace分析查询性能
- ALTER SESSION SET SQL_TRACE = TRUE;
- -- 执行查询
- SELECT e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- JOIN departments d ON e.department_id = d.department_id
- WHERE e.salary > 5000;
- ALTER SESSION SET SQL_TRACE = FALSE;
- -- 使用TKPROF格式化跟踪文件
- -- 在操作系统命令行执行
- -- tkprof trace_file.trc output_file.txt explain=username/password
- -- 使用DBMS_XPLAN显示详细执行计划
- EXPLAIN PLAN FOR
- SELECT e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- JOIN departments d ON e.department_id = d.department_id
- WHERE e.salary > 5000;
- SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(FORMAT=>'ALL +OUTLINE'));
- -- 使用10053事件查看优化器决策过程
- ALTER SESSION SET EVENTS '10053 trace name context forever, level 1';
- -- 执行查询
- SELECT e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- JOIN departments d ON e.department_id = d.department_id
- WHERE e.salary > 5000;
- ALTER SESSION SET EVENTS '10053 trace name context off';
复制代码
7.3 SQL性能分析工具
- -- 使用SQL Tuning Advisor
- DECLARE
- l_task_name VARCHAR2(30);
- l_sql_text CLOB;
- BEGIN
- l_sql_text := 'SELECT e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- JOIN departments d ON e.department_id = d.department_id
- WHERE e.salary > 5000';
-
- l_task_name := DBMS_SQLTUNE.CREATE_TUNING_TASK(
- sql_text => l_sql_text,
- user_name => 'HR',
- scope => DBMS_SQLTUNE.SCOPE_COMPREHENSIVE,
- time_limit => 60,
- task_name => 'sql_tuning_task',
- description => 'Tuning task for a query');
-
- DBMS_SQLTUNE.EXECUTE_TUNING_TASK(task_name => 'sql_tuning_task');
- END;
- /
- -- 查看SQL Tuning Advisor的结果
- SELECT DBMS_SQLTUNE.REPORT_TUNING_TASK('sql_tuning_task') AS recommendations FROM DUAL;
- -- 使用SQL Access Advisor
- DECLARE
- l_task_name VARCHAR2(30);
- BEGIN
- l_task_name := DBMS_ADVISOR.QUICK_TUNE(
- advisor_name => DBMS_ADVISOR.SQLACCESS_ADVISOR,
- task_name => 'sql_access_task',
- attr1 => 'SELECT e.employee_id, e.first_name, e.last_name, d.department_name
- FROM employees e
- JOIN departments d ON e.department_id = d.department_id
- WHERE e.salary > 5000');
-
- DBMS_ADVISOR.EXECUTE_TASK(task_name => 'sql_access_task');
- END;
- /
- -- 查看SQL Access Advisor的结果
- SELECT DBMS_ADVISOR.GET_TASK_REPORT('sql_access_task') AS recommendations FROM DUAL;
复制代码
7.4 批量操作优化
- -- 使用BULK COLLECT进行批量获取
- DECLARE
- CURSOR emp_cursor IS
- SELECT employee_id, first_name, last_name, salary
- FROM employees
- WHERE department_id = 10;
-
- TYPE emp_array_type IS TABLE OF emp_cursor%ROWTYPE;
- emp_array emp_array_type;
- BEGIN
- OPEN emp_cursor;
- LOOP
- FETCH emp_cursor BULK COLLECT INTO emp_array LIMIT 100;
- EXIT WHEN emp_array.COUNT = 0;
-
- -- 处理批量数据
- FOR i IN 1..emp_array.COUNT LOOP
- DBMS_OUTPUT.PUT_LINE(emp_array(i).first_name || ' ' || emp_array(i).last_name ||
- ': ' || emp_array(i).salary);
- END LOOP;
- END LOOP;
- CLOSE emp_cursor;
- END;
- /
- -- 使用FORALL进行批量DML操作
- CREATE TABLE emp_temp AS SELECT * FROM employees WHERE 1=0;
- DECLARE
- CURSOR emp_cursor IS
- SELECT employee_id, first_name, last_name, salary, department_id
- FROM employees
- WHERE department_id = 10;
-
- TYPE emp_array_type IS TABLE OF emp_cursor%ROWTYPE;
- emp_array emp_array_type;
- BEGIN
- OPEN emp_cursor;
- FETCH emp_cursor BULK COLLECT INTO emp_array;
- CLOSE emp_cursor;
-
- FORALL i IN 1..emp_array.COUNT
- INSERT INTO emp_temp VALUES emp_array(i);
-
- COMMIT;
- DBMS_OUTPUT.PUT_LINE('Inserted ' || emp_array.COUNT || ' records.');
- END;
- /
- -- 使用MERGE语句进行高效的条件插入/更新
- MERGE INTO employees_target t
- USING (
- SELECT employee_id, first_name, last_name, salary, department_id
- FROM employees_source
- WHERE department_id = 10
- ) s
- ON (t.employee_id = s.employee_id)
- WHEN MATCHED THEN
- UPDATE SET
- t.first_name = s.first_name,
- t.last_name = s.last_name,
- t.salary = s.salary,
- t.department_id = s.department_id
- WHEN NOT MATCHED THEN
- INSERT (employee_id, first_name, last_name, salary, department_id)
- VALUES (s.employee_id, s.first_name, s.last_name, s.salary, s.department_id);
复制代码
8. 结论与最佳实践
Oracle数据库查询语言是企业数据管理的核心工具,从基础语法到高级优化技巧,掌握这些技能可以显著提升工作效率和解决复杂数据查询与分析问题的能力。
8.1 Oracle SQL最佳实践
1. 编写清晰可读的SQL语句:使用适当的缩进、注释和命名约定,使SQL代码易于理解和维护。
2. 合理使用索引:为经常用于WHERE子句、JOIN条件和ORDER BY子句的列创建索引,但避免过度索引。
3. 定期收集统计信息:确保数据库优化器有最新的统计信息以做出最佳执行计划决策。
4. *避免使用SELECT **:只查询实际需要的列,减少数据传输量。
5. 使用绑定变量:减少硬解析,提高SQL执行效率。
6. 合理使用分区:对于大型表,考虑使用分区技术提高查询性能。
7. 定期监控和优化:使用Oracle提供的工具定期监控SQL性能,识别并优化低效查询。
编写清晰可读的SQL语句:使用适当的缩进、注释和命名约定,使SQL代码易于理解和维护。
合理使用索引:为经常用于WHERE子句、JOIN条件和ORDER BY子句的列创建索引,但避免过度索引。
定期收集统计信息:确保数据库优化器有最新的统计信息以做出最佳执行计划决策。
*避免使用SELECT **:只查询实际需要的列,减少数据传输量。
使用绑定变量:减少硬解析,提高SQL执行效率。
合理使用分区:对于大型表,考虑使用分区技术提高查询性能。
定期监控和优化:使用Oracle提供的工具定期监控SQL性能,识别并优化低效查询。
8.2 持续学习与提升
Oracle数据库技术不断发展,新版本中引入了许多新功能和优化技术。作为Oracle SQL专业人士,应持续学习新技术,如:
• Oracle 19c/21c中的新SQL功能
• 机器学习在SQL中的应用
• 多租户架构下的SQL优化
• 云环境中的Oracle SQL性能调优
通过不断学习和实践,可以将Oracle SQL技能提升到更高水平,为企业数据管理与分析提供更强大的支持。
总之,掌握Oracle数据库查询语言从基础语法到高级优化技巧,不仅可以解决企业复杂数据查询与分析问题,还能显著提升工作效率,为企业创造更大的价值。 |
|