擦亮自己的眼睛去看SQLServer之简单Select
擦亮自己的眼睛去看SQLServer之简单Select很多初学者甚至有一定经验的开发者在写SQL Server的SELECT语句时往往只停留在“能查出数据就行”的层面。但实际生产环境中一条看似简单的SELECT背后可能隐藏着性能陷阱、逻辑错误甚至数据一致性问题。今天我们就用实战代码来“擦亮眼睛”重新审视这个最基础的查询语句。### 一、你真的会写SELECT吗—— 基础语法中的“暗坑”大多数人都知道SELECT * FROM Table但这里的*在SQL Server里可能是个“定时炸弹”。它不仅会返回所有列导致不必要的I/O和网络传输还会在表结构变更时产生不可预知的结果。我们先看一个反例sql-- 反例生产环境千万不要这样写SELECT * FROM Sales.Orders WHERE OrderDate 2023-01-01;如果这张表有20个字段但你只需要OrderID和CustomerID那么每次查询都会多出18个字段的传输开销。更严重的是如果以后有人给表加了Memo字段比如一个NVARCHAR(MAX)你的查询会瞬间变慢甚至导致应用程序内存溢出。正确的做法是显式列出字段sql-- 正例只取所需字段SELECT OrderID, CustomerID, OrderDateFROM Sales.Orders WHERE OrderDate 2023-01-01;### 二、SELECT的执行顺序 —— 逻辑与物理的“双重人格”很多开发者以为SELECT是从上到下执行的其实SQL Server的查询优化器会按照特定逻辑顺序处理但最终生成的物理执行计划可能完全不一样。我们来看一个经典案例sql-- 演示逻辑执行顺序SELECT CustomerID, COUNT(*) AS OrderCountFROM Sales.OrdersWHERE OrderDate 2023-01-01GROUP BY CustomerIDHAVING COUNT(*) 5ORDER BY OrderCount DESC;逻辑顺序概念上1.FROM子句确定数据源包括JOIN2.WHERE子句过滤行3.GROUP BY子句分组4.HAVING子句过滤分组5.SELECT子句计算表达式6.ORDER BY子句排序但物理执行计划可能完全不同——如果OrderDate上有索引优化器可能会先通过索引查找获取符合条件的行再在内存中分组聚合最后才排序。所以你写的SQL只是告诉优化器“你想要什么”而不是“怎么做”。实战技巧在SSMS中按CtrlM开启“实际执行计划”你会发现SELECT语句的执行顺序可能跟你想的完全不一样。比如如果HAVING条件能下推到WHERE优化器会提前过滤减少分组的数据量。### 三、SELECT与NULL的“爱恨情仇”NULL是SQL世界里最容易出错的点。很多人以为WHERE Field NULL能查出空值结果却总是空集。我们来写一段代码演示这个坑sql-- 演示NULL比较的陷阱DECLARE table TABLE (ID INT, Name NVARCHAR(50));INSERT INTO table VALUES (1, Alice), (2, NULL), (3, Bob);-- 错误的写法查不出任何记录SELECT * FROM table WHERE Name NULL;-- 正确的写法使用 IS NULLSELECT * FROM table WHERE Name IS NULL;-- 更隐蔽的错误NOT IN 遇到 NULLSELECT * FROM table WHERE ID NOT IN (SELECT ID FROM table WHERE Name Bob OR Name NULL);-- 上面这条语句会返回空集因为子查询中出现了 NULL导致整个 NOT IN 条件为 UNKNOWN真正的坑在于NOT IN当子查询结果集中包含NULL时NOT IN会返回空集。这个错误在真实项目中经常出现比如“查询没有订单的客户”如果Orders表中存在NULL的客户ID结果会完全错误。正确替代方案sqlSELECT * FROM table tWHERE NOT EXISTS (SELECT 1 FROM table WHERE ID t.ID AND Name Bob);### 四、SELECT TOP与排序的“优先级陷阱”SELECT TOP是SQL Server里常用的分页或限制条数的语法但很多人不知道TOP与ORDER BY的执行顺序。看个例子sql-- 演示TOP与ORDER BY的坑CREATE TABLE #Temp (ID INT, Score INT);INSERT INTO #Temp VALUES (1, 80), (2, 90), (3, 70), (4, 95);-- 正确的先排序再取前2条SELECT TOP 2 ID, Score FROM #Temp ORDER BY Score DESC;-- 结果4(95)和2(90)-- 错误的先取前2条再排序但SQL Server语法上会强制先排序所以这个例子不会出错-- 但如果你写成SELECT TOP 2 ID, Score FROM #Temp; 没有ORDER BY那么取哪两条是随机的SELECT TOP 2 ID, Score FROM #Temp;-- 结果可能不是1和2而是任意两条实战警示如果没有ORDER BYTOP返回的行是不确定的。这在分页场景中会导致数据重复或遗漏。比如你在做“排行榜”查询如果不加ORDER BY每次刷新可能显示不同的人。### 五、SELECT性能优化从“全表扫描”到“索引查找”我们用一个真实场景来演示如何通过改写SELECT来提升性能。假设有一张OrderItems表有500万行数据我们要查询某个产品的所有订单sql-- 低效写法函数包裹索引列导致索引失效SELECT OrderID, Quantity, UnitPriceFROM Sales.OrderItemsWHERE YEAR(OrderDate) 2023 AND ProductID 100;这里YEAR(OrderDate)会让索引失效因为SQL Server无法对函数处理后的列使用索引。正确写法是使用范围条件sql-- 高效写法使用范围查询利用索引SELECT OrderID, Quantity, UnitPriceFROM Sales.OrderItemsWHERE OrderDate 2023-01-01 AND OrderDate 2024-01-01AND ProductID 100;更进一步的优化如果经常查询ProductID OrderDate的组合建议创建复合索引sqlCREATE NONCLUSTERED INDEX IX_OrderItems_Product_Date ON Sales.OrderItems(ProductID, OrderDate)INCLUDE (Quantity, UnitPrice);这样查询可以直接走索引查找避免回表。### 六、SELECT的“隐藏能力”CTE与窗口函数很多人把SELECT只当作“查数据”其实它还能配合WITH子句CTE和窗口函数做复杂分析且比临时表更高效。看下面的例子sql-- 演示用CTE 窗口函数实现“每个客户最近一笔订单”WITH RankedOrders AS ( SELECT CustomerID, OrderID, OrderDate, ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderDate DESC) AS rn FROM Sales.Orders)SELECT CustomerID, OrderID, OrderDateFROM RankedOrdersWHERE rn 1;这条语句避免了多次扫描Orders表逻辑清晰且性能优于CROSS APPLY或子查询。但注意窗口函数在SQL Server 2005才支持如果你还在维护老版本得用ROW_NUMBER替代方案。### 七、实战案例一个“简单”SELECT引发的性能事故我曾在生产环境遇到一个案例一条SELECT查询耗时20秒导致前端超时。原始SQL如下sqlSELECT * FROM Orders oLEFT JOIN Customers c ON o.CustomerID c.CustomerIDWHERE c.City 上海;问题分析1.SELECT *返回了所有列包括Orders表的大字段。2.LEFT JOIN其实不需要因为WHERE过滤了c.City这实际上变成了INNER JOIN。3.c.City上没有索引导致全表扫描。优化后sqlSELECT o.OrderID, o.OrderDate, c.CustomerName, c.CityFROM Orders oINNER JOIN Customers c ON o.CustomerID c.CustomerIDWHERE c.City 上海;并在Customers(City, CustomerID)上创建复合索引。最终查询耗时从20秒降到0.2秒。### 总结“简单Select”并不简单它背后涉及逻辑顺序、NULL处理、性能优化、索引使用、查询重写等多个层面。作为全栈工程师我们不能只满足于“能跑”而要“跑得对、跑得快”。记住几个关键点-显式列出字段避免SELECT *。-理解逻辑执行顺序但也要看实际执行计划。-警惕NULL特别是NOT IN和 NULL。- **TOP必须配合ORDER BY**才能稳定。-避免在索引列上使用函数。-善用CTE和窗口函数替代复杂子查询。最后建议你在SSMS中开启“实际执行计划”和“客户端统计”每次写SELECT都观察它如何执行。只有不断擦亮眼睛才能写出既正确又高效的SQL语句。

