使用C++访问数据库(使用ADO编程接口)

使用c++访问数据库(ADO)

1 创建数据库

新建查询—>粘贴下面eg代码—>执行—>刷新数据库—>创建成功

在这里插入图片描述

eg pxscj

create database pxscj
go
USE [pxscj]
GO
/****** Object:  Table [dbo].[cjb]    Script Date: 2017/9/25 5:25:57 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[cjb](

       [学号] [char](6) NOT NULL,

       [课程号] [nchar](3) NOT NULL,

       [成绩] [int] NULL,

CONSTRAINT [PK_cjb] PRIMARY KEY CLUSTERED 

(

       [学号] ASC,

       [课程号] ASC

)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

) ON [PRIMARY]

GO

/****** Object:  Table [dbo].[kcb]    Script Date: 2017/9/25 5:25:57 ******/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE TABLE [dbo].[kcb](

       [课程号] [char](3) NOT NULL,

       [课程名] [nchar](16) NOT NULL,

       [开课学期] [tinyint] NULL,

       [学时] [tinyint] NULL,

       [学分] [tinyint] NOT NULL

) ON [PRIMARY]

GO

/****** Object:  Table [dbo].[xsb]    Script Date: 2017/9/25 5:25:57 ******/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE TABLE [dbo].[xsb](

       [学号] [char](6) NOT NULL,

       [姓名] [nchar](8) NOT NULL,

       [性别] [bit] NULL,

       [出生时间] [date] NULL,

       [专业] [nchar](12) NULL,

       [总学分] [int] NULL,

       [备注] [nvarchar](500) NULL,

       [照片] [varbinary](max) NULL,

CONSTRAINT [PK_xsb] PRIMARY KEY CLUSTERED 

(

       [学号] ASC

)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

) ON [PRIMARY]

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191301', N'101', 80)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191301', N'102', 78)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191301', N'206', 76)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191302', N'102', 78)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191302', N'206', 78)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191303', N'101', 62)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191303', N'102', 70)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191303', N'206', 81)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191304', N'101', 90)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191304', N'102', 84)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191304', N'206', 84)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191306', N'101', 86)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191306', N'102', 55)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191306', N'206', 95)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191307', N'101', 67)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191307', N'102', 78)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191307', N'206', 45)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191308', N'101', 89)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191308', N'102', 78)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191309', N'101', 90)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191309', N'102', 79)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191309', N'206', 89)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191310', N'101', 95)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191310', N'206', 87)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191311', N'101', 67)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191311', N'102', 95)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191311', N'206', 78)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191313', N'101', 89)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191313', N'102', 90)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'191313', N'206', 67)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221301', N'101', 89)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221301', N'102', 90)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221301', N'206', 67)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221302', N'101', 66)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221302', N'102', 89)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221302', N'206', 70)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221303', N'102', 78)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221303', N'206', 90)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221304', N'101', 98)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221304', N'102', 76)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221304', N'206', 85)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221306', N'101', 80)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221306', N'102', 67)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221306', N'206', 87)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221310', N'101', 76)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221310', N'102', 94)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221310', N'206', 86)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221316', N'101', 76)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221316', N'102', 46)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221316', N'206', 44)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221318', N'101', 76)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221318', N'206', 77)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221320', N'101', 82)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221320', N'102', 96)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221320', N'206', 90)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221321', N'101', 56)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221321', N'102', 67)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221321', N'206', 88)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221341', N'101', 69)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221341', N'102', 78)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221341', N'206', 100)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221341', N'208', 100)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221341', N'209', 100)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221341', N'210', 100)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221341', N'212', 100)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221341', N'301', 100)

GO

INSERT [dbo].[cjb] ([学号], [课程号], [成绩]) VALUES (N'221341', N'302', 100)

GO

INSERT [dbo].[kcb] ([课程号], [课程名], [开课学期], [学时], [学分]) VALUES (N'101', N'计算机基础      ', 1, 80, 5)

GO

INSERT [dbo].[kcb] ([课程号], [课程名], [开课学期], [学时], [学分]) VALUES (N'102', N'程序设计语言    ', 2, 68, 4)

GO

INSERT [dbo].[kcb] ([课程号], [课程名], [开课学期], [学时], [学分]) VALUES (N'206', N'离散数学        ', 4, 68, 4)

GO

INSERT [dbo].[kcb] ([课程号], [课程名], [开课学期], [学时], [学分]) VALUES (N'208', N'数据结构        ', 5, 68, 4)

GO

INSERT [dbo].[kcb] ([课程号], [课程名], [开课学期], [学时], [学分]) VALUES (N'209', N'操作系统        ', 6, 68, 4)

GO

INSERT [dbo].[kcb] ([课程号], [课程名], [开课学期], [学时], [学分]) VALUES (N'210', N'计算机原理   ', 5, 85, 5)

GO

INSERT [dbo].[kcb] ([课程号], [课程名], [开课学期], [学时], [学分]) VALUES (N'212', N'数据库原理   ', 7, 68, 4)

GO

