数据库的使用03:SQLServer数据库中的高级语法及其技巧

目录

一、日期相关

1.查询当前日期相关数据

2.查询特定时间区间

3.时间加减法

(1)加法

(2)减法

4.格式化日期

二、数据类型相关

1.Int -> Decimal

2.DateTime->OtherTime

3.DateTime->string

三、条件判断相关

1.ISNULL

2.IF ELSE(单字段)

3.IF ELSE(多字段)

四、子查询

(1)联表(推荐)

(2)嵌套in / Exist(推荐)

(3)公共表达式CTE

(4)SELECT MANY SQL

五、查询数据处理

1.STUFF

2.聚合函数(MAX SUM AVG COUNT)

(1)查询非goupby字段

(2)查询聚合条件和别名字段

3.分页查询

4.字符串处理

替代

切片

5.标号RowNumber

6.一表多联

六、批量更新数据


一、日期相关

1.查询当前日期相关数据

SELECT * FROM Article WHERE Year(PublishTime) = Year(GETDATE())  --等于此年
SELECT * FROM Article WHERE CAST(PublishTime AS date) = CAST(GETDATE() AS DATE)  --等于此年此月此日
SELECT * FROM Article WHERE PublishTime = GETDATE()  --等于此年此月此日此时此分此秒

2.查询特定时间区间

SELECT * FROM table WHERE Time BETWEEN '2023-01-01 00:00:00' AND '2023-12-31 23:59:59';

3.时间加减法

(1)加法

【解说】将StartTime的值加上一天,并返回结果。

修改前 2023-11-30 11:04:15.040
修改后 2023-12-01 11:04:15.040

SELECT CAST(DATEADD(DAY, 1, StartTime) AS DATETIME) FROM TABLE

(2)减法

--算出2025年元旦距离现在还有多少天(2025-01-01减去此时此刻得到的天数)
SELECT DATEDIFF(DAY,GETDATE(), '2025-01-01')

4.格式化日期

SELECT FORMAT(GETDATE(), 'yyyy年MM月dd日') 

二、数据类型相关

1.Int -> Decimal

SELECT cast(AGE as decimal(10,2)) FROM TABLE1

【备注】

decimal(10, 3)的含义:10位数字 = 3位小数 + 7位整数 

2.DateTime->OtherTime

SELECT TOP 1 PublishTime FROM Article WHERE PublishTime IS NOT NULL
SELECT TOP 1 CAST(PublishTime AS smalldatetime) AS DateHour FROM Article WHERE PublishTime IS NOT NULL
SELECT TOP 1 CAST(PublishTime AS date) AS Date  FROM Article  WHERE PublishTime IS NOT NULL
SELECT TOP 1 CAST(PublishTime AS time) AS Hour FROM Article    WHERE PublishTime IS NOT NULL

3.DateTime->string

SELECT TOP 1
    CONVERT(VARCHAR(10), (SELECT TOP 1 TIME FROM FLHappy), 120) AS FormattedDate,
    CONVERT(VARCHAR(8), (SELECT TOP 1 TIME FROM FLHappy), 108) AS FormattedTime,
	CONVERT(VARCHAR(19), (SELECT TOP 1 TIME FROM FLHappy), 120) AS FormattedDateTime;

三、条件判断相关

1.ISNULL

SELECT  ISNULL(AGE ,0)  FROM TABLE1

2.IF ELSE(单字段)

SELECT(CASE sex
    WHEN '1' THEN '男'
    WHEN '2' THEN '女'
    ELSE '其他'
    END
             )  FROM TABLE1

3.IF ELSE(多字段)

SELECT(CASE 
    WHEN AGE=13 THEN AGE
    WHEN CLASS=2 THEN CLASS
    ELSE '其他'
    END
             )  FROM TABLE1

【注意】不要漏了end

【口诀】一个case一个end,一个when跟一个then

四、子查询

【情景说明】

Student表
        学生ID  学生姓名  学生班级(stu_id stu_name stu_class)
Score表
        成绩ID 学生ID  是否及格(score_id stu_id ispass)

要求:返回学生姓名、是否及格字段,其中筛选出来的数据要求ispass=1

(1)联表(推荐)

SELECT s.stu_name, sc.ispass
FROM Student s
INNER JOIN Score sc ON s.stu_id = sc.stu_id
WHERE sc.ispass = 1;

(2)嵌套in / Exist(推荐)

