第3章关系数据库标准语言SQL

数据库查询语言SQL:是关系数据库的标准语言。(功能包括查询,数据库模式的创建,数据库数据的插入、修改,数据库安全性完整性定义与控制)
3.1SQL概述
*SQL的特点:
- 综合统一
SQL集数据定义语言(DDL)、数据操纵语言(DML)、数据控制语言(DCL)功能于一体
- 高度非过程化
SQL进行数据操作时,只要提出“做什么”,而无需指明“怎么做”,因此无需了解存取路径。
- 面向集合的操作方式
SQL采用集合操作方式
- 以同一种语法结构提供多种使用方式
SQL即是独立的语言,又是嵌入式语言
- 语言简洁,易学易用
核心功能只用9个动词

SQL的基本概念:
支持SQL的DBSM同样支持关系数据库的三级模式结构

基本表:本身独立存在的表,DBSM中一个关系对应一个基本表
存储文件:存储文件的逻辑结构组成了关系数据库的内模式
视图:从一个或几个基本表导出的表,本身不独立存储在数据库中,是虚表(数据库只存放视图的定义而不存放视图对应的数据)
3.2学生-课程数据库
略,翻书P79、80看那几张表
3.3数据定义

SQl标准不提供修改模式定义和视图定义的操作,若想修改,只能先删除再重建
一个DBSM的实例中可以建立多个数据库,一个数据库中可以建立多个模式,一个模式下通常包括多个表、视图和索引等数据库对象