INSERT [dbo].[kcb] ([课程号], [课程名], [开课学期], [学时], [学分]) VALUES (N'301', N'计算机网络   ', 7, 51, 3)

GO

INSERT [dbo].[kcb] ([课程号], [课程名], [开课学期], [学时], [学分]) VALUES (N'302', N'软件工程        ', 7, 51, 3)

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'191301', N'王林    ', 1, CAST(N'1990-02-10' AS Date), N'计算机   ', 60, N'bbb')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'191302', N'程明    ', 1, CAST(N'1991-02-01' AS Date), N'计算机   ', 50, N'我是 191302')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'191303', N'王燕    ', 0, CAST(N'1989-10-06' AS Date), N'计算机   ', 50, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'191304', N'韦严平 ', 1, CAST(N'1990-08-26' AS Date), N'计算机   ', 50, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'191306', N'李方方 ', 1, CAST(N'1990-11-20' AS Date), N'计算机   ', 50, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'191307', N'李明    ', 1, CAST(N'1990-05-01' AS Date), N'计算机   ', 54, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'191308', N'林一帆 ', 1, CAST(N'1989-08-05' AS Date), N'计算机   ', 52, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'191309', N'张强明 ', 1, CAST(N'1994-08-11' AS Date), N'计算机   ', 50, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'191310', N'张蔚    ', 0, CAST(N'1996-07-22' AS Date), N'计算机   ', 50, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'191311', N'赵琳    ', 0, CAST(N'1995-03-18' AS Date), N'计算机   ', 50, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'191313', N'严红    ', 0, CAST(N'1994-08-11' AS Date), N'计算机   ', 48, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'191315', N'刘明仪 ', 1, CAST(N'1996-03-02' AS Date), N'计算机   ', 50, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'221301', N'王敏    ', 1, CAST(N'1994-06-10' AS Date), N'通信工程    ', 42, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'221302', N'王林    ', 1, CAST(N'1994-01-29' AS Date), N'通信工程    ', 40, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'221303', N'王玉民 ', 1, CAST(N'1995-03-26' AS Date), N'通信工程    ', 42, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'221304', N'马琳琳 ', 1, CAST(N'1995-02-10' AS Date), N'通信工程    ', 50, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'221306', N'李计    ', 1, CAST(N'1995-09-20' AS Date), N'通信工程    ', 53, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'221310', N'李红庆 ', 1, CAST(N'1994-05-01' AS Date), N'通信工程    ', 50, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'221316', N'孙详欣 ', 1, CAST(N'1994-03-19' AS Date), N'通信工程    ', 50, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'221318', N'孙研    ', 1, CAST(N'1995-10-09' AS Date), N'通信工程    ', 56, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'221320', N'吴薇华 ', 1, CAST(N'1995-03-18' AS Date), N'通信工程    ', 48, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'221321', N'刘燕敏 ', 1, CAST(N'1994-11-12' AS Date), N'通信工程    ', 50, N'...')

GO

INSERT [dbo].[xsb] ([学号], [姓名], [性别], [出生时间], [专业], [总学分], [备注]) VALUES (N'221341', N'罗林琳 ', 1, CAST(N'1995-01-30' AS Date), N'通信工程    ', 52, N'...')

GO

ALTER TABLE [dbo].[cjb] ADD  CONSTRAINT [DF_cjb_成绩]  DEFAULT ((0)) FOR [成绩]

GO

ALTER TABLE [dbo].[kcb] ADD  CONSTRAINT [DF_kcb_开课学期]  DEFAULT ((1)) FOR [开课学期]

GO

ALTER TABLE [dbo].[kcb] ADD  CONSTRAINT [DF_kcb_学时]  DEFAULT ((0)) FOR [学时]

GO

ALTER TABLE [dbo].[kcb] ADD  CONSTRAINT [DF_kcb_学分]  DEFAULT ((0)) FOR [学分]

GO

ALTER TABLE [dbo].[xsb] ADD  CONSTRAINT [DF_xsb_性别]  DEFAULT ((1)) FOR [性别]

GO

ALTER TABLE [dbo].[xsb] ADD  CONSTRAINT [DF_xsb_专业]  DEFAULT ('计算机') FOR [专业]

GO

ALTER TABLE [dbo].[xsb] ADD  CONSTRAINT [DF_xsb_总学分]  DEFAULT ((0)) FOR [总学分]

GO

2 测试是否成功连接数据库

(桌面,随便哪个位置)新建文本文档—>改拓展名.udl —>提供程序里面选择Microsoft OLE DB Provider for SQL Server—>下一步(连接)

测试数据库是否连接成功,填写

在这里插入图片描述

点击确定(中间的和下面两个)
注意:由于当时下面的确定我没有点,直接关闭了这个页面,导致打开打开的记事本文件里面没有显示
之后以记事本的方式打开这个udl文件

在这里插入图片描述

显示(之后会使用这个里面的连接串)

在这里插入图片描述

在这个连接串中,Persist Security Info 属性为 True 时表示在建立连接后仍然保存密码,一般取 False 即可 。ID 和 Password 属性只有在上述数据库属性对话框中勾选“允许保存密码”时才会有 。自己可以手工添加 。pxscj 是我的数据库名 。

