SQL数据操作核心技巧与实战应用指南 1. SQL数据操作基础概念与核心价值SQLStructured Query Language作为关系型数据库的标准查询语言已经存在了近50年。根据DB-Engines的最新排名SQL数据库在全部375个数据库系统中占据了前10名中的8席。这种持久不衰的流行度源于SQL在处理结构化数据时无可替代的优势。在实际工作中我发现很多开发者在初期学习SQL时容易陷入两个极端要么只满足于简单的SELECT查询要么过早钻研复杂的性能优化。而真正高效的SQL使用者往往掌握了数据操作的黄金中间地带——那些能解决80%日常需求的核心操作技巧。SQL数据操作主要涵盖四大类型CRUD创建CreateINSERT语句读取ReadSELECT语句更新UpdateUPDATE语句删除DeleteDELETE语句提示现代SQL标准如SQL:2016已经扩展了更多高级功能但CRUD操作仍然是日常工作中使用频率最高的部分。2. 环境准备与工具选型2.1 主流SQL数据库对比根据我多年使用经验不同数据库系统在SQL实现上存在细微差异数据库系统适用场景管理工具推荐学习曲线MySQL/MariaDBWeb应用、中小企业MySQL Workbench平缓PostgreSQL复杂业务、地理数据pgAdmin中等SQL ServerWindows生态、企业应用SSMS (SQL Server Management Studio)中等SQLite移动端、嵌入式系统DB Browser for SQLite简单注意虽然语法大体相同但各数据库在数据类型、函数实现上存在差异。例如MySQL的LIMIT子句在SQL Server中对应TOP关键字。2.2 SSMS安装实战问题排查在安装SQL Server Management Studio时常见问题包括SSL连接错误通常是由于TLS配置问题可尝试在连接字符串中添加TrustServerCertificatetrue排序规则冲突如遇到的utf8mb4_0900错误可通过修改安装选项或重建数据库解决Win10兼容性问题对于旧版SQL Server需要以兼容模式运行安装程序我建议新手从MySQL或SQLite开始学习它们的安装过程更为简单社区资源也更丰富。3. 核心数据操作详解3.1 数据查询的艺术一个完整的SELECT语句包含多个子句执行顺序与书写顺序不同SELECT [DISTINCT] 列名 -- 5. 最终选择显示的列 FROM 表名 -- 1. 确定数据来源 [WHERE 条件] -- 2. 行级过滤 [GROUP BY 分组列] -- 3. 分组聚合 [HAVING 分组后条件] -- 4. 组级过滤 [ORDER BY 排序列] -- 6. 结果排序 [LIMIT 数量] -- 7. 结果限制实际案例查询销售数据中销售额超过1000元的客户按地区分组统计SELECT region AS 地区, COUNT(DISTINCT customer_id) AS 客户数, SUM(amount) AS 总销售额 FROM sales WHERE amount 1000 GROUP BY region HAVING SUM(amount) 5000 ORDER BY 总销售额 DESC LIMIT 10;3.2 数据修改的陷阱与技巧3.2.1 安全的UPDATE操作UPDATE语句最常见的错误是忘记WHERE条件导致全表更新。我的安全操作守则先写成SELECT语句验证条件使用事务包裹UPDATE考虑添加LIMIT子句MySQL支持BEGIN TRANSACTION; -- 先验证 SELECT * FROM products WHERE price 10 AND stock 100; -- 再更新 UPDATE products SET price price * 1.1 WHERE price 10 AND stock 100 LIMIT 100; COMMIT;3.2.2 高效的批量INSERT单条INSERT效率极低应该使用批量插入-- 低效方式 INSERT INTO users (name, age) VALUES (张三, 25); INSERT INTO users (name, age) VALUES (李四, 30); -- 高效方式 INSERT INTO users (name, age) VALUES (张三, 25), (李四, 30), (王五, 28);对于超大批量插入万级以上建议使用LOAD DATA INFILEMySQL临时禁用索引和约束分批提交事务4. 高级数据操作技巧4.1 窗口函数的实战应用窗口函数是SQL中最强大的分析工具之一可以实现复杂的分组计算而不减少行数。典型场景包括排名计算SELECT product_id, sales, RANK() OVER (ORDER BY sales DESC) AS sales_rank, DENSE_RANK() OVER (PARTITION BY category ORDER BY sales DESC) AS category_rank FROM products;移动平均计算SELECT date, revenue, AVG(revenue) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM daily_sales;4.2 CTEWITH子句的妙用公共表表达式(CTE)可以显著提高复杂查询的可读性WITH regional_sales AS ( SELECT region, SUM(amount) AS total_sales FROM orders GROUP BY region ), top_regions AS ( SELECT region FROM regional_sales WHERE total_sales 1000000 ) SELECT r.region, p.product_name, SUM(o.quantity) AS product_units FROM orders o JOIN products p ON o.product_id p.product_id JOIN top_regions r ON o.region r.region GROUP BY r.region, p.product_name;递归CTE可以处理层级数据如组织架构或评论树WITH RECURSIVE org_tree AS ( -- 基础查询找出所有顶级部门 SELECT id, name, parent_id, 1 AS level FROM departments WHERE parent_id IS NULL UNION ALL -- 递归查询连接子部门 SELECT d.id, d.name, d.parent_id, ot.level 1 FROM departments d JOIN org_tree ot ON d.parent_id ot.id ) SELECT * FROM org_tree ORDER BY level, id;5. 性能优化与常见问题5.1 慢SQL优化实战根据我处理过的数百个性能案例80%的慢查询问题可以通过以下方法解决索引优化黄金法则为WHERE、JOIN、ORDER BY子句中的列创建索引避免在索引列上使用函数或计算多列索引遵循最左前缀原则执行计划分析EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id 123 AND order_date 2023-01-01;查询重写技巧用JOIN代替子查询避免SELECT *合理使用LIMIT分页5.2 SQL注入防御实践即使使用ORM框架SQL注入风险依然存在。我建议的防御策略参数化查询是必须的# 错误方式危险 cursor.execute(fSELECT * FROM users WHERE name {username}) # 正确方式 cursor.execute(SELECT * FROM users WHERE name %s, (username,))最小权限原则应用数据库用户只应拥有必要权限输入验证对特殊字符进行过滤或转义使用预编译语句PreparedStatement stmt conn.prepareStatement( SELECT * FROM users WHERE username ?); stmt.setString(1, username);6. 实战案例电商数据分析6.1 用户行为分析WITH user_activity AS ( SELECT user_id, COUNT(DISTINCT CASE WHEN action view THEN product_id END) AS viewed_products, COUNT(DISTINCT CASE WHEN action cart THEN product_id END) AS carted_products, COUNT(DISTINCT CASE WHEN action purchase THEN product_id END) AS purchased_products FROM user_actions WHERE action_date BETWEEN 2023-01-01 AND 2023-01-31 GROUP BY user_id ) SELECT CASE WHEN purchased_products 5 THEN 高价值用户 WHEN purchased_products 0 THEN 普通购买用户 WHEN carted_products 0 THEN 潜在用户 WHEN viewed_products 3 THEN 浏览用户 ELSE 低活跃用户 END AS user_segment, COUNT(*) AS user_count, ROUND(100.0 * COUNT(*) / SUM(COUNT(*)) OVER (), 2) AS percentage FROM user_activity GROUP BY 1 ORDER BY user_count DESC;6.2 销售漏斗分析WITH funnel AS ( SELECT product_id, COUNT(DISTINCT CASE WHEN action view THEN session_id END) AS view_sessions, COUNT(DISTINCT CASE WHEN action cart THEN session_id END) AS cart_sessions, COUNT(DISTINCT CASE WHEN action purchase THEN session_id END) AS purchase_sessions FROM product_events WHERE event_date BETWEEN 2023-01-01 AND 2023-01-07 GROUP BY product_id ) SELECT p.product_name, f.view_sessions, f.cart_sessions, f.purchase_sessions, ROUND(100.0 * f.cart_sessions / f.view_sessions, 2) AS view_to_cart_rate, ROUND(100.0 * f.purchase_sessions / f.cart_sessions, 2) AS cart_to_purchase_rate, ROUND(100.0 * f.purchase_sessions / f.view_sessions, 2) AS overall_conversion_rate FROM funnel f JOIN products p ON f.product_id p.product_id WHERE f.view_sessions 100 ORDER BY overall_conversion_rate DESC LIMIT 10;7. 数据格式化与类型转换7.1 日期时间处理不同数据库的日期函数差异较大以下是常见操作对比操作需求MySQLSQL ServerPostgreSQL当前日期CURDATE()GETDATE()CURRENT_DATE日期格式化DATE_FORMAT(date, %Y-%m)FORMAT(date, yyyy-MM)TO_CHAR(date, YYYY-MM)日期加减DATE_ADD(date, INTERVAL 1 DAY)DATEADD(day, 1, date)date INTERVAL 1 day提取日期部分YEAR(date)DATEPART(year, date)EXTRACT(YEAR FROM date)7.2 数值格式化技巧-- 四舍五入 SELECT ROUND(123.4567, 2); -- 123.46 -- 格式化货币 SELECT FORMAT(1234.56, 2); -- MySQL: 1,234.56 -- 百分比计算 SELECT CONCAT(CAST(ROUND(85.0/100*100, 2) AS DECIMAL(5,2)), %); -- 补零显示 SELECT LPAD(5, 3, 0); -- 0058. 存储过程与触发器实战8.1 高效的存储过程设计创建库存更新存储过程的示例DELIMITER // CREATE PROCEDURE update_inventory( IN p_product_id INT, IN p_quantity INT, OUT p_status VARCHAR(50) ) BEGIN DECLARE current_stock INT; DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; SET p_status Error occurred; END; START TRANSACTION; -- 检查库存 SELECT stock INTO current_stock FROM products WHERE product_id p_product_id FOR UPDATE; IF current_stock IS NULL THEN SET p_status Product not found; ROLLBACK; ELSEIF current_stock p_quantity 0 THEN SET p_status Insufficient stock; ROLLBACK; ELSE -- 更新库存 UPDATE products SET stock stock p_quantity WHERE product_id p_product_id; -- 记录日志 INSERT INTO inventory_log (product_id, change_amount, new_stock, change_time) VALUES (p_product_id, p_quantity, current_stock p_quantity, NOW()); COMMIT; SET p_status Success; END IF; END // DELIMITER ;8.2 触发器的合理使用订单审计触发器示例CREATE TRIGGER order_audit_trigger AFTER UPDATE ON orders FOR EACH ROW BEGIN IF OLD.status ! NEW.status THEN INSERT INTO order_audit ( order_id, changed_field, old_value, new_value, change_time, changed_by ) VALUES ( NEW.order_id, status, OLD.status, NEW.status, NOW(), CURRENT_USER() ); END IF; IF OLD.total_amount ! NEW.total_amount THEN INSERT INTO order_audit ( order_id, changed_field, old_value, new_value, change_time, changed_by ) VALUES ( NEW.order_id, total_amount, OLD.total_amount, NEW.total_amount, NOW(), CURRENT_USER() ); END IF; END;注意触发器虽然强大但过度使用会导致业务逻辑难以追踪。建议将核心业务逻辑放在应用层触发器仅用于审计、日志等辅助功能。9. 跨平台SQL技巧9.1 不同数据库的语法转换常见差异处理方案分页查询MySQL:LIMIT 10 OFFSET 20SQL Server:OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLYOracle: 使用ROWNUM或12c的OFFSET-FETCH字符串连接MySQL:CONCAT(str1, str2)SQL Server:str1 str2或CONCAT(str1, str2)Oracle:str1 || str2空值处理标准:COALESCE(val, default)Oracle: 也可以用NVL(val, default)9.2 使用SQL Fiddle进行跨平台测试SQL Fiddle (http://sqlfiddle.com) 是一个优秀的在线工具可以快速测试不同数据库的SQL语句。我经常用它来验证SQL语法在不同数据库中的兼容性分享可执行的示例代码调试复杂查询使用示例在左侧选择数据库类型如MySQL 5.6在Schema面板建表在SQL面板执行查询切换数据库类型验证兼容性10. 实战经验与避坑指南10.1 我踩过的五个典型SQL坑隐式类型转换-- 假设phone是varchar类型但存储的是数字 SELECT * FROM users WHERE phone 123456789; -- 会导致全表扫描应该使用 SELECT * FROM users WHERE phone 123456789;NULL值比较-- 错误方式永远返回空结果集 SELECT * FROM products WHERE discount NULL; -- 正确方式 SELECT * FROM products WHERE discount IS NULL;GROUP BY陷阱-- 在严格模式下会报错因为name不在GROUP BY中 SELECT department_id, name, AVG(salary) FROM employees GROUP BY department_id;事务隔离问题-- 在默认隔离级别下可能出现不可重复读 BEGIN; SELECT * FROM accounts WHERE user_id 1; -- 第一次读取 -- 另一个事务在此修改了数据并提交 SELECT * FROM accounts WHERE user_id 1; -- 第二次读取结果可能不同 COMMIT;索引失效场景-- 使用函数导致索引失效 SELECT * FROM orders WHERE YEAR(order_date) 2023; -- 应改为范围查询 SELECT * FROM orders WHERE order_date BETWEEN 2023-01-01 AND 2023-12-31;10.2 SQL性能优化检查清单根据我的经验总结优化SQL查询时应依次检查索引使用情况EXPLAIN分析是否使用了合适的索引检查是否存在索引未被使用的情况确认联合索引的列顺序是否合理查询结构优化避免SELECT *只查询需要的列用JOIN代替子查询在大多数情况下尽早过滤数据WHERE条件应尽早减少数据集资源消耗评估检查排序操作ORDER BY是否必要评估临时表的使用情况监控内存和CPU使用峰值执行计划分析比较不同写法的执行计划关注全表扫描、文件排序等高成本操作检查预估行数和实际行数的差异数据库配置检查缓冲池大小是否合适评估并发连接数设置确认统计信息是否最新11. 现代SQL新特性应用11.1 JSON数据处理现代数据库普遍支持JSON数据类型和相关函数-- 创建包含JSON列的表 CREATE TABLE products ( id INT PRIMARY KEY, details JSON, price DECIMAL(10,2) ); -- 插入JSON数据 INSERT INTO products VALUES ( 1, {name: Laptop, specs: {cpu: i7, ram: 16GB}, colors: [silver, black]}, 999.99 ); -- 查询JSON字段 SELECT id, details-$.name AS product_name, details-$.specs.cpu AS cpu_type, JSON_EXTRACT(details, $.colors[0]) AS primary_color FROM products WHERE details-$.specs.ram 16GB; -- 更新JSON部分内容 UPDATE products SET details JSON_SET(details, $.specs.ram, 32GB) WHERE id 1;11.2 时序数据处理针对时间序列数据的专用语法-- 时间桶聚合Time Bucketing SELECT DATE_TRUNC(hour, event_time) AS hour_bucket, COUNT(*) AS events_count, AVG(value) AS avg_value FROM sensor_data WHERE event_time BETWEEN 2023-01-01 AND 2023-01-02 GROUP BY DATE_TRUNC(hour, event_time) ORDER BY hour_bucket; -- 移动时间窗口计算 SELECT device_id, event_time, value, AVG(value) OVER ( PARTITION BY device_id ORDER BY event_time RANGE BETWEEN INTERVAL 15 minutes PRECEDING AND CURRENT ROW ) AS moving_avg FROM sensor_data;12. SQL在数据工程中的应用12.1 ETL管道中的SQL技巧在构建数据管道时我常用的高效模式增量抽取-- 记录最后处理的时间戳 DECLARE last_etl_time DATETIME (SELECT MAX(etl_time) FROM etl_log WHERE table_name orders); -- 增量抽取 INSERT INTO dw_orders SELECT * FROM orders WHERE update_time last_etl_time AND update_time CURRENT_TIMESTAMP; -- 更新日志 INSERT INTO etl_log (table_name, etl_time) VALUES (orders, CURRENT_TIMESTAMP);缓慢变化维(SCD)处理-- Type 2 SCD实现 INSERT INTO dim_customer SELECT s.customer_id, s.customer_name, s.email, s.current_address, Y AS is_current, CURRENT_DATE AS valid_from, NULL AS valid_to, CURRENT_TIMESTAMP AS etl_time FROM stage_customer s LEFT JOIN dim_customer d ON s.customer_id d.customer_id AND d.is_current Y WHERE d.customer_id IS NULL -- 新增记录 OR (d.customer_id IS NOT NULL AND ( s.customer_name d.customer_name OR s.email d.email OR s.current_address d.current_address )); -- 变更记录 -- 对变更记录更新历史版本 UPDATE dim_customer SET is_current N, valid_to CURRENT_DATE - 1 WHERE customer_id IN ( SELECT customer_id FROM stage_customer s JOIN dim_customer d ON s.customer_id d.customer_id WHERE d.is_current Y AND ( s.customer_name d.customer_name OR s.email d.email OR s.current_address d.current_address ) ) AND is_current Y;12.2 数据质量检查SQL我常用的数据质量检查模式-- 完整性检查 SELECT employees AS table_name, COUNT(*) AS total_rows, SUM(CASE WHEN employee_id IS NULL THEN 1 ELSE 0 END) AS missing_ids, SUM(CASE WHEN hire_date IS NULL THEN 1 ELSE 0 END) AS missing_dates FROM employees; -- 一致性检查 SELECT department_id, COUNT(*) AS emp_count, AVG(salary) AS avg_salary, MIN(salary) AS min_salary, MAX(salary) AS max_salary FROM employees GROUP BY department_id HAVING AVG(salary) 1000 OR MAX(salary) 100000; -- 异常阈值 -- 唯一性检查 SELECT email, COUNT(*) AS dup_count FROM customers GROUP BY email HAVING COUNT(*) 1;13. SQL与其他工具的集成13.1 Excel Power Query中的SQL在Excel中使用SQL查询数据的技巧连接数据库数据 → 获取数据 → 从数据库选择SQL Server/MySQL等数据源编写参数化查询-- 在Power Query中使用参数 SELECT * FROM orders WHERE order_date BETWEEN StartDate AND EndDate处理大数据集启用延迟加载选项使用WHERE子句预先过滤考虑只导入汇总数据而非明细13.2 Python与SQL的协作我常用的Python SQL操作模式import sqlite3 from contextlib import closing # 使用连接池和上下文管理器 def get_orders(start_date, end_date): with closing(sqlite3.connect(app.db)) as conn: conn.row_factory sqlite3.Row # 启用字典式访问 with closing(conn.cursor()) as cursor: cursor.execute( SELECT order_id, customer_id, total_amount FROM orders WHERE order_date BETWEEN ? AND ? ORDER BY order_date , (start_date, end_date)) return [dict(row) for row in cursor.fetchall()] # 使用Pandas进行数据分析 import pandas as pd def get_sales_report(): conn sqlite3.connect(app.db) df pd.read_sql( SELECT strftime(%Y-%m, order_date) AS month, product_category, SUM(quantity) AS total_quantity, SUM(total_amount) AS revenue FROM orders JOIN products ON orders.product_id products.product_id GROUP BY month, product_category ORDER BY month, revenue DESC , conn) conn.close() return df.pivot_table(indexmonth, columnsproduct_category, valuesrevenue)14. SQL学习路径与资源推荐14.1 分阶段学习建议根据我带团队的经验建议这样分阶段掌握SQL基础阶段1-2周掌握SELECT查询的所有子句熟练使用JOIN连接多表理解GROUP BY聚合原理学会INSERT/UPDATE/DELETE操作中级阶段2-4周掌握子查询和CTE理解事务和锁机制学习索引原理与优化熟悉常用函数日期、字符串、数值高级阶段持续精进窗口函数高级应用执行计划分析与优化存储过程与触发器开发特定数据库的高级特性14.2 优质学习资源我亲自验证过的优质资源免费资源SQLZoo (https://sqlzoo.net/) - 交互式学习平台PostgreSQL官方文档非常全面MySQL官方文档的Examples部分付费课程Udemy的The Complete SQL BootcampLinkedIn Learning的SQL Essential Training实战平台LeetCode数据库题库HackerRank的SQL挑战StrataScratch真实业务场景题目参考书籍《SQL必知必会》- 适合入门《SQL进阶教程》- 深入窗口函数等高级特性《高性能MySQL》- 专注MySQL优化15. 面试常见SQL问题解析15.1 典型面试题实战排名问题 找出每个部门薪资排名前三的员工WITH ranked_employees AS ( SELECT employee_id, name, department, salary, DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank FROM employees ) SELECT * FROM ranked_employees WHERE dept_rank 3;连续登录问题 找出连续登录超过5天的用户WITH login_groups AS ( SELECT user_id, login_date, DATE_SUB(login_date, INTERVAL ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) DAY) AS grp FROM user_logins GROUP BY user_id, login_date ) SELECT user_id, MIN(login_date) AS start_date, MAX(login_date) AS end_date, COUNT(*) AS days_count FROM login_groups GROUP BY user_id, grp HAVING COUNT(*) 5;留存率计算 计算新用户7日留存率WITH first_logins AS ( SELECT user_id, MIN(login_date) AS first_date FROM user_logins GROUP BY user_id ), retained_users AS ( SELECT fl.first_date, COUNT(DISTINCT fl.user_id) AS new_users, COUNT(DISTINCT ul.user_id) AS retained_users FROM first_logins fl LEFT JOIN user_logins ul ON fl.user_id ul.user_id AND ul.login_date DATE_ADD(fl.first_date, INTERVAL 7 DAY) GROUP BY fl.first_date ) SELECT first_date, new_users, retained_users, ROUND(100.0 * retained_users / new_users, 2) AS retention_rate FROM retained_users ORDER BY first_date;15.2 面试准备建议根据我作为面试官的经验候选人应该准备3-5个你解决过的复杂SQL问题案例理解你写的每个SQL语句的执行计划熟悉至少一种数据库的特有功能准备关于索引、事务隔离级别等理论问题练习在白板上手写SQL有些公司仍采用这种方式16. SQL在特殊场景中的应用16.1 递归查询处理层级数据处理组织架构、评论树等层级数据的递归CTE模式WITH RECURSIVE org_hierarchy AS ( -- 基础查询选择顶级节点 SELECT id, name, parent_id, 1 AS level, CAST(name AS VARCHAR(1000)) AS path FROM departments WHERE parent_id IS NULL UNION ALL -- 递归查询连接子节点 SELECT d.id, d.name, d.parent_id, oh.level 1, CAST(CONCAT(oh.path, , d.name) AS VARCHAR(1000)) AS path FROM departments d JOIN org_hierarchy oh ON d.parent_id oh.id ) SELECT id, REPEAT( , level - 1) || name AS tree_display, level, path FROM org_hierarchy ORDER BY path;16.2 图数据分析使用SQL处理朋友关系等图数据-- 找出共同朋友 SELECT u1.user_id AS user1, u2.user_id AS user2, COUNT(f.common_friend) AS mutual_friends FROM users u1 JOIN friends f1 ON u1.user_id f1.user_id JOIN friends f2 ON f1.friend_id f2.friend_id AND f2.user_id u1.user_id JOIN users u2 ON f2.user_id u2.user_id LEFT JOIN friends f ON f.user_id u1.user_id AND f.friend_id u2.user_id WHERE f.user_id IS NULL -- 排除已经是朋友的关系 GROUP BY u1.user_id, u2.user_id HAVING COUNT(f1.friend_id) 3 ORDER BY mutual_friends DESC;17. 数据库设计与SQL优化17.1 规范化与反规范化规范化设计-- 符合3NF的设计 CREATE TABLE users ( user_id INT PRIMARY KEY, username VARCHAR(50) UNIQUE, email VARCHAR(100) UNIQUE ); CREATE TABLE orders ( order_id INT PRIMARY KEY, user_id INT REFERENCES users(user_id), order_date DATETIME ); CREATE TABLE order_items ( item_id INT PRIMARY KEY, order_id INT REFERENCES orders(order_id), product_id INT REFERENCES products(product_id), quantity INT, price DECIMAL(10,2) );合理反规范化-- 为提高查询性能适度反规范化 CREATE TABLE order_summary ( order_id INT PRIMARY KEY, user_id INT, username VARCHAR(50), order_date DATETIME, total_amount DECIMAL(12,2), item_count INT, INDEX (user_id), INDEX (order_date) );17.2 分区表实战大数据量表的分区策略示例-- 按范围分区时间序列数据 CREATE TABLE sensor_data ( sensor_id INT, record_time DATETIME, value DECIMAL(10,2), PRIMARY KEY (sensor_id, record_time) ) PARTITION BY RANGE (TO_DAYS(record_time)) ( PARTITION p202201 VALUES LESS THAN (TO_DAYS(2022-02-01)), PARTITION p202202 VALUES LESS THAN (TO_DAYS(2022-03-01)), PARTITION pmax VALUES LESS THAN MAXVALUE ); -- 按列表分区离散值 CREATE TABLE sales ( sale_id INT PRIMARY KEY, region VARCHAR(20), amount DECIMAL(12,2) ) PARTITION BY LIST (region) ( PARTITION p_north VALUES IN (Beijing, Tianjin, Hebei), PARTITION p_east VALUES IN (Shanghai, Jiangsu, Zhejiang), PARTITION p_other VALUES IN (DEFAULT) );18. SQL在数据安全中的应用18.1 行级安全控制PostgreSQL的行级安全策略示例-- 启用行级安全 ALTER TABLE customer_data ENABLE ROW LEVEL SECURITY; -- 创建策略销售人员只能看到自己区域的客户 CREATE POLICY sales_region_policy ON customer_data TO sales_role USING (region current_setting(app.current_region)); -- 经理可以看到所有客户但无法看到敏感字段 CREATE VIEW customer_list AS SELECT customer_id, name, region, CASE WHEN current_setting(app.current_role) manager THEN email ELSE ***** END AS email FROM customer_data;18.2 数据脱敏技术-- 创建脱敏视图 CREATE VIEW masked_customers AS SELECT customer_id, CONCAT( LEFT(name, 1), REPEAT(*, LENGTH(name) - 1) ) AS name, CONCAT( ****-****-****-, RIGHT(credit_card, 4) ) AS credit_card_masked, CASE WHEN YEAR(CURRENT_DATE) - YEAR(birth_date) 18 THEN NULL ELSE birth_date END AS birth_date FROM customers; -- 使用函数动态脱敏 CREATE FUNCTION get_customer_email(p_customer_id INT, p_requester_role VARCHAR(20)) RETURNS VARCHAR(100) BEGIN DECLARE v_email VARCHAR(100); SELECT email INTO v_email FROM customers WHERE customer_id p_customer_id; IF p_requester_role admin THEN RETURN v_email; ELSE RETURN CONCAT( LEFT(v_email, 3), ****, SUBSTRING_INDEX(v_email, , -1) ); END IF; END;19. SQL与NoSQL的协作模式19.1 混合数据模型设计关系型与文档型的协作示例-- 关系型表存储核心业务数据 CREATE TABLE products ( product_id INT PRIMARY KEY, name VARCHAR(100), base_price DECIMAL(10,2) ); -- JSON列存储可变属性 CREATE TABLE product_attributes ( product_id INT PRIMARY KEY REFERENCES products(product_id), attributes JSON, INDEX ((CAST(attributes-$.color AS CHAR(20)))), INDEX ((CAST(attributes-$.size AS CHAR(10)))) ); -- 查询示例 SELECT p.product_id, p.name, p.base_price, pa.attributes-$.color AS color,