此问题是Microsoft ODBC中一系列错误的一部分 驱动程序:
- ODBC driver fails to raise errors; but instead suppresses them
- Reading columns out of order returns incorrect results
- Cannot execute a stored procedure that is a SYNONYM
微软表示他们不会在他们的ODBC中修复这些错误 驱动程序。
如果我有样本表:
CREATE TABLE Wallet (
WalletID int NOT NULL,
Name varchar(50) NOT NULL
)
我尝试发出插入表中的sql而不指定 NOT NULL WalletID列的值:
INSERT INTO Wallet (WalletID, Name) VALUES (1, 'Fan')
INSERT INTO Wallet (Name) VALUES ('Ardent Defender') --Constraint violation
SQL Server出错:
(1行受影响)
Msg 515,Level 16,State 2,Line 2
无法将值NULL插入列' WalletID',table' Scratch.dbo.Wallet&#39 ;;列不允许空值。 INSERT失败。
声明已经终止。
这是有道理的。
使用ADO / OLEDB和SQL Server OLE DB提供程序(SQLOLEDB)进行连接时:
Provider = SQLOLEDB; Data Source = hyperion; User ID = Contoso; Password = Trub4dor;
我执行 INSERT ,ADO / OLEDB / COM基础设施返回失败,作为例外,它返回高级语言:
无法将值NULL插入列'电子钱包ID',表'电子钱包&#39 ;;列不允许空值。 INSERT失败
这一切都有道理。
通过对本机客户端 OLE DB提供程序(以及the MS recommendation that you do not use the native client ODBC drivers)的劝告,我想我会尝试使用 SQL Server ODBC驱动程序:
Provider = MSDASQL; Driver = {SQL Server}; Server = {hyperion}; UID = {Contoso}; PWD = {Trub4dor};
更新 - 未完成:六年后,Microsoft发布了announced the un-deprecation of OLE DB support for SQL Server,并为SQL Server创建了第三个OLE DB驱动程序:msoledbsql。 (archive)
以前,Microsoft announced deprecation of the Microsoft OLE DB Provider for SQL Server是SQL Server Native Client(SNAC)的一部分。当时,当我们使用Azure SQL数据库进入云时代时,我们决定尝试为Windows本机软件开发提供更简单的开发人员故事,并尝试利用JDBC和ODBC的相似性为开发人员。但是,在随后的审核过程中,确定弃用是一个错误,因为SQL Server中的实际场景仍然依赖于OLE DB,而更改这些场景会破坏一些现有的客户场景。
考虑到这一点,我们决定取消激活OLE DB 并在2018年3月的第一季度发布新版本。
我发出我的批次:
INSERT INTO Wallet (WalletID, Name) VALUES (1, 'Fan')
INSERT INTO Wallet (Name) VALUES ('Ardent Defender')
我很惊讶地得知同样的SQL语句:
使用ODBC驱动程序时将静默失败。该语句执行时不会引发任何错误。
我的SQL语句在没有错误的情况下运行了大约一个小时,但是这些行不会出现在数据库中。
显然,无声的失败并不好。
但是发生了什么?
如何告诉ADO-OLEDB-ODBC驱动程序报告错误。它是ODBC连接字符串设置吗?它是MSDASQL连接字符串设置吗?
我实际上正在使用Delphi和ADO。但我会将其转码为伪C#样式代码,以便于概念理解。
String commandText =
"INSERT INTO Wallet (WalletID, Name) VALUES (1, 'Fan')"+CRLF+
"INSERT INTO Wallet (Name) VALUES ('Ardent Defender')";
ADOConnection conn = new ADOConnection();
conn.ConnectionString = szConnectionString;
conn.Open();
HRESULT hr = conn.Execute(commandText, ref recordsAffected, [eoExecuteNoRecords]);
实际上, HRESULT 的检查由语言基础结构和编译器魔术处理 - 如果 FAILED
则抛出本机语言异常。
答案 0 :(得分:3)
除非您指定SET NOCOUNT ON,否则SQL Server会在每个语句后向客户端返回DONE_IN_PROC
条消息。这些消息(行计数)将影响未编码的ADO应用程序,以处理通过调用NextRecordset
方法返回的多个记录集。
简单的解决方案是将SET NOCOUNT ON
指定为批处理或存储过程中的第一个语句。
答案 1 :(得分:2)
我没有仔细阅读您的问题,因此我的评论提到错误的ODBC驱动程序。
无论如何,我使用针对SQL Server 2014的三种不同的ODBC驱动程序对其进行了测试:
更新:对于所有三个驱动程序,如果我只执行一个插入,我会在表单中收到预期的错误:
ERROR;土生土长:515;州:23000; msg:[Microsoft] [ODBC SQL Server驱动程序] [SQLServer]无法将值NULL插入列' WalletID',table' Test.dbo.Walle t&#39 ;;列不允许空值。 INSERT失败。 错误;土生土长:3621;州:01000; msg:[Microsoft] [ODBC SQL Server驱动程序] [SQL Server]语句已终止。
如果我在一个语句中执行多个插入,并且第一个插入没有失败,则不会引发错误,如Ian Boyd的评论中所述。
解决方法:启用自动提交,但启用手动提交。执行多个插入仍然不会失败,但提交事务将引发错误。不幸的是,我无法检索任何错误信息,但至少在尝试提交时会得到SQL_ERROR
。
以下是更新的示例代码:
#include <windows.h>
#include <tchar.h>
#include <iostream>
#include <sql.h>
#include <sqlext.h>
#include <sqlucode.h>
#include <functional>
void printErr(SQLHANDLE handle, SQLSMALLINT handleType)
{
SQLSMALLINT recNr = 1;
SQLRETURN ret = SQL_SUCCESS;
while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
{
SQLWCHAR errMsg[SQL_MAX_MESSAGE_LENGTH + 1];
SQLWCHAR sqlState[5 + 1];
errMsg[0] = 0;
SQLINTEGER nativeError;
SQLSMALLINT cb = 0;
ret = SQLGetDiagRec(handleType, handle, recNr, sqlState, &nativeError, errMsg, SQL_MAX_MESSAGE_LENGTH + 1, &cb);
if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
{
std::wcerr << L"ERROR; native: " << nativeError << L"; state: " << sqlState << L"; msg: " << errMsg << std::endl;
}
++recNr;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
SQLHSTMT stmt = SQL_NULL_HSTMT;
SQLRETURN nResult = 0;
SQLHANDLE handleEnv = 0;
nResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, (SQLHANDLE*)&handleEnv);
nResult = SQLSetEnvAttr(handleEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3_80, SQL_IS_INTEGER);
SQLHANDLE handleDBC = 0;
nResult = SQLAllocHandle(SQL_HANDLE_DBC, handleEnv, (SQLHANDLE*)&handleDBC);
// SQLWCHAR strConnect[256] = L"Driver={SQL Server Native Client 11.0};Server=.\\TEST;Database=Test;Trusted_Connection=yes;";
// SQLWCHAR strConnect[256] = L"Driver={ODBC Driver 11 for SQL Server};Server=.\\TEST;Database=Test;Trusted_Connection=yes;";
SQLWCHAR strConnect[256] = L"Driver={SQL Server};Server=.\\TEST;Database=Test;Trusted_Connection=yes;";
SQLWCHAR strConnectOut[1024] = { 0 };
SQLSMALLINT nNumOut = 0;
nResult = SQLDriverConnect(handleDBC, NULL, (SQLWCHAR*)strConnect, SQL_NTS, (SQLWCHAR*)strConnectOut, sizeof(strConnectOut),
&nNumOut, SQL_DRIVER_NOPROMPT);
if (!SQL_SUCCEEDED(nResult))
{
printErr(handleDBC, SQL_HANDLE_DBC);
}
// Enable manual commit
nResult = SQLSetConnectAttr(handleDBC, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
if (!SQL_SUCCEEDED(nResult))
{
printErr(handleDBC, SQL_HANDLE_DBC);
}
SQLHSTMT handleStatement = 0;
nResult = SQLAllocHandle(SQL_HANDLE_STMT, handleDBC, (SQLHANDLE*)&handleStatement);
if (!SQL_SUCCEEDED(nResult))
{
printErr(handleDBC, SQL_HANDLE_DBC);
}
// try to drop, ignore if it exists
SQLExecDirect(handleStatement, L"DROP TABLE Wallet", SQL_NTS);
nResult = SQLExecDirect(handleStatement, L"CREATE TABLE Wallet (WalletID int NOT NULL, Name varchar(50) NOT NULL)", SQL_NTS);
if (!SQL_SUCCEEDED(nResult))
{
printErr(handleStatement, SQL_HANDLE_STMT);
}
//nResult = SQLExecDirect(handleStatement, L"INSERT INTO Wallet (WalletID, Name) VALUES (2, 'Fan') INSERT INTO Wallet (WalletID, Name) VALUES (1, 'Fan')", SQL_NTS);
nResult = SQLExecDirect(handleStatement, L"INSERT INTO Wallet (WalletID, Name) VALUES (2, 'Fan') INSERT INTO Wallet (Name) VALUES ('Fan')", SQL_NTS);
//nResult = SQLExecDirect(handleStatement, L"INSERT INTO Wallet (Name) VALUES ('Fan')", SQL_NTS);
//nResult = SQLExecDirect(handleStatement, L"INSERT INTO Wallet (WalletID, Name) VALUES (1, 'Fan')", SQL_NTS);
if (!SQL_SUCCEEDED(nResult))
{
printErr(handleStatement, SQL_HANDLE_STMT);
}
// Try to end transaction. Will fail, but no error information can be fetched(?)
nResult = SQLEndTran(SQL_HANDLE_DBC, handleDBC, SQL_COMMIT);
if (!SQL_SUCCEEDED(nResult))
{
printErr(handleDBC, SQL_HANDLE_DBC);
}
return 0;
}
答案 2 :(得分:0)
WalletID int列未设置为自动递增。