inlandchris1 Ответов: 1

Как получить возврат из хранимой процедуры, используя C++, которая не имеет параметров, только вывод


Вот основной код, взятый из Microsoft docs. У меня были самые большие проблемы с подключением, но я обнаружил, что, как вы можете видеть, длина DSN, имя пользователя и пароль должны быть точными. Позже я помещу размер в переменную, чтобы очистить его. Я намеренно изменил пароль, чтобы убедиться, что логин/пароль работает через графический интерфейс ODBC (odbcad32.exe) у которого есть опция трассировки. I подключается к SQL и, согласно SQLExecDirect (), его подключение к хранимой процедуре.
Но результаты/sParam1 равны нулю. Что бы я ни менял, я не могу получить счет.
После того, как приведенный ниже код, СП будет после него. SP всегда работал, когда я использовал Crecordset, см. ниже.

retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
	retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER;
	if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
	{
		retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
		if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
		{
			retcode = SQLConnect(hdbc, (UCHAR *)"WISKY4", 6, (UCHAR *)"Admin", 5,
							(UCHAR *)"SEASEME", 7); 
				if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
				{
					retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
					if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
					{
						retcode = SQLBindParameter(hstmt, 1, SQL_PARAM_OUTPUT, 
						SQL_C_SSHORT, SQL_INTEGER, 0, 0, &sParam1, 0, &cbParam1);  
						if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
						{
							retcode = SQLExecDirect(hstmt, 
							(UCHAR *)"{?=CALL [dbo].[count_alarms]}", SQL_NTS);
							while( ( retcode = SQLMoreResults(hstmt) ) != 
												SQL_NO_DATA)
								;
								s1.Format("Return Results = %d", sParam1);
								s2.Format("cbReturn Results = %d", cbParam1);
							
						}
					}
				}
			}
		}
	}
}

После этого я освобождаю любую выделенную сверху SQL-память, просто не показывая ее, слишком много для чтения.
Хранимые процедуры ниже:
USE [MyDB]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[count_transactions]

AS
SELECT COUNT(*) FROM [dbo].[Transaction]
RETURN


Я изменил ниже, чтобы убедиться, что он будет выводиться, но на самом деле оба этих SP работают с использованием “exec count_alarms”.
Модифицированный ниже теперь производит заголовок/заголовок и подсчеты, в то время как выше просто выводит количество.
USE [MyDB]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[count_alarms]

 AS
DECLARE @tableOneCount int 
SELECT @tableOneCount = (SELECT COUNT(*) FROM [dbo].[ALARM_LOG] AS OUTPUT)

SELECT @tableOneCount AS 'TOTAL ALARM RECORDS'
RETURN


Ниже: Это модифицированная версия MFC/Crecordset, которая хорошо мне служит. Однако CRcordset занимает слишком много накладных расходов и не может быть перезапущен. В случае выхода из строя SQL-сервера или выхода из строя сетевого соединения crecordset не может восстановиться, и программа должна быть перезапущена; это невозможно, если это удаленный компьютер. CDatabase может восстановиться, но я не могу получить доступ к SP через CDatabase и получить возврат.
Ниже приведено то, что у меня есть, но я сотру его, если смогу получить команды CDatabase или SQL ODBC для работы с этими SPs.

IMPLEMENT_DYNAMIC(CCOUNT_ALARMS, CRecordset)

CCOUNT_ALARMS::CCOUNT_ALARMS(CDatabase* pdb)
	: CRecordset(pdb)
{
	m_COUNT.Empty();
	m_nFields	= 1;
	m_strParam	= 0;
	m_nDefaultType = ConnectType;
    m_nParams	= 1;
}
CString CCOUNT_ALARMS::GetDefaultConnect()
{
	CString Dsn;
	if (CONNECT_SQL_DB == TRUE && m_SQL_DB_ONLINE == TRUE)
	  Dsn.Format("%s%s", 
(char*)m_dbParam.DbRptString.GetString(),										(char *)m_dbParam.DbLogonStr.GetString( ) );
	if (CONNECT_TYPE_MS_DB == TRUE && CONNECT_SQL_DB == FALSE)
		Dsn.Format("%s%s%s",  (char *)m_dbParam.DbRptString.GetString(),
		 (char *)m_dbParam.DbParamStr.GetString(),  (char *)m_dbParam.DbLogonStr.GetString());
	return(Dsn);
}

CString CCOUNT_ALARMS::GetDefaultSQL()
{
	// argument ? == either  MS ACCESS OR SQL
	if (CONNECT_SQL_DB == TRUE && m_SQL_DB_ONLINE == TRUE)
		return _T( "{CALL [dbo].[count_alarms]}" );
	if (CONNECT_TYPE_MS_DB == TRUE && CONNECT_SQL_DB == FALSE)
		return _T("{CALL [count_alarms]}");
return _T("{CALL [count_alarms]}");// HERE ONLY TO QUIET THE COMPILIER
}

void CCOUNT_ALARMS::DoFieldExchange(CFieldExchange* pFX)
{
	pFX->SetFieldType(CFieldExchange::outputColumn);
	RFX_Text(pFX, _T("[COUNT]"),	m_COUNT);
	pFX->SetFieldType(CFieldExchange::param);
	RFX_Int(pFX, _T("COUNT"), m_strParam);
}

