C#高级:在SQLserver中使用视图、存储过程、索引和触发器

目录

一、视图

1.视图是什么,有什么作用?

2.视图和存储过程有什么区别?

3.建立一个视图,名为PersonBorrowView,SQL已给出:

4.如果往BorrowInfo加一条记录,我原本的SQL会增加一条记录,那么查询视图,数据会增加吗?

5.写出查询视图的SQL。

6.写出根据视图名获取视图的SQL

7.小结:

二、存储过程

1.创建一个存储过程(一键归还非管理员用户2024年借的书),要求做以下几件事情:

2.写出执行存储过程的SQL/方法

3.假设一个存储过程分为4步,第2步发现报错,那么它会执行1、2步,还是都不执行呢?

4.谈谈临时表和公共表达式的区别

5.存储过程的出入参和异常处理

三、索引

1.索引是什么,创建索引有什么好处

2.以下SQL装载10万数据,每天被查询1000多万次,建议怎么设置索引?

3.写出创建索引的SQL(单个索引和复合索引各列举一条)

4.写出根据表名获取索引设置情况的方法

5.为什么主键一般不设置为索引?

四、触发器

1.触发器是什么,使用有什么好处

2.触发器有多少种类型

3.请你完成三个触发器SQL的编写:

4.查询触发器

5.触发器可以接收外部入参吗,为什么?

五、事务

1.事务是什么,运用事务有什么好处?

2.事务的重要操作有哪些?

3.演示一个事务SQL

4.事务隔离性有哪些,特点是啥?


一、视图

1.视图是什么,有什么作用?

①一个虚拟表

②简化查询

2.视图和存储过程有什么区别?

①视图:用于简化查询和数据展示

②存储过程:用于执行一系列SQL语句,包含增删改查等,即业务逻辑得处理。

3.建立一个视图,名为PersonBorrowView,SQL已给出:

sql 复制代码
  SELECT U.Name,BW.BoTime,BI.BookName FROM UserInfo U
  INNER JOIN BorrowInfo BW ON U.UserID=BW.UserID
  INNER JOIN BookInfo BI ON BW.BookID=BI.BookID

【解答】建立视图的SQL语句:

sql 复制代码
CREATE VIEW PersonBorrowView AS
SELECT U.Name, BW.BoTime, BI.BookName
FROM UserInfo U
INNER JOIN BorrowInfo BW ON U.UserID = BW.UserID
INNER JOIN BookInfo BI ON BW.BookID = BI.BookID;

4.如果往BorrowInfo加一条记录,我原本的SQL会增加一条记录,那么查询视图,数据会增加吗?

会,因为查询视图,本质是查询视图保存的SQL

5.写出查询视图的SQL。

sql 复制代码
SELECT * FROM PersonBorrowView;

6.写出根据视图名获取视图的SQL

sql 复制代码
SELECT OBJECT_DEFINITION(OBJECT_ID('PersonBorrowView')) AS VIEWSQL;

7.小结:

sql 复制代码
-- 建立视图:
CREATE VIEW 视图名 AS
SQL-- 写你的查询SQL

-- 查询视图:
SELECT * FROM 视图名

-- 根据视图名查询视图保存的SQL:
SELECT OBJECT_DEFINITION(OBJECT_ID('视图名')) AS VIEWSQL

二、存储过程

1.创建一个存储过程(一键归还非管理员用户2024年借的书),要求做以下几件事情:

sql 复制代码
-- 筛选出需要一键归还的用户
SELECT UserID,Name FROM UserInfo WHERE Power !=1

-- 筛选出2024年未归还的书
SELECT BorID,UserID,BookID FROM BorrowInfo Where Year(BoTime)='2024' and BackTime is null and UserID in (xxxxx)

-- 将这些书的归还状态更新为今天
Update BorrowInfo set BackTime = GETDATE() Where  BorID in (xxxxx)

-- 将一键归还涉及到的用户、书名、还款日期插入到PC_Table中
INSERT INTO PC_TABLE  ([PCName],[PCBoTime],[PCBookName]) VALUES(xxxx)

【解答:存储过程的创建SQL】

定义存储过程的名称:ReturnBooksForNonAdmin2024

另外定义一些临时表去推进存储过程的流程