SELECT stu_name, '1' AS ispass
FROM Student
WHERE stu_id IN (
    SELECT stu_id
    FROM Score
    WHERE ispass = 1
);
SELECT stu_name, '1' AS ispass
FROM Student s
WHERE EXISTS (
    SELECT 1
    FROM Score sc
    WHERE sc.stu_id = s.stu_id AND sc.ispass = 1
);

(3)公共表达式CTE

WITH CTE AS(
SELECT stu_name, (
    SELECT ispass
    FROM Score
    WHERE Score.stu_id = Student.stu_id
) AS 'ispass'
FROM Student
)
SELECT * FROM CTE WHERE ispass=1

【CTE模板语法】

WITH CTE AS (
    SELECT * FROM TABLE1 
)

SELECT * FROM CTE;
WITH TEMP1 AS(
	--SQL1
),TEMP2 AS(
	--SQL2
)
SELECT * FROM TEMP2

(4)SELECT MANY SQL

【不同条件写法】

SELECT 
    (SELECT COUNT(*) FROM [Millionaire].[dbo].[FLHappy] WHERE allnum LIKE '%01%') AS num01,
    (SELECT COUNT(*) FROM [Millionaire].[dbo].[FLHappy] WHERE allnum LIKE '%02%') AS num02;

【同条件写法】

SELECT
    MAX(CASE WHEN  TYPE = 1 THEN NAME ELSE NULL END) AS NAME,
    MAX(CASE WHEN  TYPE = 2 THEN NAME ELSE NULL END) AS UID
FROM STUDENT
WHERE SCHOOL='440' AND CODE = 'A'

五、查询数据处理

1.STUFF

【数据表】SchoolData:id  teachername   student (老师对学生是一对多,这张表允许老师重复)
【需求】查询输出teachername和studentlist(逗号拼接,输出老师名字和该老师管辖的所有学生)

【注解】STUFF(原字符串,被替换的位置,替换的长度,替换的符号)

  • STUFF(..., 1, 1, ''):从结果的开头(位置1)移除1个长度的字符(即逗号)
SELECT
    teachername,
    STUFF(
        (SELECT ',' + student
         FROM SchoolData AS t2
         WHERE t2.teachername = t1.teachername
         FOR XML PATH('')), 1, 1, '') AS studentlist
FROM
    SchoolData AS t1
WHERE 1=1
GROUP BY
    teachername;

【模板】大哥找小弟(逗号拼接)

         提示:将大哥和小弟替换为你想查的字段即可。

SELECT
    大哥,
    COUNT(*),
    STUFF(
        (SELECT ',' + 小弟
         FROM 表名 AS t2
         WHERE 1=1 AND t2.大哥 = t1.大哥  
         FOR XML PATH('')), 1, 1, '') AS Alllist
FROM
    表名 AS t1
WHERE 1=1
GROUP BY
    大哥
ORDER BY
    大哥

示例效果:

【模板】拼接一列字段

--逗号拼接
SELECT STUFF((
    SELECT ',' + 单独的字段名
    FROM 表名
    FOR XML PATH('')), 1, 1, '') AS T

2.聚合函数(MAX SUM AVG COUNT)

(1)查询非goupby字段