А КОД ДЛЯ РАБОТЫ ВЫШЕОПИСАННОГО ПРОГОНЯЕТСЯ ЧЕРЕЗ ПОТОК КАЖДЫЕ 5 МИНУТ
void CLeveL10View::CountAlarmsDB(void)
{
	int c = 0;

	if (m_FAIL_SAFE_ONLINE == FALSE && m_SQL_DB_ONLINE == FALSE
                && CONNECT_TYPE_SQL_DB == TRUE)
		return;
	if (m_FAIL_SAFE_ONLINE == TRUE && m_SQL_DB_ONLINE == TRUE &&
                           CONNECT_TYPE_SQL_DB == TRUE)
	{
		if (m_FailedDBAtempts > MAX_OFFLINE_FAILURES)
			return;
	}
	if (ALM_COUNTS_IN_PROGRESS == TRUE)
	{
		do
		{
			MyWait((DWORD)500, TRUE);
			if (c++ > 5)
				break;
		} while (ALM_COUNTS_IN_PROGRESS == TRUE);
	}

	ALM_COUNTS_IN_PROGRESS = TRUE;
	if ((m_SQL_DB_ONLINE == TRUE && CONNECT_TYPE_SQL_DB == TRUE) ||   
                                                     CONNECT_TYPE_MS_DB == TRUE)
		if (m_CountAlarms->IsOpen())
			m_CountAlarms->Close();
try
{
	m_CountAlarms->Open(NULL, NULL, CRecordset::executeDirect);
}
catch (CDBException * e)//IF NO STORED PROCEDURE OR NO PROCEDURE TO USE, ERROR BELOW BUT KEEPS ON GOIN
{
	COUNTING_ERROR = TRUE;
	e->Delete();
	TransactionCount = 0l;
	MYYield();
	COUNTS_IN_PROGRESS = FALSE;
	COUNT_TRANS_OK = TRUE;
	return;
}
if (m_CountAlarms->IsBOF() && m_CountAlarms->IsEOF())
{
	COUNTING_ERROR = TRUE;
	AlarmCount = 0l;
	AlarmIncrement = 0l;
	COUNTS_IN_PROGRESS = FALSE;
	COUNT_ALM_TRANS_OK = TRUE;
	m_CountAlarms->Close();
	return;
}
AlarmCount = atol(m_CountAlarms->m_COUNT);
AlarmIncrement = atol(m_CountAlarms->m_COUNT);
AlarmFailSafeIncrement = AlarmIncrement;
COUNT_ALM_TRANS_OK = TRUE;//TO FLAG OnTimer() to get the stats and put it on the screen
m_CountAlarms->Close();
ALM_COUNTS_IN_PROGRESS = FALSE;
return;
}



У кого - нибудь есть конструктивные идеи по программе ODBC или CDatabase ideas?

Что я уже пробовал:

Я пробовал объекты CDatabase для подключения к SQL DB и запуска SP безрезультатно. Затем я запустил код ODBC для запуска SP, но не смог получить никаких результатов. Я изменил такие функции, как SQLConnect на SQLDriverConnect, но безуспешно. Я потратил на это 2 дня и готов сдаться и позволить программе перезагрузиться, если SQL выйдет из строя; поместив EXE-файл в пакетный файл в каталоге автозагрузки В Windows 10. Я действительно нашел 7000-страничный документ на SQL и ODBC, и я все еще просматриваю его.
Извините за копию и вставку, но все вышло не так, как я хотел.

Herman<T>.Instance

Пожалуйста, используйте codeblocks при сбросе вашей среды кодирования здесь.

Gerry Schmitz

Я изучал "программирование баз данных" на языке С++ много лет назад ... По сравнению с языком с поддержкой 4GL / LINQ это похоже на кодирование на ассемблере. Используйте "управляемую" библиотеку фреймворков / классов, например ADO.NET-да.

inlandchris1

Gerry, thanks for the reply and accept all views. However, I only told a tiny bit of this story. I have been using C and C++ for 30 years because its the fastest high level language around next to assembler which is a low level language. My tiny code I put up is part of a 400,000 line count code which is used in a security environment. The hardware limitation is 127 card readers but I started this code from scratch that now has no limit on card readers and up to 4 billion cards. All this is possible because of C++. I need speed and C++ is only limited by the cpu clock speed. I have always been impressed with this language except for MFC. I needed a good looking GUI so 25 years ago, I chose MFC but the CRecordset sucks. This part of the code doesn’t not need CRecordsets because I am only writing to the transaction log and alarm logs. Doing the stored procedure is not writing, hence my trouble. Still pouring over the 7,000 page document but hoping someone has already cracked this problem. Again, thanks for the info.

1 Ответов

Рейтинг:
2

inlandchris1

Я наконец нашел его читая документы:
После того как вы сделаете выборку, теперь получите данные из SQLGetData. Работает с выходным параметром или параметром input_output, но SP имеет только выход. AlarmCount имеет данные (длинные).

retcode = SQLPrepare(hstmt, (UCHAR *)"{?=CALL count_alarms}", SQL_NTS);
if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
    retcode = SQLExecute(hstmt);
    if(SQLFetch(hstmt) == SQL_SUCCESS)
    {
        SQLGetData(hstmt, 1, SQL_C_ULONG, &AlarmCount, 0, NULL);
    }

}