sql 复制代码
CREATE PROCEDURE ReturnBooksForNonAdmin2024
AS
BEGIN
    -- 1. 筛选出需要一键归还的用户
    DECLARE @NonAdminUsers TABLE (UserID NVARCHAR(100), Name NVARCHAR(100));
    INSERT INTO @NonAdminUsers (UserID, Name)
    SELECT UserID, Name
    FROM UserInfo
    WHERE Power != 1;

    -- 2. 筛选出2024年未归还的书
    DECLARE @BooksToReturn TABLE (BorID NVARCHAR(100), UserID NVARCHAR(100),BookID NVARCHAR(100));
    INSERT INTO @BooksToReturn (BorID, UserID,BookID)
    SELECT BorID, UserID,BookID
    FROM BorrowInfo
    WHERE YEAR(BoTime) = 2024
    AND BackTime IS NULL
    AND UserID IN (SELECT UserID FROM @NonAdminUsers);

    -- 3. 将这些书的归还状态更新为今天
    UPDATE BorrowInfo
    SET BackTime = GETDATE()
    WHERE BorID IN (SELECT BorID FROM @BooksToReturn);

    -- 4. 将一键归还涉及到的用户、书名、还款日期插入到PC_TABLE中
    INSERT INTO PC_TABLE ([PCName], [PCBoTime], [PCBookName])
    SELECT u.Name AS [PCName], GETDATE() AS [PCBoTime], bi.BookName AS [PCBookName]
    FROM @BooksToReturn b
    JOIN UserInfo u ON b.UserID = u.UserID
    JOIN BookInfo bi ON b.BookID= bi.BookID;

END;

2.写出执行存储过程的SQL/方法

sql 复制代码
EXECUTE ReturnBooksForNonAdmin2024;

【查询某个存储过程的SQL】

3.假设一个存储过程分为4步,第2步发现报错,那么它会执行1、2步,还是都不执行呢?

取决于SQL的事务

  • 如果步骤1和步骤2在同一个事务中执行,并且第2步失败,步骤1也不会生效。
  • 如果每一步都在独立的事务中,那么步骤1和步骤2会分别执行,失败的步骤不会影响之前成功的步骤。

(本例的存储过程没有指定事务,因此会被当做是独立的事务,因此会执行第1、2步,而不执行后续的SQL)

4.谈谈临时表和公共表达式的区别

sql 复制代码
-- 临时表
DECLARE @NonAdminUsers TABLE (UserID NVARCHAR(100), Name NVARCHAR(100));
INSERT INTO @NonAdminUsers (UserID, Name)
SELECT UserID, Name
FROM UserInfo
WHERE Power != 1;
SELECT * FROM @NonAdminUsers

-- 公共表达式CTE
WITH NonAdminUsers AS
(
    SELECT UserID, Name
    FROM UserInfo
    WHERE Power != 1
)
SELECT * FROM NonAdminUsers

【区别和特点】

  1. 作用域: 临时表通常用于存储过程或复杂逻辑,公共表达式(CTE)更适合单次查询,特别是递归查询。
  2. 复杂性: 临时表处理复杂的增删改查操作时更具优势。
  3. 用途: 公共表达式适合在查询中引用和计算临时数据。

5.存储过程的出入参和异常处理

【需求】

传入一个USERID,如果查到下面的SQL则插入,查不到就不插入,且查到且成功插入返回1,查不到返回0,查到了插入失败返回-99

sql 复制代码
SELECT U.Name,BW.BoTime,BI.BookName FROM UserInfo U
INNER JOIN BorrowInfo BW ON U.UserID=BW.UserID
INNER JOIN BookInfo BI ON BW.BookID=BI.BookID
WHERE U.UserID=''

【创建存储过程】

sql 复制代码
CREATE PROCEDURE InsertUserData
    @UserID NVARCHAR(100),  -- 输入的用户ID
    @Result INT OUTPUT     -- 输出结果
AS
BEGIN
    -- 初始化结果为0
    SET @Result = 0;

    -- 使用事务处理数据插入操作
    BEGIN TRANSACTION;

    BEGIN TRY
        -- 检查是否有记录需要插入
        IF EXISTS (
            SELECT 1
            FROM UserInfo U
            INNER JOIN BorrowInfo BW ON U.UserID = BW.UserID
            INNER JOIN BookInfo BI ON BW.BookID = BI.BookID
            WHERE U.UserID = @UserID
        )
        BEGIN
            -- 将所有相关数据插入目标表
            INSERT INTO PC_TABLE ([PCName], [PCBoTime], [PCBookName])
            SELECT U.Name, BW.BoTime, BI.BookName
            FROM UserInfo U
            INNER JOIN BorrowInfo BW ON U.UserID = BW.UserID
            INNER JOIN BookInfo BI ON BW.BookID = BI.BookID
            WHERE U.UserID = @UserID;

            -- 如果插入成功,设置结果为1
            SET @Result = 1;
        END
        ELSE
        BEGIN
            -- 如果没有记录符合条件,设置结果为0
            SET @Result = 0;
        END

        -- 提交事务
        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        -- 如果发生错误,回滚事务
        ROLLBACK TRANSACTION;

        -- 设置结果为-99
        SET @Result = -99;
    END CATCH