相关新闻

密码合规校验:从GESP真题到工程实践的设计与优化

密码合规校验:从GESP真题到工程实践的设计与优化

1. 项目概述:从一道题看密码合规的实战逻辑最近在整理GESP(图形化编程能力等级认证)的历年真题时,2023年6月三级的那道“密码合规”题让我印象挺深。这道题本身难度不算大,但它的内核——对一串密码进行多重规则校验—…

2026/10/7 14:07:23 阅读更多 →
C语言扫雷游戏开发:从基础实现到性能优化

C语言扫雷游戏开发:从基础实现到性能优化

1. C语言二刷强化:基础扫雷实践与拓展作为一名有十年C语言开发经验的程序员,我始终认为"二刷"经典项目是突破技术瓶颈的最佳方式。扫雷游戏作为C语言入门的经典案例,看似简单却蕴含着内存管理、算法逻辑和交互设计的核心思想。这次…

2026/10/1 0:14:49 阅读更多 →
TVS瞬态电压抑制二极管选型实战指南:从参数解析到应用场景

TVS瞬态电压抑制二极管选型实战指南:从参数解析到应用场景

1. 项目概述:从“TVS参数”到“选型对比”的实战闭环如果你在电路设计或者硬件维护中,听到过“TVS管又烧了”、“端口被静电打坏了”这类抱怨,那么“TVS参数、选型、对比”这个话题对你来说就绝不是纸上谈兵。TVS,瞬态电压抑制二极…