模式的定义与删除:
- 定义模式
SQL中定义模式语句为:CREATE SCHEMA <模式名> AUTHORIZATION <用户名>;
若没有指定<模式名>
,那么<模式名>
隐含为<用户名>
;若找不到<用户名>
对应的用户,则会报错
定义模式实际上定义了一个命名空间,在这个空间中可以进一步定义该模式包含的数据库对象(基本表、视图、索引等)
即CREATE SCHEMA <模式名> AUTHORIZATION <用户名> [<表定义子句>|<视图定义子句>|<授权定义子句>];
例:为用户ZHANG创建一个模式TEST,并且在其中定义一个表TAB1
| CREATE SCHEMA TEST AUTHORIZATION ZHANG //无此行则默认在当前用户建立 |
| CREATE TABLE TAB1(COL1 SMALLINT, |
| COL2 INT, |
| COL3 CHAR(20), |
| COL4 NUMERIC(10,3), |
| COL5 DECIMAL(5,2) |
| ); |
- 删除模式
SQL中删除模式语句为:DROP SCHEMA <模式名> <CASCADE|RESTRICT>;
其中CASCADE
和RESTRICT
两者必选其一。
选择CASCADE
(级联),表示删除模式的同时把该模式中所有的数据库对象全部删除
选择RESTRICT
(限制),表示如果该模式中已经定义了下属的数据库对象(表、视图等),则拒绝该删除语句的执行,只有该模式中没有任何下属的对象时才能执行DROP SCHEMA
语句
表的定义、删除与修改:
- 定义基本表,使用
CREATE TABLE
语句
列级完整性约束条件可以有PRIMARY KEY
(主码)、UNIQUE
(取唯一值,相当于候选码)、NOT NULL
(不能为空)等
表级完整性约束条件可以有FOREIGN KEY(<外码>) REFERENCE <被参照表>(<被参照列>)
(定义外码)、PRIMARY KEY(<属性1>, <属性2>,...)
(定义两个属性以上组成的主码)
| CREATE TABLE <表名> (<列名> <数据类型> [列级完整性约束条件] |
| [, <列名> <数据类型> [列级完整性约束条件]] |
| ... |
| [, <表级完整性约束条件>]); |
| //方法一,在表名中显式给出模式名 |
| CREATE TABLE "<模式名>".<表名>(...); |
| |
| //方法二,在创建模式语句中同时创建表 |
| CREATE SCHEMA <模式名> AUTHORIZATION <用户名> |
| [<表定义子句>|<视图定义子句>|<授权定义子句>]; |
| |
| //方法三,设置所属的模式,再创建表(不必给出模式名) |
| SET search path TO "...", PUBLIC; |
| CREATE TABLE <表名>(...); |
- 修改基本表
SQL中使用ALTER TABLE
语句
| ALTER TABLE <表名> |
| [ADD [COLUMN] <新列名> <数据类型> [完整性约束]] |
| [ADD <表级完整性约束>] |
| [DROP [COLUMN] <列名> [CASCADE | RESTRICT]] |
| [DROP CONSTRAINT <完整性约束名> [RESTRICT | CASCADE]] |
| [ALTER COLUMN <列名> <数据类型>]; |
| DROP TABLE <表名> [RESTRICT | CASCADE]; |
索引的建立与删除:
索引能加快查询速度,但索引占用一定的存储空间,过多的索引会增加数据库的负担
*常见的索引包括:顺序文件上的索引、B+树的索引、散列索引、位图索引
| CREATE [UNIQUE] [CLUSTER] INDEX > <索引名> |
| ON <表名> (<列名> [<次序>] [, <列名> [<次序>]] ...); |
| |
| |
| |
| |
| |
| |
| ALTER INDEX <旧索引名> RENAME TO <新索引名>; |
| |
3.4数据查询
| SELECT [ALL | DISTINCT] <目标列表达式> [,<目标列表达式>] ... |
| FROM <表名或视图名> [, <表名或视图名> ...] | (<SELECT语句>) [AS] <别名> |
| [WHERE <条件表达式>] |
| [GROUP BY <列名1> [<HAVING <条件表达式>]] |
| [ORDER BY <列名2> [ASC | DESC]]; |
| /* |
| FROM 指定基本表/视图/派生表 |
| WHERE 从中找到满足条件的元组 |
| SELECT 选出元组中的属性值形成结果表 DISTINCT 在此基础上消除重复行(不添加默认为ALL) |
| GROUP BY 按<列名1>的值进行分组(值相等分为一组) HAVING 在此基础上挑选出符合条件的组 |
| ORDER BY 结果表按<列名2>的值进行升序或降序排列 |
| */ |
单表查询
| |
| SELECT <目标列表达式> [,<目标列表达式>] ... |
| ... |
| |
| |
| SELECT * |
| ... |
| |
| |
| 与查询指定列类似,<目标列表达式>可以是表达式(比如数值+-x÷、列名小写LOWER(<列名>)、修改列名(空格+想改的名字)) |
| 操作不会改变原数据,只会改变结果表 |
| //消除取值重复的行(使用DISTINCT) |
| SELECT DISTINCT <目标列表达式> [,<目标列表达式>] ... |
| ... |
| |
| //查询满足条件的元组(使用WHERE) |
| ... |
| WHERE <列名> <比较运算符> <比较的值> //比较大小 |
| |
| WHERE [NOT] BETWEEN ... AND //在(不在)某一范围 |
| |
| WHERE <列名> [NOT] IN (<属性值> [,<属性值>] ...) //属性值属于指定集合 |
| |
| WHERE <列名> [NOT] LIKE '<匹配串>' [ESCAPE '<换码字符>'] //匹配字符串 |
| WHERE <列名> = '<匹配串>' //匹配无通配符的字符串 |
| |
| |
| |
| |
| |
| WHERE <列名> IS [NOT] NULL //查询空值(非空值) |
| |
| WHERE <查询条件> [OR|AND <查询条件>] ... //连接多个查询条件 |