3 开始写程序

3.1 程序框架

打开vs —> 创建空项目—>在项目中新建一个名为 main.cpp 的源文件 ,然后在这个文件中输入以下代码:

#import "c:\\Program Files\\Common Files\\System\\ADO\\msado15.dll" no_namespace rename("EOF", "EndOfFile")
#include <stdio.h>  // wprintf
#include <locale.h> // setlocale

int main()
{
       setlocale(LC_ALL, "chs");
       CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
       
    try
       {          

       }
    
       catch (_com_error &err)
       {
               wprintf(L"The application throws the error: %s\n", (wchar_t*)err.ErrorMessage());   wprintf(L"Description = %s\n", (wchar_t*)err.Description());
       }
    
       CoUninitialize();
       return 0;
}

点击调试—>选择开始执行(不调试)—>弹出黑框—>关闭

其中:

  • #import 是一个编译器指令(#include 也是编译器指令) 。用于导入指定的 DLL 文件 。在本程序中,导入了 msado15.dll 文件,这个文件实现了 ADO 编程接口 。由于这个编译器指令,编译器会为我们做大量的工作,比如包含所需的头文件(头文件中通常包括函数和类型的声明以及常量或枚举的定义等);除此之外,还指定了不要使用命名空间(稍后解释)以及将标识符更名(将 EOF 更名为 EndOfFile) 。
  • 调用 setlocale 函数可能有助于解决在控制台窗口中的中文显式问题 。
  • CoInitialize 和 CoUninitialize 函数分别用于 COM 对象的初始化和关闭 ,调用这两个函数是必须的,因为 ADO 是一种 COM 方式的接口 。
  • 使用 ADO 访问数据路可能会出现各种错误,因此应该将所有的使用 ADO 的代码放到 try 块中 。对于本教程后面的各个节中的大多数示例代码,拷贝到这个 try 块中即可 。
3.2 连接串和连接对象
创建连接对象

使用以下代码创建并打开一个连接对象 。

将下面代码粘贴到上面代码try块

_bstr_t ConnStr("Provider = SQLOLEDB.1; Persist Security Info = False; User ID = sa; Password = 123456; Initial Catalog = pxscj; Data Source = XT-PC");

_ConnectionPtr Conn(__uuidof(Connection));

Conn->Open(

   ConnStr,

   "",

   "",

   (long)0

);

其中:

  • _bstr_t 的字面意思是“基本的串类型”,这是 一种 COM 特有的数据类型 。调用 _bstr_t 的构造函数,并以一个连接串作为参数,得到一个 _bstr_t 对象 。
  • 接下来调用 _ConnectionPtr 的构造函数,创建并初始化一个连接对象 Conn 。
  • 再接下来调用 Conn 的 Open 方法 。如果调用失败,会产一个出错 。连接对象的类名为 _ConnectionPtr,这个类重载了 -> 运算符,这使得通过这个类的实例(对象)访问成员时,可以使用点(.)运算符,也可以使用箭头(->)运算符,就好像这个类的实例是一个指向这个类的实例的指针,这也就是为什么在这个类的类名中有“Ptr”的字样的原因 。
使用连接对象

在创建并打开了一个连接对象后,通过使用连接对象的 Execute 方法,几乎可以将任意的 SQL 语句作为其参数,从而可以完成很多数据库的操作(增 、删 、查 、改) 。比如:

Conn->Execute(

   "UPDATE xsb SET 备注 = '外校互认学分课程', 总学分 = 总学分 + 3 WHERE 学号 = '231302'",

   NULL,

   (long)0);

对于查询这样的操作,则需要将连接对象 Execute 方法的调用结果返回给一个记录集对象 。

将上面代码粘贴至刚刚try里面(接着刚刚代码块)

点击调试—> 开始执行—>关闭

把刚刚udl用记事本打开的文件,红色框

在这里插入图片描述

复制,删贴到main.cpp文件里面try块

(把上面的连接串替换成自己的连接串)

调试—>执行 返回0 则运行成功

数据库的操作(增 、删 、查 、改)

打开数据库

选择pxscj—>dbo_xsb—>选择前1000行

在这里插入图片描述

则会显示

在这里插入图片描述

在刚刚try代码里面,修改里面的数据

在这里插入图片描述

学号 = '191301'   修改学号为191301的信息

调试—>开始执行

显示

在这里插入图片描述

之后重新进行刚才的操作:

选择pxscj—>dbo_xsb—>选择前1000行

你会发现数据发生改变(跟上面的表图对比)备注和总学分被修改

在这里插入图片描述

3.3 记录集对象

一个记录集对象代表了一些记录的集合,比如一个查询命令的执行结果 。

一个sql语句执行结果就是一个集合,ADO会把集合返回给一个记录集

3.3.1 创建记录对象

示例:

_RecordsetPtr RecordsetObj(__uuidof(Recordset)); //创建记录集对象
//将sql执行结果返回记录集RecordsetObj中
RecordsetObj = ConnObj->Execute(  
   "select * from xsb",
   NULL,
   (long)0);    // 当这段sql语句执行成功后  
// GetString 将sql语句 执行为字符串   wprintf 库函数   wchar_t一个字符占用两个字节 宽字符串
wprintf(L"%s\n", (wchar_t*)RecordsetObj->GetString(adClipString, long(-1), "\t", "\n", ""));   

其中:

  • 首先创建并初始化了一个名字为 RecordsetObj 记录集对象 。
  • 然后这个记录集对象得到了一个查询命令的查询结果。
  • 最后调用这个记录集对象的 GetString 方法,将整个查询结果作为一个串显示。

wprintf()使用

复制上面代码

粘贴至刚刚try块里面:(删除选中部分再粘贴)

在这里插入图片描述

将try块里面的Conn改为ConnObj

然后:调试—>开始执行

在这里插入图片描述

3.3.2 使用记录集对象执行命令

如前所述,可以使用连接对象的 Execute 方法执行命令,如果有返回的记录集,可以将这个记录集返回给一个记录集对象 。使用 记录集对象的 Open 方法,也可以执行命令,但通常是有返回记录集的命令,比如执行 SELECT 语句 。

使用open方法,通常执行命令有返回记录集

_bstr_t ConnStr("Provider = SQLOLEDB.1; Persist Security Info = False; User ID = sa; Password = 123456; Initial Catalog = pxscj; Data Source = XT-PC"); // 定义一个连接串

_ConnectionPtr ConnObj(__uuidof(Connection));  // 定义一个连接对象
// 使用open方法
ConnObj->Open(   
   ConnStr,
   "",
   "",
   NULL );

_RecordsetPtr RecordsetObj(__uuidof(Recordset));  // 定义记录集对象

// 记录集获得整个学生信息
RecordsetObj->Open( 
    // 注意第一个个最后一个参数
   "xsb",  // 参数:表名
   ConnObj.GetInterfacePtr(),
   adOpenForwardOnly,
   adLockReadOnly,
   adCmdTable);   // 表类型

// 表解释为一个字符串,作为字符串显示 
wprintf(L"%s\n", (wchar_t*)RecordsetObj->GetString(adClipString, long(-1), "\t", "\n", ""));

在上述代码中,调用记录集对象 RecordsetObj 的 Open 方法,注意其中的几个参数:

  • 第一个参数是一个表的名字,记录集将得到这个表的所有数据;与这个参数对应的是最后一个参数,指定命令的类型为 adCmdTable ,将第一个参数中的字符串解释为一个表名 。更常见的做法是将第一个参数设置为一个 SQL 查询语句,比如 “select * from xsb”,相应地,将最后一个参数设置为 adCmdText
  • 第二个参数将这个记录集对象与一个连接对象关联起来 。还可以将这个参数直接设置为一个连接字符串,在这种情况下,会导致 ADO 隐式地创建一个连接对象。
  • 第三个和第四个参数分别指定游标的类型和锁的类型 。

删除整个try块里面的代码

在这里插入图片描述

将上面代码粘贴至此处

重复之前一个操作:

把刚刚udl用记事本打开的文件,红色框

在这里插入图片描述

复制,粘贴到main.cpp文件里面try块

(把上面的连接串替换成自己的连接串)

注意:如果里面连接串有反斜线\,需要多加一个反斜线\ ,不然可能出现问题。至于为什么加,我也不知道,老师讲的

在这里插入图片描述

调试—>开始执行

在这里插入图片描述

上面方法存在问题:列没有对齐,没有表头,需要改进

3.3.3 记录集的游标、当前记录、fields集合和字段对象

每个打开的记录集对象都有一个相应的游标,用于访问这个游标指向的一个记录 。

记录集想象成一个二维表,游标就好像是一个指针,游标指向某一个记录,就是当前记录。当我们访问记录集某一个记录,首先让游标指向记录,再操作。

对于一个记录集的当前记录,使用这个记录集对象Fields 属性(这是一个集合)可以访问这个当前记录中的各个字段对象 。

记录集对象有属性有方法。eg 面向对象中,成员变量相当于属性,成员函数相当于方法

Fields 属性:记录集字段属性

(字段集合 eg 姓名 学号 性别 分数) 记录集的字段集合(各个字段的集合)就是字段属性

每个字段对象均有一些诸如名字 、类型和值等属性 。

对于一个集合类型的对象,比如 记录集对象的 Fields 的属性,可以通过使用 GetItem(i) 方法访问其中的第 i+1 个元素(作为另一种选择,不是将一个集合元素的序号作为参数传递,而是传递一个字符串类型的字段名 。 ),通过使用 Count 属性可以确定这个集合中包含的元素的个数 。

游标的类型

一个记录集的对象的游标是有类型的,通常可在调用这个记录集的 Open 方法时指定,比如在前面的示例中,指定游标的类型为 adOpenForwardOnly,这是一种资源开销最小的游标类型,但同时也是功能最弱的一种游标类型 。这种游标类型正如它的名字所暗示的,它是一种只能向前滚动的游标,此外,这种游标没有敏感性,也就是说,在通过这种类型的游标访问记录集中的数据时,无法看到其它客户对这个记录集对应的基本表的修改 。更多的游标的类型见 CursorTypeEnum

录集想象成一个二维表,游标就好像是一个指针

在下面的示例中,不再使用记录集对象的 GetString 方法来显示记录集,而是先显示记录集的各个字段名作为标题,然后再每行显示一个记录 。

// 创建并初始化连接对象
_bstr_t ConnStr("Provider = SQLOLEDB.1; Persist Security Info = False; User ID = sa; Password = 123456; Initial Catalog = pxscj; Data Source = XT-PC"); 
_ConnectionPtr ConnObj(__uuidof(Connection));
ConnObj->Open(
    ConnStr,
    "",
    "",
    NULL);  

_RecordsetPtr RecordsetObj(__uuidof(Recordset));

// 记录集open方法执行sql语句
RecordsetObj->Open(
    "select 学号, 姓名, 性别, 出生时间, 专业, 总学分, 备注 from xsb",  
    ConnObj.GetInterfacePtr(),
    adOpenForwardOnly,
    adLockReadOnly,
    adCmdText);

// 打印表头(粗略方法)
printf("学号\t姓名\t性别\t出生时间\t专业\t总学分\t备注\n"); // 7个字段形成集合

// 打印记录集各个部分

//for循环 :对记录集遍历
// 游标开始指向第一条记录,之后继续指向后面的
// !RecordsetObj->EndOfFile不是指向最后一条记录

// (wchar_t*)(_bstr_t) 两个强制性转换,字符类型,然后宽字符类型
// RecordsetObj->Fields记录集对象  RecordsetObj->Fields->GetItem获取字段 Value获取值  GetItem返回一个字段

getstring 直接获取所有 
for (; !RecordsetObj->EndOfFile; RecordsetObj->MoveNext())
{
    wprintf(L"%s\t", (wchar_t*)(_bstr_t)RecordsetObj->Fields->GetItem("学号")->Value); // 执行第一个for循环,获取学号字段值,打印
    wprintf(L"%s\t", (wchar_t*)(_bstr_t)RecordsetObj->Fields->GetItem("姓名")->Value);  // 执行第二个for循环,获取姓名字段,打印
    wprintf(L"%s\t", (wchar_t*)(_bstr_t)RecordsetObj->Fields->GetItem("性别")->Value);  // ...
    wprintf(L"%s\t", (wchar_t*)(_bstr_t)RecordsetObj->Fields->GetItem("出生时间")->Value);
    wprintf(L"%s\t", (wchar_t*)(_bstr_t)RecordsetObj->Fields->GetItem("专业")->Value);
    wprintf(L"%s\t", (wchar_t*)(_bstr_t)RecordsetObj->Fields->GetItem("总学分")->Value);
    wprintf(L"%s\t", (wchar_t*)(_bstr_t)RecordsetObj->Fields->GetItem("备注")->Value);                 
    printf("\n");
}

上述代码跟之前相比多了表头,并没有什么改进,之后会进行修改

删除下图选中部分,将上述代码复制,粘贴到main.cpp文件里面try块删除部分

(第一行代码不用粘贴,粘贴了需要改成自己的连接串)

在这里插入图片描述

然后,调试—>开始执行

显示

在这里插入图片描述

或者(程序另外一个版本)

使用下面的代码,重复上面的操作

_bstr_t ConnStr("Provider = SQLOLEDB.1; Persist Security Info = False; User ID = sa; Password = 123456; Initial Catalog = pxscj; Data Source = XT-PC");   
_ConnectionPtr ConnObj(__uuidof(Connection));
ConnObj->Open(
    ConnStr,
    "",
    "",
    NULL);
_RecordsetPtr RecordsetObj(__uuidof(Recordset));

// 通过执行一个sql获得记录集对象
RecordsetObj->Open(
    "select 学号, 姓名, 性别, 出生时间, 专业, 总学分, 备注 from xsb",
    ConnObj.GetInterfacePtr(),
    adOpenForwardOnly,
    adLockReadOnly,
    adCmdText);

// 打印表头
// RecordsetObj->Fields->Count  RecordsetObj记录集,Fields方法,Count个数(7,学号, 姓名, 性别...)
// RecordsetObj->Fields->GetItem(i)字段集对象中第i个字段
for (long i = 0; i < RecordsetObj->Fields->Count; i++)
{
    wprintf(L"%s\t", (wchar_t*)RecordsetObj->Fields->GetItem(i)->Name);
}

printf("\n");

// 游标对记录集进行遍历
// 第一个循环指向第一个记录,第二个指向第二个记录...
for(;!RecordsetObj->EndOfFile; RecordsetObj->MoveNext())  // 打印某行
{
    for (long i = 0; i < RecordsetObj->Fields->Count; i++)  // 打印某列,记录集列的个数,每行打印七次
    {
        wprintf(L"%s\t", (wchar_t*)(_bstr_t)RecordsetObj->Fields->GetItem(i)->Value);   // 字段集序号i列引用     之前通过字段名GetItem("专业")
    }
    printf("\n");
}

执行结果

在这里插入图片描述

其中:

  • 在打开记录集对象 RecordsetObj 后,使用一个 for 循环显示这个记录集中的各个字段的名字,起到显示标题的效果 。
  • 再接下来的一个 for 循环用于显示这个记录集中的各个记录(行);在显示每个记录时,又需要一个 for 循环用于显示一个记录中的各个字段(列)的值

刚刚上面的代码存在的bug:

  • 显示格式不如人意 。不要试图穷尽 printf 的各种格式控制来解决这个问题 。

  • 如果某个记录的某个字段为 NULL,执行上述程序会出错 。

    以下是一种可能的较好的解决显示对齐的方案:

_bstr_t ConnStr("Provider = SQLOLEDB.1; Persist Security Info = False; User ID = sa; Password = 123456; Initial Catalog = pxscj; Data Source = XT-PC");

_ConnectionPtr ConnObj(__uuidof(Connection));
ConnObj->Open(
    ConnStr,
    "",
    "",
    NULL);
_RecordsetPtr RecordsetObj(__uuidof(Recordset));
RecordsetObj->Open(
    "select * from xsb",
    ConnObj.GetInterfacePtr(),
    adOpenForwardOnly,
    adLockReadOnly,
    adCmdText);

COORD  pos = { 0,0 };
for (long i = 0; i < RecordsetObj->Fields->Count; i++)
{
    // SetConsoleCursorPosition 很重要的一个函数
    // 获取出一个字符串的宽度,像素
    SetConsoleCursorPosition(
        GetStdHandle(STD_OUTPUT_HANDLE),
        pos
    );
    // 显示字段名
    wprintf(L"%s", (wchar_t*)RecordsetObj->Fields->GetItem(i)->Name);
    
    // RecordsetObj->Fields->GetItem(i)->DefinedSize%16 + 4; 
    pos.X += (short)RecordsetObj->Fields->GetItem(i)->DefinedSize%16 + 4;                    
}
for (; !RecordsetObj->EndOfFile; RecordsetObj->MoveNext())
{
    pos.X = 0;
    pos.Y++;
    for (long i = 0; i < RecordsetObj->Fields->Count; i++)
    {
        SetConsoleCursorPosition(
            GetStdHandle(STD_OUTPUT_HANDLE),
            pos
        );                            
        if( RecordsetObj->Fields->GetItem(i)->Value.vt == VT_NULL )
            wprintf(L"Null");
        else
            wprintf(L"%s", (wchar_t*)(_bstr_t)RecordsetObj->Fields->GetItem(i)->Value);
        pos.X += (short)RecordsetObj->Fields->GetItem(i)->DefinedSize % 16 + 4;                                
    }
}

调试执行显示

在这里插入图片描述

最开始我的显示对齐的,但是最大化黑框就出现了问题,然后调整了窗口大小就好了(至于为什么,我还不知道)

在这里插入图片描述

3.3.4 修改记录

如果要修改某个记录的某个字段的数据,可以将一个 update 语句发送给服务器 。要完成这个工作,可以使用 连接对象的 Execute 方法,可以使用 命令对象的 Execute 方法,也可以使用 记录集对象的 Open 方法 。尽管使用记录集对象的 Open 方法来执行一条不返回任何记录的 SQL 语句,不是通常的做法,但是确实可以这样做,比如:

RecordsetObj->Open(
    "update xsb set 姓名='李四' where 学号='191301'",
    ConnObj.GetInterfacePtr(),
    adOpenForwardOnly,
    adLockReadOnly,
    adCmdText);

使用记录集对象确实可以修改数据,但不是像前面那样发送一个 update 语句给服务器,而是给游标指向的当前记录的 Fields 中的某个 Field 对象的 Value 属性赋值(等价于调用这个 Field 对象的 PutValue 方法) ,然后调用这个 记录集对象的 Update 方法,将这个修改提交给服务器 。

首先需要根据指定的条件,将记录集中的某个记录设置为当前记录,可以使用记录集的各种 Move 方法,或者使用查找方法 。

在本例中,由于查找条件只涉及到一个列,因此可以使用 记录集的 Find 方法 。此外,在打开记录集时,需要改变 Open 调用中的锁类型和游标类型,以支持查找和更新 。

_bstr_t ConnStr("Provider = SQLOLEDB.1; Persist Security Info = False; User ID = sa; Password = 123456; Initial Catalog = pxscj; Data Source = XT-PC");
_ConnectionPtr ConnObj(__uuidof(Connection));
ConnObj->Open(
    ConnStr,
    "",
    "",
    NULL);   

_RecordsetPtr RecordsetObj(__uuidof(Recordset));
RecordsetObj->Open(
    "select * from xsb",
    ConnObj.GetInterfacePtr(),
    adOpenStatic,
    adLockOptimistic,
    adCmdText);


// 修改某某行某某列

// 记录集对象查找方法 游标指向 某个特定记录,学号='191302',定位
RecordsetObj->Find("学号='191302'",0, adSearchForward/*,vtMissing*/);

if (!RecordsetObj->EndOfFile)
{
    // 将学号='191302' 姓名 修改   只是在客户端修改还没有在服务器修改
    RecordsetObj->Fields->GetItem("姓名")->Value = "王五";
    
    // Update将更新结果提交给服务器,服务器才被修改
    RecordsetObj->Update();
}

有两种更新模式,一种是立即更新模式,另一种是批更新模式,前面的这个示例展示的只是立即更新模式,至于 批更新模式 ,详见 记录集的 Update 方法UpdateBatch 方法

3.3.5 插入记录

使用记录集对象的 AddNew 方法可在这个记录集中追加一个新记录,且这个新纪录成为当前记录,然后设置这个记录的各个字段的值,最后调用这个记录集的 Update 方法,将对当前记录所做的更新提交到服务器 。

_bstr_t ConnStr("Provider = SQLOLEDB.1; Persist Security Info = False; User ID = sa; Password = 123456; Initial Catalog = pxscj; Data Source = XT-PC");

_ConnectionPtr ConnObj(__uuidof(Connection));

ConnObj->Open(
    ConnStr,
    "",
    "",
    NULL);
                           
_RecordsetPtr RecordsetObj(__uuidof(Recordset));
// 获取记录集
RecordsetObj->Open(
    "select * from xsb",
    ConnObj.GetInterfacePtr(),
    adOpenStatic,
    adLockOptimistic,
    adCmdText);

// 添加一条新记录,默认为当前记录
RecordsetObj->AddNew();

RecordsetObj->Fields->GetItem("学号")->Value = "000000";
RecordsetObj->Fields->GetItem("姓名")->Value = "王五";
RecordsetObj->Fields->GetItem("性别")->Value = true;
RecordsetObj->Fields->GetItem("出生时间")->Value = "2000/01/01";
RecordsetObj->Fields->GetItem("专业")->Value = "计算机";
RecordsetObj->Fields->GetItem("总学分")->Value = 0;
RecordsetObj->Update();

注意,在上面的示例中,并没有新纪录的备注字段赋值,这是允许的,因为这个字段允许空值(NULL)。此外,AddNew 方法还有一种带参数的用法(详见 AddNew 方法 主题)。这种用户要求为 AddNew 指定两个 VARIANT 类型的参数,这两个参数都是数组(SafeArray),一个用于指定字段名列表,一个用于指定值的列表 。

_bstr_t ConnStr("Provider = SQLOLEDB.1; Persist Security Info = False; User ID = sa; Password = 123456; Initial Catalog = pxscj; Data Source = XT-PC");

_ConnectionPtr ConnObj(__uuidof(Connection));
ConnObj->Open(
    ConnStr,
    "",
    "",
    NULL);
_RecordsetPtr RecordsetObj(__uuidof(Recordset));
RecordsetObj->Open(
    "select * from xsb",
    ConnObj.GetInterfacePtr(),
    adOpenStatic,
    adLockOptimistic,
    adCmdText);
_variant_t FieldName[6]  = { "学号",   "姓名", "性别", "出生时间",   "专业",   "总学分" };
_variant_t FieldValue[6] = { "000001", "王五", true,   "2000/01/01", "计算机", 0 };
SAFEARRAY *psaFieldName  = SafeArrayCreateVector(VT_VARIANT, 0, 6);
SAFEARRAY *psaFieldValue = SafeArrayCreateVector(VT_VARIANT, 0, 6);

for (long i = 0; i < 6; i++)
{
    SafeArrayPutElement(psaFieldName,  &i, &FieldName[i]);
    SafeArrayPutElement(psaFieldValue, &i, &FieldValue[i]);
}
_variant_t FieldNameList, FieldValueList;
FieldNameList.vt  = VT_VARIANT | VT_ARRAY;
FieldValueList.vt = VT_VARIANT | VT_ARRAY;
FieldNameList.parray  = psaFieldName;
FieldValueList.parray = psaFieldValue;
RecordsetObj->AddNew(FieldNameList, FieldValueList);

这种方法不需要调用 Update,因为添加后,ADO会自动调用它 。

3.3.6 删除记录

调用 记录集对象的 Delete 方法 可删除当前记录 。在当前记录被删除后,它仍然是当前记录 。此外,如果是立即更新模式,在调用 Delete 方法后,无需再调用 Update 方法 。

_bstr_t ConnStr("Provider = SQLOLEDB.1; Persist Security Info = False; User ID = sa; Password = 123456; Initial Catalog = pxscj; Data Source = XT-PC");

_ConnectionPtr ConnObj(__uuidof(Connection));
ConnObj->Open(
    ConnStr,
    "",
    "",
    NULL);
                             
_RecordsetPtr RecordsetObj(__uuidof(Recordset));
RecordsetObj->Open(
    "select * from xsb",
    ConnObj.GetInterfacePtr(),
    adOpenStatic,
    adLockOptimistic,
    adCmdText);

RecordsetObj->Find("学号='000000'", 0, adSearchForward/*,vtMissing*/);
if (!RecordsetObj->EndOfFile)
{
    if (MessageBox(NULL, L"删除", L"删除学号为 000000 的学生记录", MB_OKCANCEL) == IDOK)
    {
        RecordsetObj->Delete(adAffectCurrent);
    }
}
3.3.7 记录集的排序