【错误的写法】Group字段和Select字段不一致(我想显示出Score

SELECT Name, Age, Score

FROM PERSON

GROUP BY Name, Age;

【方法一】取MAX值

SELECT Name, Age, MAX(SCORE) AS Score

FROM PERSON

GROUP BY Name, Age;

【方法二】子查询(如果可以的话)

SELECT Name,Age,(SELECT SCORE FROM SCORETABLE WHERE NAME=NAME) AS Score

FROM PERSON

GROUP BY Name,Age

(2)查询聚合条件和别名字段

【引入】

SELECT 
    ID,
    NAME,
    MAX(SCORE),
    (SELECT Code FROM TABLE2 WHERE ID = ID) AS Code
FROM TABLE
WHERE Code=500 AND SCORE > 60
GROUP BY
    ID,
    NAME

这个SQL是不对的,不能对聚合函数和别名字段直接筛选,且ID=ID有歧义,解决方案有两个:

    1.SQL外包一层

    2.对于聚合函数用having筛选,对于别名字段在子查询SQL中过滤

    (且都用T.ID特指是TABLE的ID)


(2-1)SQL外包一层

SELECT *
FROM (
    SELECT 
        ID,
        NAME,
        MAX(SCORE) AS MaxScore,
        (SELECT Code FROM TABLE2 WHERE ID = T.ID) AS Code
    FROM TABLE T
    GROUP BY ID, NAME
) AS SubQuery
WHERE Code = 500 AND MaxScore > 60;
--【注】包完之后一定要 'AS SubQuery',否则报错

(2-2)对于聚合函数用having筛选,对于别名字段在子查询SQL中过滤

SELECT 
    ID,
    NAME,
    MAX(SCORE) AS MaxScore,
    (SELECT Code FROM TABLE2 WHERE ID = T.ID AND Code = 500) AS Code
FROM TABLE T
GROUP BY ID, NAME
HAVING MAX(SCORE) > 60;
--【注】HAVING SCORE > 60 是不对的。

3.分页查询

【后端传回前端】总页数、当前页行数

DECLARE @page_index INT = 2;-- 第几页,前端传入
DECLARE @page_size INT = 10; -- 一页几条,前端传入

-- 执行查询并分页
select * from UserInfo  
order by UserID 
offset ((@page_index-1)*@page_size) rows  
fetch next (@page_size) rows only

4.字符串处理

替代

 SELECT 'ABC123' AS DATA --输出【ABC123】
 SELECT REPLACE('ABC123','ABC','D') AS DATA2 --后面代替前面,输出【D123】
 SELECT SUBSTRING('ABC123',2,4) AS DATA3 --切片,从第2开始,切4个长度的区间的内容,输出【BC12】

切片

假设你要从字符串 123 中单独查出 123

SELECT 
    SUBSTRING('123', 1, 1) AS FirstDigit,  -- 取第一个字符
    SUBSTRING('123', 2, 1) AS SecondDigit, -- 取第二个字符
    SUBSTRING('123', 3, 1) AS ThirdDigit    -- 取第三个字符

5.标号RowNumber

示例:

SELECT *, 
       ROW_NUMBER() OVER (PARTITION BY SUMVALUE ORDER BY TIME DESC) AS RowNum
FROM FLLottery 
ORDER BY TIME DESC;--此处无论是否order by 都不影响结果

结果:

模板:

--根据某个字段分组,并根据另一个字段排序,返回排名(例如根据班级分组,并根据成绩大到小倒序排序,返回排名)

SELECT *, 
       ROW_NUMBER() OVER (PARTITION BY 分组字段 ORDER BY 排序字段 DESC) AS RowNum --DESC=排序方式是倒序
FROM 表名

6.一表多联

①两张表:

  • userinfo:id username postcode (区邮编)
  • placeinfo:id postcode type name  pid (type=1是区,type=2是市,pid关联市和区)

②需求:

查出:用户,区名称,市名称

③SQL:

SELECT 
    u.id AS user_id,
    u.username,
    district.name AS district_name,
    city.name AS city_name
FROM 
    userinfo u
JOIN 
    placeinfo district ON u.postcode = district.postcode AND district.type = 1
JOIN 
    placeinfo city ON district.pid = city.id AND city.type = 2;

④linq写法:

var query = from user in userlist
            let district = placelist.FirstOrDefault(p => p.Postcode == user.Postcode && p.Type == 1)
            let city = district != null ? placelist.FirstOrDefault(p => p.Id == district.Pid && p.Type == 2) : null
            select new
            {
                Username = user.Username,
                DistrictName = district != null ? district.Name : "未知区",
                CityName = city != null ? city.Name : "未知市"
            };

7.递归查询

【需求】查找 AmoebaID= 4408180d-f097-46ef-9512-8338a618bd0a 的所有后代记录

WITH CTE AS (
    -- 初始查询:选取目标记录
    SELECT * 
    FROM T_BD_Amoeba 
    WHERE AmoebaID = '4408180d-f097-46ef-9512-8338a618bd0a'
    AND IsDeleted = 0  -- 确保查询到的记录没有被删除

    UNION ALL

    -- 递归部分:获取子记录
    SELECT a.* 
    FROM T_BD_Amoeba a
    INNER JOIN CTE b ON b.AmoebaCode = a.ParentAmoeba
    WHERE a.IsDeleted = 0  -- 确保只查询未删除的子记录
)

-- 最终查询:获取所有后代记录的详细信息
SELECT d.AmoebaID,
       d.AmoebaCode,
       d.AmoebaName
FROM CTE d


六、批量更新数据

stu表
id name code

dic表
id code1 code2

我stu表装的code是code1,我想改为code2(code1和code2是一一对应的,应该如何改呢)

UPDATE stu
SET code = dic.code2
FROM stu
JOIN dic ON stu.code = dic.code1;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值