END

【执行存储过程】

sql 复制代码
DECLARE @OutputResult INT;

EXEC InsertUserData @UserID = 'some_user_id', @Result = @OutputResult OUTPUT;

-- 查询输出结果
SELECT @OutputResult AS Result;

三、索引

1.索引是什么,创建索引有什么好处

①提高数据库查询效率的数据结构。

②可以让数据库系统快速查找到特定的数据行。

2.以下SQL装载10万数据,每天被查询1000多万次,建议怎么设置索引?

sql 复制代码
SELECT * FROM [BookManage].[dbo].[BookInfo] WHERE Author = 'XXX'

如果 Author 是主要的查询条件列,则应对 Author 列创建索引。

3.写出创建索引的SQL(单个索引和复合索引各列举一条)

CREATE INDEX IX_Author ON BookManage.dbo.BookInfo (Author);

CREATE INDEX IX_BookName_Author ON BookManage.dbo.BookInfo (BookName, Author);

4.写出根据表名获取索引设置情况的方法

【方法一:SSMS查找】

【方法二:SQL查找】

sql 复制代码
-- 获取指定表的索引信息
SELECT 
    t.name AS TableName,
    i.name AS IndexName,
    i.type_desc AS IndexType,
    c.name AS ColumnName,
    ic.index_column_id AS ColumnID,
    ic.key_ordinal AS KeyOrdinal
FROM 
    sys.indexes AS i
    INNER JOIN sys.index_columns AS ic
        ON i.object_id = ic.object_id AND i.index_id = ic.index_id
    INNER JOIN sys.columns AS c
        ON ic.object_id = c.object_id AND ic.column_id = c.column_id
    INNER JOIN sys.tables AS t
        ON i.object_id = t.object_id
WHERE 
    t.name = 'BookInfo'  -- 指定表名
ORDER BY 
    i.name, ic.index_column_id;

说明:

5.为什么主键一般不设置为索引?

因为创建主键时会自动创建一个聚集索引,以确保数据的唯一性和高效的查找。

四、触发器

1.触发器是什么,使用有什么好处

①定义:附加到表或视图上 的一个特殊类型的存储过程。