| //默认是升序ASC,降序要手动声明DESC |
| //排列时与属性的声明的顺序有关,先排前面的属性的值,再排后面的属性的值(前面的属性值相同时) |
| ORDER BY <属性名> [ASC | DESC] [,<属性名> [ASC | DESC] ] |
- 聚集函数
聚集函数用来方便用户检索
聚集函数遇到空值时,除COUNT (*)
外,都跳过空值而只处理非空值
聚集函数只能用于SELECT
子句和GROUP BY
中的HAVING
子句
| COUNT(*) |
| COUNT( [DISTINCT | ALL] <列名>) |
| SUM( [DISTINCT | ALL] <列名>) |
| AVG( [DISTINCT | ALL] <列名>) |
| MAX( [DISTINCT | ALL] <列名>) |
| MIN( [DISTINCT | ALL] <列名>) |
| |
| |
| |
- GROUP BY子句
GROUP BY子句将查询结果按某一列或多列的值分组,值相等的为一组
分组的目的是细化聚集函数的作用对象
分组后聚集函数将作用于每一个组,即每一个组都有一个函数值
WHERE
子句与HAVING
短语的区别是作用对象不同
WHERE
子句作用于基本表或视图
HAVING
短语作用于组
| GROUP BY <列名> [,<列名>]... |
| [HAVING <条件表达式(可用聚集函数)>] |
连接查询
连接查询:一个查询同时涉及两个以上的表
- 等值与非等值连接查询
连接条件/连接谓词:连接查询的WHERE
子句中用来连接两个表的条件
连接谓词中的列名称为连接字段,各连接字段的类型必须是可比的
把目标列中重复的属性列去掉则为自然连接
| |
| SELCTE [<表名>.]<列名> [,[<表名>.]<列名>]... |
| |
| |
| SELCT * |
| |
| FROM .... |
| |
| |
| |
| |
| WHERE [<表名1>.]<列名1> <比较运算符> [<表名2>.]<列名2> |
| |
| |
| WHERE [<表名1>.]<列名1> BETWEEN [<表名2>.]<列名2> AND [<表名3>.]<列名3> |
| 一般要对同一个表进行不同的命名来实现自身连接 |
| |
| 例: |
| SELECT FIRST.Cno, SECOND.Cpno |
| FROM Course FIRST,Course SECOND |
| WHERE FIRST.Cpno=SECOND.Cno; |
- 外连接
把悬浮元组保存在结果关系中,没有值的属性填上NULL
| |
| FORM <表名> LEFT OUTER JOIN SC ON (<连接>) |
| |
| |
| FORM <表名> RIGHT OUTER JOIN SC ON (<连接>) |
| |
| |
| FORM <表名> ALL OUTER JOIN SC ON (<连接>) |
| 例: |
| SELECT ... |
| FROM Student,SC,Course |
| WHERE ... |
| //过程是先将Student与SC连接,结果表再与Course连接 |
嵌套查询
一个SELECT-FROM-WHERE
语句称为一个查询块
嵌套查询:将一个查询块嵌套在另一个查询块的WHERE
子句或HAVING
短语的条件中的查询
SQL允许多层嵌套查询
子查询的SELECT
语句中不能使用ORDER BY
子句,ORDER BY
子句只能对最终查询结果排序
| 先分步来完成此查询,然后再构造嵌套查询 |
| 例: |
| |
| |
| |
| SELCET Sno,Sname,Sdept |
| FROM Student |
| WHERE Sdept IN( |
| SELECT Sdept |
| FROM Student |
| WHERE Sname='刘晨' |
| ) |
| |
| |
| |
| SELCET Sno,Sname,Sdept |
| FROM Student S1, Student S2 |
| WHERE S1.Sdept=S2.Sdept AND S2.Sname='刘晨' |
| 例: |
| //找出每个学生超过他自己选修课程平均成绩的课程号 |
| SELECT Sno,Cno |
| FROM SC x |
| WHERE Grade >=( |
| SELECTE AVG(Grade) |
| FROM SC y |
| WHERE y.Sno=x.Sno |
| ); |
| //子查询的查询条件依赖于父查询,这类查询称为相关子查询(x.Sno与父查询有关) |
| //内层查询每次从外层查询的x中取出一个元组进行WHERE子句的连接 |
| |
| //求解相关子查询不能像求解不相关子查询那样一次将子查询求解出来,然后求解父查询 |
| //内层查询由于与外层查询有关,因此必须反复求值 |
- 带有ANY(SOME)或ALL谓词的子查询
子查询返回单值可用比较运算符,返回多值必须要用ANY(有些系统用SOME)或ALL,使用ANY或ALL时必须同时使用比较运算符
ANY(SOME):查询结果中存在某个值满足比较条件即为TRUE,否则为FALSE
ALL:所有查询结果都满足比较条件才为TRUE,否则为FALSE
一般的ANY和ALL都能用聚集函数代替

