在C++中调用 MS SQL 存储过程
Calling an MS SQL Stored Procedure in C++
我正在编写一个连接到MS SQL (2012(服务器上的存储过程的应用程序。该过程用于将数据插入数据库。我在理解如何定义与存储过程的连接、将变量绑定到参数以及将这些参数与存储过程中的变量相关联时遇到了真正的麻烦。我花了几天时间浏览 MSDN 阅读 API 并尝试遵循此处的示例,但似乎有很多方法可以做到这一点,以至于我看不到"树木的木头"。我想我已经有了基本的结构,但我在细节上摔倒了。
以下是我到目前为止的代码。为简单起见,我省略了数据库连接代码。该函数是类"rigDatabase"的一部分,该类具有各种SQL句柄的私有成员。
我遇到的主要问题是对SQLSetDescField的调用。根据Microsoft提供的文档和示例,这些调用应该有效,但返回HY092 - "无效的属性/选项标识符"。这就是我需要帮助的。最近,我尝试记录 ODBC 驱动程序管理器的输出,以查看这是否对此事有所了解。对 SQLSetDescField 的一个调用的输出可以在存储过程定义下面看到。
注意:我还没有尝试将SQL嵌入C的更简单的方法。我正在尝试与现有基础架构(存储过程(接口。
SQLRETURN rigDatabase::send_SQL(const char* filename,
const char* extn,
const char* path,
DWORD& fSize,
const char* rigName,
FILETIME& created,
const char* notes) {
SQLHDESC hIpd = NULL;
SQLINTEGER PartIDInd = 0;
SQL_TIMESTAMP_STRUCT datetime2;
datetime2.year = fileDate.wYear;
datetime2.month = fileDate.wMonth;
datetime2.day = fileDate.wDay;
datetime2.hour = fileDate.wHour;
datetime2.minute = fileDate.wMinute;
datetime2.second = fileDate.wSecond;
datetime2.fraction = fileDate.wMilliseconds;
retcode = SQLPrepareA(sqlStmtHandle, (SQLCHAR*)"{call insertTestRigDataTest(?, ?, ?, ?, ?, ?, ?)}", SQL_NTS);
retcode = SQLBindParameter(sqlStmtHandle, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, FILENAME_MAX, 0, (SQLPOINTER)filename, 0, NULL);
retcode = SQLBindParameter(sqlStmtHandle, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, MAX_PATH, 0, (SQLPOINTER)path, 0, NULL);
retcode = SQLBindParameter(sqlStmtHandle, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, BUF_SIZE, 0, (SQLPOINTER)rigName, 0, NULL);
retcode = SQLBindParameter(sqlStmtHandle, 4, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, (SQLPOINTER)fSize, 0, &PartIDInd);
retcode = SQLBindParameter(sqlStmtHandle, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 4, 0, (SQLPOINTER)extn, 0, NULL);
retcode = SQLBindParameter(sqlStmtHandle, 6, SQL_PARAM_INPUT, SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP, sizeof(SQL_TIMESTAMP_STRUCT), 0, &datetime2, 0, NULL);
retcode = SQLBindParameter(sqlStmtHandle, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 4000, 0, (SQLPOINTER)notes, 0, NULL);
retcode = SQLGetStmtAttrA(sqlStmtHandle, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0);
// All calls to SQLSetDescField below return -1
// SQLGetDiagRecA returns "Invalid attribute/option identifier"
retcode = SQLSetDescField(hIpd, 1, SQL_DESC_NAME, "@Filename", SQL_NTS);
retcode = SQLSetDescField(hIpd, 2, SQL_DESC_NAME, "@Path", SQL_NTS);
retcode = SQLSetDescField(hIpd, 3, SQL_DESC_NAME, "@Rigname", SQL_NTS);
retcode = SQLSetDescField(hIpd, 4, SQL_DESC_NAME, "@Size", SQL_NTS);
retcode = SQLSetDescField(hIpd, 5, SQL_DESC_NAME, "@Extn", SQL_NTS);
retcode = SQLSetDescField(hIpd, 6, SQL_DESC_NAME, "@Created", SQL_NTS);
retcode = SQLSetDescField(hIpd, 7, SQL_DESC_NAME, "@Notes", SQL_NTS);
retcode = SQLExecute(sqlStmtHandle);
return EXIT_FAILURE;
}
// Function to convert from FILETIME to int64
unsigned __int64 FILETIME_to_int64( const FILETIME& ac_FileTime ) {
ULARGE_INTEGER lv_Large;
lv_Large.LowPart = ac_FileTime.dwLowDateTime;
lv_Large.HighPart = ac_FileTime.dwHighDateTime;
return lv_Large.QuadPart;
}
MS SQL 存储过程
USE [TestRigDataTest]
GO
/****** Object: StoredProcedure [dbo].[insertTestRigDataTest] Script Date: 16/10/2018 10:14:34 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[insertTestRigDataTest]
(
@fileName nvarchar(255),
@path nvarchar(255),
@rigName nvarchar(255),
@size [numeric](18, 0),
@extn nvarchar(255),
@created [datetime],
@notes nvarchar(4000) = NULL
)
AS
BEGIN
SET NOCOUNT ON;
declare @id int
declare @tmpNotes nvarchar(4000)
set @tmpNotes=''
select @id=id,@tmpNotes=notes from [TestRigDataTest].[dbo].[RigData]
where
[Filename]=@filename and
[Path]=@Path and
[Size]=@Size and
[Extension]=@Extn and
[created]=@created
if @@rowcount=0
begin
INSERT INTO [TestRigDataTest].[dbo].[RigData] (
[Filename], [Path], [Rigname] ,[UploadDate] ,[Size] ,[Extension] ,[created] ,[notes]
)
VALUES (
@fileName, @path, @rigName, getdate(), @size, @extn, @created, @notes
)
end
else
begin
if @notes != ''
begin
update [TestRigDataTest].[dbo].[RigData] set [notes]=@tmpNotes + CHAR(13)+CHAR(10) + @notes
where id=@id
end
end
END
ODBC Driver Manager Partial Trace Log:NB:SQLSetDescField is #defined to SQLSetDescFieldW
AirCatFeeder 26f0-1e50 ENTER SQLSetDescFieldW
SQLHDESC 0x00000000004CB9E8
SQLSMALLINT 2
SQLSMALLINT 1011 <SQL_DESC_NAME>
SQLPOINTER 0x000000013FBA15EC [ -3] "??h 0"
SQLINTEGER -3
AirCatFeeder 26f0-1e50 ENTER SQLSetDescField
SQLHDESC 0x00000000004CB9E8
SQLSMALLINT 2
SQLSMALLINT 1011 <SQL_DESC_NAME>
SQLPOINTER 0x000000013FBA15EC [ -3] "@Path 0"
SQLINTEGER -3
AirCatFeeder 26f0-1e50 EXIT SQLSetDescField with return code -1 (SQL_ERROR)
SQLHDESC 0x00000000004CB9E8
SQLSMALLINT 2
SQLSMALLINT 1011 <SQL_DESC_NAME>
SQLPOINTER 0x000000013FBA15EC [ -3] "@Path 0"
SQLINTEGER -3
DIAG [HY092] [Microsoft][ODBC SQL Server Driver]Invalid attribute/option identifier (0)
AirCatFeeder 26f0-1e50 EXIT SQLSetDescFieldW with return code -1 (SQL_ERROR)
SQLHDESC 0x00000000004CB9E8
SQLSMALLINT 2
SQLSMALLINT 1011 <SQL_DESC_NAME>
SQLPOINTER 0x000000013FBA15EC [ -3] "??h 0"
SQLINTEGER -3
DIAG [HY092] [Microsoft][ODBC SQL Server Driver]Invalid attribute/option identifier (0)
问题是使用宽字符串。SQLSetDescField
#definesSQLSetDescFieldW
,但字符串文字没有被标记为宽字符串。在字符串定义之前,我缺少"L"。下面是正确代码的示例。
retcode = SQLSetDescField(hIpd, 1, SQL_DESC_NAME, L"@filename", SQL_NTS);
有趣的是,我尝试使用MSDN的演示代码,该代码基于宽字符串,但是他们自己的代码出现了此错误。当你试图学习时,这非常令人沮丧,但也许它们就像旧的麦卡诺套件一样 - 故意犯错误。我当然不会忘记这一课!
- 在C++中调用 MS SQL 存储过程
- C++使用存储过程返回结果
- Visual样本存储过程从C 程序执行
- 多次调用存储过程时C++连接器"Commands out of sync" mySQL
- SQL Server 2016 CLR 存储过程错误:"A system assertion check has failed"
- 将大量数据从C 应用程序传递到SQLServer存储过程的最快方法是什么?
- 在我的代码中执行存储过程时出现问题
- 读取 Firebird 存储过程的返回值
- 在C++中通过SOCI/ODBC实现SQL Server存储过程
- 如何使用 Oracle 存储过程中C++函数
- 使用 QSqlQuery 检索 MySQL 存储过程的输出
- C++/CLI 存储过程找不到资源
- ADO 不支持存储过程中的 XML
- MSSQL服务器存储过程在从c++调用时不返回输出参数
- ODBC,SQL_SUCCESS_WITH_INFO,游标类型已更改 - 发生在具有单个 SELECT 的存储过程上
- SQL Server-使用C++ODBC调用存储过程
- 通过OCI调用Oracle存储过程,并在C++中使用out-ref游标返回结果
- c++内部的sqlite3(带有TABLE及其INDEX的存储过程或复杂sql)
- 使用dbffcmd和dbrpcsend从c++调用SQL Server存储过程
- 为什么SQL服务器将值传递给存储过程中的OUTPUT变量?