2026/9/22 13:53:37 阅读更多 →

最新新闻

C盘爆红不用怕:C盘搬家工具的原理与实操指南

C盘爆红不用怕:C盘搬家工具的原理与实操指南

前一阵子帮同事收拾电脑,那台办公机的C盘常年飘红,右键刷新都能卡上两秒,系统更新更是装一次失败一次。最离谱的是连系统时间都开始偶尔走偏,查了一圈才发现根因是磁盘满到连必要的临时文件都落不了地。这其实不是个例&#xff0c…

2026/10/11 2:26:02 阅读更多 →
船舶数据集VOC转YOLO格式与YOLOv8训练避坑指南

船舶数据集VOC转YOLO格式与YOLOv8训练避坑指南

简介:针对船舶目标检测场景的YOLO系列数据集已封装为zip包,面向目标检测算法学习者和开发者,覆盖充气船、独木舟等类别的1088张带标签图像,适合海事监控、水上交通等场景的模型训练与实验。包内共有2000个文件:1063个x…

2026/10/11 2:26:02 阅读更多 →
ESP32-S31一键烧录包制作:镜像合并与数据边界保护实战

ESP32-S31一键烧录包制作:镜像合并与数据边界保护实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 2:26:02 阅读更多 →
基于PyTorch的CNN火焰识别项目实战:从数据集到PyQt界面

基于PyTorch的CNN火焰识别项目实战:从数据集到PyQt界面

简介:本资源面向具备一定Python与深度学习基础、希望快速上手图像分类实战的开发者与学习者,提供一套基于PyTorch框架的CNN火焰识别完整方案,可用于火灾预警、安防监控等场景的算法验证与二次开发。压缩包共302个文件,以288张jpg与…

2026/10/11 2:26:02 阅读更多 →
告别鼠标:系统整理高效快捷键思维与实操技巧

告别鼠标:系统整理高效快捷键思维与实操技巧

你们有没有遇到过这种场面:旁边同事噼里啪啦一顿操作,窗口切换、文件另存、表格求和,鼠标基本不碰,几分钟搞定你磨蹭半小时的活儿。你不好意思问,只能默默盯着屏幕,心想这人是不是开了什么外挂。其实没啥神…

2026/10/11 2:26:02 阅读更多 →
二手车交易数据分析与可视化:从清洗到交互看板的完整实践

二手车交易数据分析与可视化:从清洗到交互看板的完整实践

简介:二手车交易数据分析与可视化系统是一份面向数据分析学习者与前端开发者的综合实战资源。项目整合网络爬虫、前后端分离架构、MySQL数据库存储以及Pandas/NumPy数据分析流程,最终通过Echarts、Plotly等交互式图表呈现二手车价格、里程与市场趋势&…

2026/10/11 2:25:01 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →