记录集:参数化记录集 (ODBC)
本主题适用于 MFC ODBC 类。
有时,你可能想能够使用从最终用户计算或获取的信息在运行时选择记录。 记录集参数使你能够实现此目标。
本主题介绍:
参数化记录集
参数化记录集使你能够在运行时传递参数信息。 此操作将产生两个重要的影响:
-
它可能会加快执行速度。
-
它使你能够根据在设计时不可用的信息(例如从用户获取的信息或在运行时计算的信息)在运行时生成查询。
当你调用 Open
运行查询时,记录集将使用参数信息来完成其 SQL SELECT 语句。 你可以参数化任何记录集。
何时使用参数
参数的典型用途包括:
-
将运行时参数传递给预定义的查询。
若要将参数传递给存储过程,必须在调用
Open
时使用参数占位符指定完整的自定义 ODBC CALL 语句,从而替代记录集的默认 SQL 语句。 有关详细信息,请参阅类库参考中的 CRecordset::Open 和 SQL:自定义记录集的 SQL 语句 (ODBC) 和记录集:为预定义查询声明一个类 (ODBC)。 -
使用不同的参数信息有效地执行大量再次查询。
例如,每当最终用户在学生注册数据库中查找特定学生的信息时,你可以将学生的姓名或 ID 指定为从用户获取的参数。 然后,当你调用记录集的
Requery
成员函数时,查询仅会选择该学生的记录。记录集的筛选器字符串(存储在
m_strFilter
中)可能如下所示:C++"StudentID = ?"
假设你在变量
strInputID
中获取学生 ID。 当你将参数设置为strInputID
时(例如,学生 ID 为 100),变量的值会绑定到由筛选器字符串中的“?”表示的参数占位符。按如下所示分配参数值:
C++strInputID = "100"; ... m_strParam = strInputID;
你不希望以这种方式设置筛选器字符串:
C++m_strFilter = "StudentID = 100"; // 100 is incorrectly quoted // for some drivers
有关如何正确针对筛选器字符串使用引号的讨论,请参阅记录集:筛选记录 (ODBC)。
每当再次查询新学生 ID 的记录集时,参数值都是不同的。
提示
使用参数比使用简单的筛选器更高效。 对于参数化记录集,数据库必须只处理一次 SQL SELECT 语句。 对于没有参数的已筛选的记录集,每次当你使用新筛选器值进行
Requery
时,SELECT 语句都必须被处理。
有关筛选器的详细信息,请参阅记录集:筛选记录 (ODBC)。
参数化记录集类
备注
本部分适用于从 CRecordset
派生的对象,其中尚未实现批量提取行。 如果使用的是批量行提取,实现参数则是一个类似的过程。 有关详细信息,请参阅记录集:批量提取记录 (ODBC)。
创建记录集类之前,请确定所需的参数、其数据类型以及记录集如何使用它们。
参数化记录集类
备注
MFC ODBC 使用者向导在 Visual Studio 2019 及更高版本中不可用。 你仍可以手动创建此功能。
-
从“添加类”运行 MFC ODBC 使用者向导以创建类。
-
为记录集的列指定字段数据成员。
-
当向导将类写入项目中的文件后,转到 .h 文件并手动将一个或多个参数数据成员添加到类声明中。 添加可能类似于以下示例,快照类的一部分旨在回答查询“哪些学生在高年级?”
C++class CStudentSet : public CRecordset { // Field/Param Data CString m_strFirstName; CString m_strLastName; CString m_strStudentID; CString m_strGradYear; CString m_strGradYrParam; };
在向导生成的字段数据成员后面添加参数数据成员。 约定是将“Param”一词追加到每个用户定义的参数名称。
-
修改 .cpp 文件中的 DoFieldExchange 成员函数定义。 为添加到类中的每个参数数据成员添加 RFX 函数调用。 有关编写 RFX 函数的信息,请参阅记录字段交换:RFX 的工作方式。 通过单个调用将针对参数的 RFX 调用置于前面:
C++pFX->SetFieldType( CFieldExchange::param ); // RFX calls for parameter data members
-
在记录集类的构造函数中,递增参数计数,
m_nParams
。有关详细信息,请参阅记录字段交换:使用向导代码。
-
在编写创建此类的记录集对象的代码时,请在 SQL 语句字符串中要替换参数的每个位置中放置一个“?”(问号)符号。
在运行时,“?”占位符是按顺序由传递的参数值填充。 在 SetFieldType 调用之后设置的第一个参数数据成员将替换 SQL 字符串中的第一个“?”,第二个参数数据成员将替换第二个“?”,依此类推。
备注
参数顺序是非常重要的:DoFieldExchange
函数中针对参数的 RFX 调用的顺序必须与 SQL 字符串中参数占位符的顺序相匹配。
提示
最可能使用的字符串是为类的 m_strFilter 数据成员指定的字符串(如果有),但某些 ODBC 驱动程序可能允许其他 SQL 子句中的参数。
在运行时传递参数值
在调用 Open
(针对新记录集对象)或 Requery
(针对现有记录集对象)之前必须指定参数值。
在运行时将参数值传递给记录集对象
-
构造记录集对象。
-
准备一个或多个字符串,例如
m_strFilter
字符串,包含 SQL 语句或其中一部分。 将“?”占位符放置在参数信息要转至的位置。 -
将运行时参数值分配给对象的每个参数数据成员。
-
调用
Open
成员函数(针对现有记录集,则调用Requery
)。
例如,假设要使用在运行时获取的信息为记录集指定筛选器字符串。 假设你之前已构造了一个 CStudentSet
类的记录集(称为 rsStudents
),现在想再次查询特定类型的学生信息。
// Set up a filter string with
// parameter placeholders
rsStudents.m_strFilter = "GradYear <= ?";
// Obtain or calculate parameter values
// to pass--simply assigned here
CString strGradYear = GetCurrentAcademicYear( );
// Assign the values to parameter data members
rsStudents.m_strGradYrParam = strGradYear;
// Run the query
if( !rsStudents.Requery( ) )
return FALSE;
记录集包含那些记录符合筛选器所指定条件的学生的记录,筛选器是根据运行时参数构造的。 在这种情况下,记录集包含所有高年级学生的记录。
备注
如果需要,可以使用 SetParamNull 将参数数据成员的值设置为 Null。 同样可以使用 IsFieldNull 检查参数数据成员是否为 Null。
记录集:为预定义查询声明一个类 (ODBC)
备注
MFC ODBC 使用者向导在 Visual Studio 2019 及更高版本中不可用。 你仍可以手动创建使用者。
本主题适用于 MFC ODBC 类。
本主题介绍如何为预定义查询(有时称为存储过程,例如在 Microsoft SQL Server 中)创建记录集类。
备注
本主题适用于从 CRecordset
派生的对象,其中尚未实现批量提取行。 如果实现了批量提取行,此过程则非常相似。 若要了解实现大容量行提取的记录集与不实现大容量行提取的记录集之间的差异,请参阅记录集:使用 ODBC (提取) 。
某些数据库管理系统 (DBMS) 使你能够创建预定义查询并从程序中像函数一样进行调用。 查询具有名称,可能会采用参数,并且可能会返回记录。 本主题中的过程描述了如何调用返回记录(可能还会采用参数)的预定义查询。
数据库类不支持更新预定义查询。 快照预定义查询和动态集预定义查询之间的差异不具有可更新性,但无论是否是其他用户(或程序中的其他记录集)所进行的更改均在记录集中可见。
提示
不需要记录集即可调用不返回记录的预定义查询。 尽管可以如下所述准备 SQL 语句,但需要通过调用 CDatabase
成员函数 CDatabase
来执行它。
尽管可以创建一个单一记录集类来管理调用预定义查询,但有一些工作必须要亲自完成。 向导不支持专门为此目的创建类。
创建用于调用预定义查询(存储过程)的类
-
使用“添加类”中的 MFC ODBC 使用者向导为表创建一个记录集类,此表提供了查询返回的大部分列。 这使你能够为接下来的操作做好准备。
-
手动为查询返回的但向导未创建的任何表的任何列添加字段数据成员。
例如,如果查询从另外两个表中返回三列,则向此类添加六个字段数据成员(具有适当的数据类型)。
-
在类的 DoFieldExchange 成员函数中手动添加 RFX 函数调用,对应每个添加的字段数据成员的数据类型。
C++Immediately before these RFX calls, call <MSHelp:link keywords="_mfc_CFieldExchange.3a3a.SetFieldType" TABINDEX="0">SetFieldType</MSHelp:link>, as shown here: pFX->SetFieldType( CFieldExchange::outputColumn );
备注
必须知道结果集中返回的数据类型和列的顺序。
DoFieldExchange
中 RFX 函数调用的顺序必须与结果集列的顺序匹配。 -
在记录集类构造函数中手动添加新字段数据成员的初始化。
还必须递增 m_nFields 数据成员的初始化值。 尽管向导将编写初始化,但它仅涵盖其添加的字段数据成员。 例如:
C++m_nFields += 6;
某些数据类型不应在此处进行初始化,例如,
CLongBinary
或字节数组。 -
如果查询会采用参数,则为每个参数添加参数数据成员、RFX 函数调用和初始化。
-
必须为每个添加的参数递增
m_nParams
,就像在此过程的步骤 4 中为添加的字段执行m_nFields
一样。 有关详细信息,请参阅 记录集:将 记录集参数化 (ODBC) 。 -
采用以下格式手动编写 SQL 语句字符串:
{CALL proc-name [(? [, ?]...)]}
其中“CALL”是 ODBC 关键字,“proc-name”是数据源上已知的查询名称,“?”项是你在运行时提供给记录集的参数值的占位符(如果有)。 以下示例为一个参数准备占位符:
CString mySQL = "{CALL Delinquent_Accts (?)}";
-
在打开记录集的代码中,设置记录集的参数数据成员的值,然后调用
Open
成员函数,从而为 lpszSQL 参数传递你的 SQL 字符串Open
。 或者,替换类中由GetDefaultSQL
成员函数返回的字符串。
以下示例显示了调用名为 Delinquent_Accts
的预定义查询的过程,这将为销售地区编号采用一个参数。 此查询返回三列:Acct_No
、L_Name
、Phone
。 所有列都来自“客户”表。
以下记录集指定查询返回的列的字段数据成员以及在运行时请求的销售地区编号的参数。
class CDelinquents : public CRecordset
{
// Field/Param Data
LONG m_lAcct_No;
CString m_strL_Name;
CString m_strPhone;
LONG m_lDistParam;
// ...
};
除了手动添加的 m_lDistParam
成员外,此类声明与向导所编写的一样。 此处不显示其他成员。
下一个示例演示了 CDelinquents
构造函数中数据成员的初始化。
CDelinquents::CDelinquents(CDatabase* pdb)
: CRecordset(pdb)
{
// Wizard-generated params:
m_lAcct_No = 0;
m_strL_Name = "";
m_strPhone = "";
m_nFields = 3;
// User-defined params:
m_nParams = 1;
m_lDistParam = 0;
}
请注意 m_nFields 和 m_nParams 的初始化。 向导将初始化 m_nFields
;你需要初始化 m_nParams
。
下一个示例演示了 CDelinquents::DoFieldExchange
中的 RFX 函数:
void CDelinquents::DoFieldExchange(CFieldExchange* pFX)
{
pFX->SetFieldType(CFieldExchange::outputColumn);
RFX_Long(pFX, "Acct_No", m_lAcct_No);
RFX_Text(pFX, "L_Name", m_strL_Name);
RFX_Text(pFX, "Phone", m_strPhone);
pFX->SetFieldType(CFieldExchange::param);
RFX_Long(pFX, "Dist_No", m_lDistParam);
}
除了为三个返回的列进行 RFX 调用外,此代码还管理在运行时传递的参数的绑定。 此参数与 Dist_No
(地区编号)相匹配。
下一个示例演示了如何设置 SQL 字符串以及如何使用它来打开记录集。
// Construct a CDelinquents recordset object
CDelinquents rsDel( NULL );
CString strSQL = "{CALL Delinquent_Accts (?)}"
// Specify a parameter value (obtained earlier from the user)
rsDel.m_lDistParam = lDistrict;
// Open the recordset and run the query
if( rsDel.Open( CRecordset::snapshot, strSQL ) )
// Use the recordset ...
此代码将构造一个快照,向它传递先前从用户处获取的参数,并调用预定义查询。 查询运行时,它将返回指定销售地区的记录。 每条记录都包含帐号、客户姓氏和客户电话号码的列。
提示
你可能需要处理来自存储过程的返回值(输出参数)。 有关详细信息和示例,请参阅 CFieldExchange::SetFieldType。
另请参阅
记录集 (ODBC)
记录集:使用 ODBC (重新)
记录集:为 ODBC 表声明 (类)
记录集:在 ODBC (执行联接)
Recordset: Architecture (ODBC)
| Overview | How Do I | FAQ | Sample | | ODBC Driver List
This article applies to the MFC ODBC classes. For DAO recordsets, see the article DAO Recordset.
This article describes the data members that comprise the architecture of a recordset object:
-
Field data members
-
Parameter data members
-
Using m_nFields and m_nParams data members
****Note ****This article applies to objects derived from CRecordset in which bulk row fetching has not been implemented. If bulk row fetching is implemented, the architecture is similar. To understand the differences, see the article Recordset: Fetching Records in Bulk (ODBC).
A Sample Class
When you use ClassWizard to declare a recordset class derived from CRecordset, the resulting class has the general structure shown in the following simple class:
class CCourse : CRecordset
{
//{{AFX_FIELD(CCourse, CRecordset)
CString m_strCourseID;
CString m_strCourseTitle;
//}}AFX_FIELD
CString m_strIDParam;
};
At the beginning of the class, ClassWizard writes a set of field data members. When you create the class with ClassWizard, you must specify one or more field data members. If the class is parameterized, as the sample class is (with the data member m_strIDParam
), you must manually add parameter data members. ClassWizard doesn’t support adding parameters to a class.
Field Data Members
The most important members of your recordset class are the field data members. For each column you select from the data source, the class contains a data member of the appropriate data type for that column. For example, the sample class shown at the beginning of this article has two field data members, both of type CString, called m_strCourseID
and m_strCourseTitle
.
When the recordset selects a set of records, the framework automatically “binds” the columns of the current record (after the Open call, the first record is current) to the field data members of the object. That is, the framework uses the appropriate field data member as a buffer in which to store the contents of a record column.
As the user scrolls to a new record, the framework uses the field data members to represent the current record. The framework refreshes the field data members, replacing the previous record’s values. The field data members are also used for updating the current record and for adding new records. As part of the process of updating a record, you specify the update values by assigning values directly to the appropriate field data member(s).
Parameter Data Members
If the class is “parameterized,” it has one or more parameter data members. A parameterized class lets you base a recordset query on information obtained or calculated at run time.
****Note ****You must manually place these data members outside the “//{{AFX_FIELD” comment brackets.
Typically, the parameter helps narrow the selection, as in the following example. Based on the sample class at the beginning of this article, the recordset object might execute the following SQL statement:
SELECT CourseID, CourseTitle FROM Course WHERE CourseID = ?
The “?” is a placeholder for a parameter value that you supply at run time. When you construct the recordset and set its m_strIDParam
data member to “MATH101”, the effective SQL statement for the recordset becomes:
SELECT CourseID, CourseTitle FROM Course WHERE CourseID = MATH101
By defining parameter data members, you tell the framework about parameters in the SQL string. The framework binds the parameter, which lets ODBC know where to get values to substitute for the placeholder. In the example, the resulting recordset contains only the record from the Course table with a CourseID column whose value is “MATH101”. All specified columns of this record are selected. You can specify as many parameters (and placeholders) as you need.
****Note ****MFC does nothing itself with the parameters — in particular, it doesn’t perform a text substitution. Instead, MFC tells ODBC where to get the parameter; ODBC retrieves the data and performs the necessary parameterization.
****Important ****The order of parameters is important. For details about this and more information about parameters, see the article Recordset: Parameterizing a Recordset (ODBC).
Using m_nFields and m_nParams
When ClassWizard writes a constructor for your class, it also initializes the data member, which specifies the number of field data members in the class. If you add any parameters to your class, you must also add an initialization for the data member, which specifies the number of parameter data members. The framework uses these values to work with the data members.
For more information and examples, see the article Record Field Exchange: Using RFX.
See Also Recordset: Declaring a Class for a Table (ODBC), Record Field Exchange
ClassWizard: Creating a Recordset Class
This article explains how to create a recordset class with ClassWizard.
You'll need a new -derived class (for ODBC) or -derived class (for DAO) for each table, join of tables, or predefined query you work with in your program. Each recordset class specifies its own set of columns and may also specify parameters. For information about the structure of your recordset class and its uses, see the article Recordset: Architecture (ODBC) or DAO Recordset: Architecture.
For information about mapping recordset field data members to columns in the table, see the article ClassWizard: Binding Recordset Fields to Table Columns.
For information about using a (ODBC) for a join of tables, see the article Recordset: Performing a Join (ODBC). (Performing a join with DAO is similar.)
For information about using a CRecordset for a predefined query, see the article Recordset: Declaring a Class for a Predefined Query (ODBC). For information about predefined ("saved") queries in DAO, see the article DAO Querydef.
Note When creating CRecordset-derived or CDaoRecordset-derived classes with ClassWizard, be careful when selecting multiple tables or queries. Selecting multiple tables or queries will result in the construction of a JOIN query without any restriction on how to perform the JOIN (called a cross product or a Cartesian product JOIN). You may wish to specify a filter using or (resulting in MFC building an SQL WHERE clause) before the recordset is opened. This will constrain the number of records in the result set. This is especially necessary when using the ODBC Cursor Library, since the Cursor Library may create a large temporary file for result sets with many records.
The table below shows the major steps in creating a recordset class.
Recordset Class Creation Summary
To | Do this |
Create the class | Use the Add Class dialog box in ClassWizard. |
Select a data source and database table for the class | Select options in the Database Options dialog box. These include a data source, possibly a recordset type, and possibly some advanced options. Then specify details about the data source in the Select Database dialog box (for ODBC) or the Open dialog box (for DAO). Next, use the Select Database Tables dialog box to select tables from those available on the data source. |
Remove any column mappings you don't want. By default, AppWizard and ClassWizard bind all columns in the table to recordset field data members | Select a column name on the Member Variables tab and choose Delete Variable. |
Optionally parameterize the underlying SQL statements | Manually add parameter data members, or, in DAO, base your recordset on a parameterized a querydef object. |
Optionally use dialog data exchange (DDX) to map recordset data members to controls in a record view | See the article Record Views. |
For more information about parameterizing your class, see the article Recordset: Parameterizing a Recordset (ODBC) or the article DAO Queries: Filtering and Parameterizing Queries. For information about using DDX between record view controls and recordset data members, see the article ClassWizard: Mapping Form Controls to Recordset Fields.
To create the recordset class
You can add a new class derived from class or in ClassWizard's Add Class dialog box. This dialog box is available from any tab in ClassWizard. (All figures follow the procedure below.)
-
On the View menu, click ClassWizard.
-
Click Add Class and select New from the drop-down list.
The Add Class dialog box appears (see figure below).
-
In the Name box, enter a name for the class; in the Filenames box, enter filenames for its .h and .cpp files.
-
From the Base class drop-down list, select as the base-class type of the new class. If you're using the MFC DAO classes, select .
-
Click OK.
The Database Options dialog box appears (see figure below).
-
Choose ODBC or DAO. Then select a data source:
-
For ODBC, select from the drop-down list. Depending on what you choose, you might need to make a further selection in an Open dialog box. If the data source is on a server, you might be prompted to log in to the server.
-
For DAO, click the browse button beside the DAO edit control. Then, in the Open dialog box, navigate to the database file you want to use. (By default, the dialog box displays only Microsoft Jet databases, .mdb, but you can open any database that the Microsoft Jet database engine can read. For information, see the article Databases: Overview.
-
-
Select the Recordset type (Snapshot, Dynaset, or Table) and Advanced features you want the class to have.
-
Click OK.
-
In the Select Database Tables dialog box (see figure below), select the name(s) of the table(s) you want.
Click OK.
On ClassWizard's Member Variables tab, notice that ClassWizard binds all of the table's columns to recordset field data members. For information about removing bindings you don't want, see the article ClassWizard: Binding Recordset Fields to Table Columns.
-
When you finish, click OK to close ClassWizard. ClassWizard writes your class files in the specified directory and adds them to your project.
Specifying Information for a New Class
Selecting a Data Source in ClassWizard
Selecting a Database Table in ClassWizard
See Also ClassWizard: Database Support, ClassWizard: Binding Recordset Fields to Table Columns
记录集:获取 SUM 及其他聚合结果 (ODBC)
备注
MFC ODBC 使用者向导在 Visual Studio 2019 及更高版本中不可用。 你仍可以手动创建使用者。
本主题适用于 MFC ODBC 类。
本主题介绍如何使用以下 SQL 关键字来获取聚合结果:
-
SUM 计算数值数据类型列中的值的总和。
-
MIN 提取数值数据类型列中的最小值。
-
MAX 提取数值数据类型列中的最大值。
-
AVG 计算数值数据类型列中所有值的平均值。
-
COUNT 计算任何数据类型列中的记录数。
可以使用这些 SQL 函数来获取有关数据源中记录的统计信息,而不是用于从数据源中提取记录。 创建的记录集通常由包含值的单个记录(如果所有列为聚合)组成。 (如果使用了 GROUP BY 子句,则可能存在多个记录。)此值是 SQL 函数执行的计算或提取的结果。
提示
若要在 SQL 语句中添加 SQL GROUP BY 子句(可能还有“HAVING”子句),请将其附加到 m_strFilter
的末尾。 例如:
m_strFilter = "sales > 10 GROUP BY SALESPERSON_ID";
可以通过对列进行筛选和排序来限制用于获取聚合结果的记录数。
注意
某些聚合运算符将从其聚合的列返回不同的数据类型。
-
SUM 和 AVG 可能会返回下一个更大的数据类型(例如,使用
int
进行调用会返回 LONG 或double
)。 -
不管目标列的类型是什么,“COUNT”通常会返回“LONG”。
-
“MAX”和“MIN”返回与其计算列相同的数据类型。
例如,“添加类”向导会创建
long
m_lSales
以容纳 Sales 列,但需要将此替换为double m_dblSumSales
数据成员以容纳聚合结果。 请参阅以下示例。
获取记录集的聚合结果
-
创建记录集,如添加 MFC ODBC 使用者中所述,其中包含要从中获取聚合结果的列。
-
修改记录集的 DoFieldExchange 函数。 将表示列名称的字符串(RFX 函数调用的第二个参数)替换为表示列的聚合函数的字符串。 例如,将:
RFX_Long(pFX, "Sales", m_lSales);
替换为:
RFX_Double(pFX, "Sum(Sales)", m_dblSumSales)
-
打开记录集。 聚合操作的结果保留在
m_dblSumSales
中。
备注
向导实际将分配没有匈牙利语前缀的数据成员名称。 例如,向导将为“Sales”列生成 m_Sales
,而不是之前用于图中的 m_lSales
名称。
如果使用 CRecordView 类来查看数据,则必须更改 DDX 函数调用以显示新的数据成员值;在这种情况下,更改:
DDX_FieldText(pDX, IDC_SUMSALES, m_pSet->m_lSales, m_pSet);
到:
DDX_FieldText(pDX, IDC_SUMSALES, m_pSet->m_dblSumSales, m_pSet);
另请参阅
南来地,北往的,上班的,下岗的,走过路过不要错过!
======================个性签名=====================
之前认为Apple 的iOS 设计的要比 Android 稳定,我错了吗?
下载的许多客户端程序/游戏程序,经常会Crash,是程序写的不好(内存泄漏?刚启动也会吗?)还是iOS本身的不稳定!!!
如果在Android手机中可以简单联接到ddms,就可以查看系统log,很容易看到程序为什么出错,在iPhone中如何得知呢?试试Organizer吧,分析一下Device logs,也许有用.