②好处:在某表(视图)发生增删改时自动化地执行定义好的SQL 。(可选是增删改某表前执行 ,还是增删改某表后执行

2.触发器有多少种类型

①按状态分:

INSERT 触发器: 在插入操作之前或之后执行。

UPDATE 触发器: 在更新操作之前或之后执行。

DELETE 触发器: 在删除操作之前或之后执行。

INSTEAD OF 触发器: 替代执行触发器所附加的操作,这意味着触发器内的操作会取代原来的操作。

②按时间分:

BEFORE触发器:在数据操作语句(如INSERT、UPDATE、DELETE)执行之前触发。

AFTER触发器:在数据操作语句执行之后触发。

3.请你完成三个触发器SQL的编写:

  • LogInfo表:ID,LogTime,Remarks
  • UserInfo表作更新(Update)前,将信息插入到LogInfo表中(ID:NEWID())
  • UserInfo表作新增(Insert)后,将信息插入到LogInfo表中(ID:NEWID())
  • BorrowInfo表作删除(Delete)后,将信息插入到LogInfo表中(ID:NEWID())
sql 复制代码
-- UserInfo表作更新(Update)前,将信息插入到LogInfo表中(ID:NEWID())
CREATE TRIGGER trg_UserInfo_Update_Before
ON dbo.UserInfo
AFTER UPDATE
AS
BEGIN
    INSERT INTO dbo.LogInfo (ID, LogTime, Remarks)
    SELECT 
        NEWID() AS ID,
        GETDATE() AS LogTime,
        'Update Operation: ID = ' + CAST(i.UserID AS VARCHAR(50)) + ', Updated By = ' + SYSTEM_USER AS Remarks
    FROM 
        inserted i  -- 这里的i指的是UserInfo表,下同
END;


-- UserInfo表作新增(Insert)后,将信息插入到LogInfo表中(ID:NEWID())
CREATE TRIGGER trg_UserInfo_Insert_After
ON dbo.UserInfo
AFTER INSERT
AS
BEGIN
    INSERT INTO dbo.LogInfo (ID, LogTime, Remarks)
    SELECT 
        NEWID() AS ID,
        GETDATE() AS LogTime,
        'Insert Operation: ID = ' + CAST(i.UserID AS VARCHAR(50)) + ', Inserted By = ' + SYSTEM_USER AS Remarks
    FROM 
        inserted i
    -- 这里也可以使用 WHERE 子句来筛选特定的记录
END;


-- BorrowInfo表作删除(Delete)后,将信息插入到LogInfo表中(ID:NEWID())
CREATE TRIGGER trg_BorrowInfo_Delete_After
ON dbo.BorrowInfo
AFTER DELETE
AS
BEGIN
    INSERT INTO dbo.LogInfo (ID, LogTime, Remarks)
    SELECT 
        NEWID() AS ID,
        GETDATE() AS LogTime,
        'Delete Operation: ID = ' + CAST(d.UserID AS VARCHAR(50)) + ', Deleted By = ' + SYSTEM_USER AS Remarks
    FROM 
        deleted d
    -- 这里也可以使用 WHERE 子句来筛选特定的记录
END;

4.查询触发器

【方法一:SSMS】

【方法二:SQL】

sql 复制代码
SELECT 
    t.name AS TriggerName,
    o.name AS TableName
FROM 
    sys.triggers t
INNER JOIN 
    sys.objects o
    ON t.parent_id = o.object_id
WHERE 
    o.name = 'UserInfo'; -- 替换为你的表名

5.触发器可以接收外部入参吗,为什么?

①不可以

②防止注入风险;简化设计,确保其可维护性;

五、事务

1.事务是什么,运用事务有什么好处?

①定义:事务是一个或者多个SQL,一个事务要么全部成功,要么全部失败。(ACID属性)

②好处:事务执行过程中发生错误或故障,事务可以被回滚到其开始前的状态,防止数据损坏。

2.事务的重要操作有哪些?

BEGIN TRANSACTION:开始一个新的事务。

COMMIT:提交事务,保存事务中的所有操作,使它们对其他事务可见。提交操作将所有更改写入数据库。

ROLLBACK:回滚事务,撤销事务中的所有操作,将数据库恢复到事务开始前的状态。

3.演示一个事务SQL

sql 复制代码
-- 开始事务
BEGIN TRANSACTION;

-- 执行多个数据库操作
INSERT INTO [dbo].[LogInfo]
           ([ID]
           ,[LogTime]
           ,[Remarks])
     VALUES
           (NEWID()
           ,GETDATE()
           ,'执行了插入操作')

-- 提交事务
--COMMIT;

-- 回滚事务
--ROLLBACK;

SELECT * FROM LogInfo

问题:
①如果开始事务后,执行SQL,然后既不执行提交事务,又不执行回滚事务,会怎么样?

对数据的修改都会在事务结束之前保持在事务的上下文中。

事务通常会在数据库连接关闭时自动回滚,或者在数据库服务器重启时也会回滚未提交的事务。

②开始事务后,执行SQL,还没提交,为何就可以查出数据了?

SQLsever默认自动提交事务,且隔离级别默认设置是"读已提交"隔离级别,事务内的查询会看到其他事务已提交的更改。

4.事务隔离性有哪些,特点是啥?

  • 读未提交(Read Uncommitted):可读到:开启事务+SQL增删改(未提交)的数据
  • 读已提交(Read Committed):默认,可读到:开启事务+SQL增删改后提交的数据(不开启事务默认执行SQL自动提交)
  • 可重复读(Repeatable Read):开启事务1读A表,此时A表插入新数据,再读A表,读不到新数据,直到事务1提交后读A表才看得到。
  • ‌串行化(Serializable):开启事务1读A表,此时A表插入新数据,发现插入失败,直到事务1提交后,才可以插入新数据(事务隔离,需排队)。
相关推荐
dingdingfish17 分钟前
JSON 系列之1:将 JSON 数据存储在 Oracle 数据库中
oracle·json·database
小蜗牛慢慢爬行38 分钟前
如何在 Spring Boot 微服务中设置和管理多个数据库
java·数据库·spring boot·后端·微服务·架构·hibernate
hanbarger41 分钟前
nosql,Redis,minio,elasticsearch
数据库·redis·nosql
微服务 spring cloud1 小时前
配置PostgreSQL用于集成测试的步骤
数据库·postgresql·集成测试
先睡1 小时前
MySQL的架构设计和设计模式
数据库·mysql·设计模式
弗罗里达老大爷1 小时前
Redis
数据库·redis·缓存
仰望大佬0072 小时前
Avalonia实例实战五:Carousel自动轮播图
数据库·microsoft·c#
学不透java不改名2 小时前
sqlalchemy连接dm8 get_columns BIGINT VARCHAR字段不显示
数据库
一只路过的猫咪2 小时前
thinkphp6使用MongoDB多个数据,聚合查询的坑
数据库·mongodb