通过执行带有 order by 子句的查询命令。可以得到有序的记录 。通过设置记录集对象的 Sort 属性,也可以得到有序的记录 。要使 Sort 属性起作用,必须在调用记录集对象的 Open 方法之前将这个记录集对象的 CursorLocation 属性设置为 adUseClient(这个属性的缺省值为 adUseServer ),否则在设置 Sort 属性 会导致一个错误 。

_bstr_t ConnStr("Provider = SQLOLEDB.1; Persist Security Info = False; User ID = sa; Password = 123456; Initial Catalog = pxscj; Data Source = XT-PC");

_ConnectionPtr ConnObj(__uuidof(Connection));

ConnObj->Open(

    ConnStr,

    "",

    "",

    NULL);

 

_RecordsetPtr RecordsetObj(__uuidof(Recordset));

RecordsetObj->CursorLocation = adUseClient;

RecordsetObj->Open(

    "select * from xsb",

    ConnObj.GetInterfacePtr(),

    adOpenStatic,

    adLockOptimistic,

    adCmdText);

 

wprintf(L"%s\n\n", (wchar_t*)RecordsetObj->GetString(adClipString, long(-1), "\t", "\n", ""));

RecordsetObj->Sort = "出生时间";

wprintf(L"%s\n\n", (wchar_t*)RecordsetObj->GetString(adClipString, long(-1), "\t", "\n", ""));

RecordsetObj->Sort = "专业, 总学分 desc";

wprintf(L"%s\n\n", (wchar_t*)RecordsetObj->GetString(adClipString, long(-1), "\t", "\n", ""));


RecordsetObj->Close();
ConnObj->Close();

如果认为将会对某些字段执行较多次序的排序或查找操作,那么为这些字段建立索引会有助于提示这些操作的效率 。这里所指的索引,并不是服务器中的某个基本表的索引,而是客户端的记录集的索引 。在设置了一个记录集对象的 CursorLocation 为 adUseClient ,然后打开这个记录集对象后,如果要为一个字段建立索引,那么将这个字段的 Optimize 属性(这是字段对象的一个动态属性)设置为 TRUE 即可 。注意,仅当记录集的 CursorLocation 属性为 adUseClient 时,这个记录集对象中的字段对象才有 Optimize 动态属性 。

_bstr_t ConnStr("Provider = SQLOLEDB.1; Persist Security Info = False; User ID = sa; Password = 123456; Initial Catalog = pxscj; Data Source = XT-PC");

_ConnectionPtr ConnObj(__uuidof(Connection));

ConnObj->Open(

    ConnStr,

    "",

    "",

    NULL);

 

_RecordsetPtr RecordsetObj(__uuidof(Recordset));

RecordsetObj->CursorLocation = adUseClient;

RecordsetObj->Open(

    "select * from xsb",

    ConnObj.GetInterfacePtr(),

    adOpenStatic,

    adLockOptimistic,
 
    adCmdText);
 

RecordsetObj->Fields->GetItem("学号")->Properties->GetItem("Optimize")->Value = TRUE;

RecordsetObj->Sort = "学号 desc";

wprintf(L"%s\n\n", (wchar_t*)RecordsetObj->GetString(adClipString, long(-1), "\t", "\n", ""));

RecordsetObj->Close();
ConnObj->Close();
3.3.8 记录集的过滤

通过执行带有 where 子句的查询命令。可以得具有指定条件的记录 。通过设置记录集对象的 Filter 属性,也可以得到具有指定条件的记录 。

_bstr_t ConnStr("Provider = SQLOLEDB.1; Persist Security Info = False; User ID = sa; Password = 123456; Initial Catalog = pxscj; Data Source = XT-PC");

_ConnectionPtr ConnObj(__uuidof(Connection));

ConnObj->Open(

    ConnStr,

    "",

    "",

    NULL);

 

_RecordsetPtr RecordsetObj(__uuidof(Recordset));

RecordsetObj->CursorLocation = adUseClient;

RecordsetObj->Open(

    "select * from xsb",

    ConnObj.GetInterfacePtr(),

    adOpenStatic,

    adLockOptimistic,

    adCmdText);

 

wprintf(L"%s\n\n", (wchar_t*)RecordsetObj->GetString(adClipString, long(-1), "\t", "\n", ""));

 

RecordsetObj->Filter = "总学分 >= 52 and 姓名 like '王*'";

wprintf(L"%s\n\n", (wchar_t*)RecordsetObj->GetString(adClipString, long(-1), "\t", "\n", ""));

 

RecordsetObj->Filter = "";

wprintf(L"%s\n\n", (wchar_t*)RecordsetObj->GetString(adClipString, long(-1), "\t", "\n", ""));
                              
RecordsetObj->Close();
ConnObj->Close();
3.3.9 记录集的查找

前述的记录集的过滤操作起到了查找的作用,除此之外,可以使用


版权声明:本文为weixin_51249285原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>