| 例: |
| //查询非计算机系中比计算机系任意一个学生年龄小的学生姓名和年龄 |
| //使用ANY |
| SELECT Sname,Sage |
| FROM Student |
| WHERE Sage < ANY( |
| SELECTE Sage |
| FROM Student |
| WHERE Sdept='CS' |
| ); |
| |
| //等价的聚集函数写法 |
| SELECT Sname,Sage |
| FROM Student |
| WHERE Sage < ( |
| SELECTE MAX(Sage) |
| FROM Student |
| WHERE Sdept='CS' |
| ); |
- 带有EXISTS谓词的子查询
EXISTS代表存在量词∃,带有EXISTS谓词的子查询不返回任何数据,只产生true或false
使用EXISTS,若内层查询结果非空,则外层的WHERE子句返回true,否则返回false(NOT EXISTS相反)
由EXISTS引出的子查询,其目标列表达式一般用*(列名无实际意义)
IN谓词、比较运算符、ANY、ALL谓词引出的子查询都能用EXISTS/NOT EXISTS谓词的子查询等价代替,反之却不一定可以
用EXISTS实现全称量词(∀x)P≡┐∃x(┐P)
用EXISTS实现逻辑蕴含(∀y)p→q≡┐∃y(p∧┐q)
| 例: |
| (实现全称量词) |
| 查询 选修了全部课程 的学生姓名 ↓ |
| //查询 不存在有未选修课程 的学生姓名 ↓ |
| //查询 (NOT EXISTS 未选修) 的学生姓名 ↓ |
| //查询 (NOT EXISTS (NOT EXISTS 选修了的课程)) 的学生姓名 |
| SELECT Sname |
| FROM Student |
| WHERE NOT EXISTS( |
| SELECT * |
| FROM Course |
| WHERE NOT EXISTS( |
| SELECT * |
| FROM SC |
| WHERE Sno=Student.Sno AND |
| Cno=Course.Cno |
| ) |
| ) |
| |
| (实现逻辑蕴含) |
| 查询至少选修了学生201215122选修的全部课程的学生号码 |
| //p代表“学生201215122选修了课程y”,q代表“学生x选修了课程y”,即求(∀y)p->q |
| //转换成 ┐∃y(p∧┐q) |
| SELECT DISTINCT Sno |
| FROM SC SCX |
| WHERE NOT EXISTS( |
| SELECT * |
| FROM SC SCY |
| WHERE SCY.Sno='201215122' AND |
| NOT EXISTS( |
| SELECT * |
| FROM SC SCZ |
| WHERE SCZ.Sno=SCX.Sno AND |
| SCZ.Cno=SCY.Cno |
| ) |
| ); |
集合查询
集合操作包括:并UNION、交INTERSECT、差EXCEPT
参与集合操作的各查询结果的列数必须相同,对应项的数据类型也必须相同
| //MySql不支持交、差操作 |
| |
| //SELECT-FROM-WHERE |
| UNION/INTERSECT/EXCEPT |
| //SELECT-FROM-WHERE |
| |
| 例: |
| 查询既选修了课程1又选修了课程2的学生 |
| SELECT Sno |
| FROM SC |
| WHERE Cno='1' |
| INTERSECT |
| SELECT Sno |
| FROM SC |
| WHERE Cno='2' |
| |
| //另一种写法 |
| SELECT Sno |
| FROM SC |
| WHERE Cno='1' AND |
| Sno IN( |
| SELECT Sno |
| FROM SC |
| WHERE Cno='2' |
| ); |
基于派生表的查询
子查询可以出现在FROM子句中,生成的临时派生表成为主查询的查询对象
| //MySql不支持 |
| |
| 例: |
| 查询每个学生超过他自己选修课程平均成绩的课程号 |
| SELECT Sno,Cno |
| FROM SC,(SELECT Sno,Avg(Grade) FROM SC GROUP BY Sno) |
| [AS] Avg_sc(avg_sno,avg_grade) |
| WHERE SC.Sno=Avg_sc.avg_sno AND |
| SC.Grade>=Avg_sc.avg_grade; |
| |
| 查询所有选修了1号课程的学生姓名 |
| SELECT Sname |
| FROM Student,(SELECT Sno FROM SC WHERE Cno='1') [AS] SC1 |
| WHERE Student.Sno=SC1.Sno; |
| //默认派生表的属性列为子查询SELECT子句后的列名 |
| //AS可省略,但必须有别名 |
3.5数据更新
数据更新的3种操作:添加若干行数据、修改表中的数据、删除表中的若干行数据
插入数据
- 插入元组
功能:将新元组插入指定表中
INTO
子句指明了表名
若INTO
子句没有指明任何属性列名,则新插入的元组必须在每个属性列上有值,且顺序要一致
若INTO
子句指明了属性列名,则亦相应指明了新增加的元组在哪些属性上要赋值,顺序可以与CREATE TABLE
的不一样,INTO
子句上没指明的属性列则会取空值
VALUE
子句对新元组的各属性赋值,字符串常数要用单引号括起来
| |
| INSERT |
| INTO <表名> [(<属性列1> [,<属性列2>] ...)] |
| VALUES (<常量1> [,<常量2>] ...); |
- 插入子查询结果
子查询可以嵌套在INSERT
语句中以生成要插入的批量数据
| |
| INSERT |
| INTO <表名> [(<属性列1> [,<属性列2>...]) |
| 子查询; |
修改数据
修改数据又称更新操作
功能:修改指定表中满足WHERE
子句条件的元组(用SET
子句给出的值作相应取代),若省略WHERE
子句,则表示要修改表中所有的元组
| //一般格式 |
| UPDATE <表名> |
| SET <列名>=<表达式> [,<列名>=<表达式>]... |
| [WHERE <条件>]; |
| |
| 1. 修改某个元组的值 |
| 将学生201215121的年龄改为22岁 |
| UPDATE Student |
| SET Sage=22 |
| WHERE Sno='201215121'; |
| |
| 2. 修改多个元组的值 |
| 将所有的学生的年龄增加1岁 |
| UPDATE Student |
| SET Sage=Sage+1; |
| |
| 3.带查询的修改语句 |
| 将计算机科学系全体学生的成绩置零 |
| UPDATE SC |
| SET Grade=0 |
| WHERE Sno IN( |
| SELETE Sno |
| FROM Student |
| WHERE Sdept='CS' |
| ); |
删除数据
功能:从指定表中删除满足WHERE
子句条件的所有元组,若省略WHERE
则表示删除表中全部元组,但表的定义仍在字典中
注意:DELETE
语句删除的是表中的数据,而不是关于表的定义
| //一般格式 |
| DELETE |
| FROM <表名> |
| [WHERE <条件>]; |
| |
| 1. 删除某个元组的值 |
| 删除学号为201215121的学生记录 |
| DELETE |
| FROM Student |
| WHERE Sno='201215121'; |
| |
| 2. 删除多个元组的值 |
| 删除所有学生的选课记录 |
| DELETE |
| FROM SC |
| |
| 3.带查询的删除语句 |
| 删除计算机系所有学生的选课记录 |
| DELETE |
| FROM SC |
| WHERE Sno IN( |
| SELETE Sno |
| FROM Student |
| WHERE Sdept='CS' |
| ); |
3.6空值的处理
空值是一个特殊的值,含有不确定性
- 空值的产生:插入、修改、外连接、空值的关系运算等都可能会产生空值
- 空值的判断:用
IS NULL
或IS NOT NULL
来表示
- 空值的约束条件:属性定义中有NOT NULL,加了UNIQUE限制的属性,码属性等不能取空值,外码可以取空值
- 空值的算术、比较和逻辑运算:
空值与另一个值的算术运算结果为空值
空值与另一个值的比较运算结果为UNKNOWN
有了UNKNOWN后,传统的逻辑运算中的二值逻辑就扩展成了三值逻辑

注:增删改都注意要满足3个完整性条件
3.7视图
定义:是从一个或几个基本表(或视图)导出的表,只存放定义不存储数据,是一个虚表
定义视图
- 建立视图
CREATE VIEW
语句只是把视图的定义存入数据字典,并不执行子查询中的SELECT
语句
子查询可以是任意的SELETE
子句,是否可以含有ORDER BY
子句和DISTINCT
短语,则取决于具体的系统实现
WITH CHECK OPTION
表示对视图进行更新、插入、删除操作时要保证对应的行满足视图中定义的谓词条件(子查询的条件表达式)
视图的属性列名必须全部指定或全部省略
必须明确指定组成视图的所有列名的三种情况:
1)某个目标列是聚集函数或表达式
2)多表连接是选出了几个同名列作为视图的字段
3)为某个列启用新的名字
| //一般格式 |
| CREATE VIEW <视图名> [(<列名> [,<列名>] ...)] |
| AS <子查询> |
| [WITH CHECK OPTION]; |
| |
| //关于WITH CHECK OPTION |
| CREATE VIEW IS_Student |
| AS |
| SELECT Sno,Sname,Sage |
| FROM Student |
| WHERE Sdept='IS' |
| WITH CHECK OPTION; |
| //之后对该视图进行增删改操作时,关系数据库管理系统会自动加上Sdept='IS'的条件 |
| 行列子集视图:若视图是从单个基本表导出的,并且只是去掉了基本表的某些行和某些列,但保留了主码,则称这类视图为行列子集视图 |
视图可以建立在一个或多个基本表上,也可以建立在一个或多个已定义好的视图上,或建立在基本表与视图上
| 虚拟列:根据应用需求在视图中设置的一些派生属性列,而这些列在基本表中并不实际存在 |
| 带表达式的视图:带虚拟列的视图 |
| CREATE VIEW BT_S(Sno,Sname,Sbirth) |
| AS |
| SELECT Sno,Sname,2021-Sage |
| FROM Student; |
| |
| 分组视图:带有聚集函数和GROUP BY子句的查询来定义的视图 |
| CREATE VIEW S_G(Sno,Gavg) |
| AS |
| SELECT Sno,AVG(Grade) |
| FROM SC |
| GROUP BY Sno; |
- 删除视图
删除视图后视图定义将从数据字典中删除
若该视图上还导出了其他视图,则普通的删除会拒绝执行,要用级联删除把其与其导出的表都删除
| |
| DROP VIEW <视图名> [CASCADE]; |
| |
查询视图
视图消解(一转换过程):从数据字典中取出视图的定义,把定义中的子查询和用户的查询结合起来,转换成等价的对基本表的查询,再执行修正了的查询
| 例1:视图消解的一个例子 |
| SELECT Sno,Sage |
| FROM IS_Student |
| WHERE Sage<20l |
| ↓ 转换成对基本表的查询 |
| SELECT Sno,Sage |
| FROM Student |
| WHERE Sdept='IS' AND Sage<20; |
| |
| 例2:多数数据库系统对行列子集视图的查询均能进行正确转换,但对非行列子集视图的查询不一定能做转换(转换后查询出错) |
| SELECT * |
| FROM S_G |
| WHERE Gavg>=90 |
| 语法错误!因为定义视图时,Gavg是一聚集函数的“代表”(转换后会变成聚集函数),不能用于WHERE |
| 修正:应该直接对基本表进行查询 |
| SELECT * |
| FROM ( |
| SELECT Sno,AVG(Grade) |
| FROM SC |
| GROUP BY Sno) AS S_G(Sno,Gavg) |
| WHERE Gavg>=90; |
| //此时的Gavg是一派生表的属性列,与聚集函数无关,可以用WHERE |
视图定义后将保存在数据字典中,而派生表执行完后即被删除
更新视图
更新包括插入、删除和修改
对视图进行的修改最后会转换为对基本表的修改
在定义了WITH CHECK OPTION
的视图中更新时,只有符合视图定义的条件的操作才会执行
| 例1:修改视图的例子 |
| UPDATE IS_Student |
| SET Sname='刘辰' |
| WHERE Sno='201215121' |
| ↓ 转换成对基本表的修改 |
| UPDATE Student |
| SET Sname='刘辰' |
| WHERE Sno='201215121' AND Sdept='IS'; |
| |
| 例2:视图更新失败的例子 |
| //视图的定义 |
| CREATE VIEW S_G(Sno,Gave) |
| AS |
| SELECT Sno,AVG(Grade) |
| FROM SC |
| GROUP BY Sno; |
| //视图修改 |
| UPDATE S_G |
| SET Gavg=90 |
| WHERE Sno='201215121'; |
| 错误!因为系统不能修改各科成绩使平均成绩为90(Gavg不是属性不能直接修改) |
一般地,行列子集视图可更新,其它子图有些理论上可更新
不允许更新和不可更新是两个不同的概念
视图的作用
- 简化用户操作
- 可以从多种角度看待同一数据
- 对重构数据库提供了一定程度的逻辑独立性
- 对机密数据提供安全保护
- 更清晰地表达查询
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律