VC驿站

 找回密码
 加入驿站

QQ登录

只需一步,快速开始

搜索
查看: 255|回复: 1

[原创] 数据库之网络数据库

[复制链接]
75_avatar_middle
在线会员 发表于 2019-5-12 13:14:04 | 显示全部楼层 |阅读模式
    本章主要示范ODBC SQL访问MSSQL ,不过MSSQL是网络用数据库,如果要测试,用户ID和口令需要修改为测试数据库地。
本测试没有配置DSN,用驱动串代替,免得配置又要讲半天。
   ODBC不仅能访问网络数据库,也能访问本地数据库,还能访问其它非标准数据库。这里缺省是访问本地mdb,主要担心
测试者没装MSSQL数据库,又要扯一阵子。没有就干脆用本地测吧,道理都是一样地。
   连接串举了3例,1例是连接mssql, 用的驱动连接,如果熟悉,用dsn配置串也行,总之,都很简单。
                             2例是连接mdb,用数据库之本地数据库的.mdb。表名那是个中文表名。
                            3例是连接Excel文件,用它批量移植数据方便,规范。
   这ODBC已经够用,除此之外还有OleDB,略比SQL表现稍微复杂。操作跟本地表类似。
   本例不讲SQL语法,就跟打游戏一样,连接上,怎么打都自己摸索吧,不需要教。找语法书看一下,就能八窍通七窍,剩下
的那一窍不通就是我的。
例程如下:

#include "stdafx.h"
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
using namespace std;
class CODBC
{
        SQLHENV     hEnv = NULL;
        SQLHDBC     hDbc = NULL;
        SQLHSTMT    hStmt = NULL;
        struct DataBinding {
                SQLSMALLINT TargetType;
                SQLPOINTER TargetValuePtr;
                SQLINTEGER BufferLength;
                SQLLEN StrLen_or_Ind;
        };
public:
        HRESULT ConnectToDatabase(wstring connstr);
        void SqlCmd();
        void ShowStatementResult(SQLHSTMT hstmt);
        void DiagnosticRecord(SQLHANDLE  hHandle, SQLSMALLINT hType, RETCODE  RetCode);
        void DisConnectDatabase();
};
int main()
{
        wcout.imbue(locale("chs"));
        wcout << L"连接数据库..." << endl;
        wstring connstr;

        //访问mssql,实际测试中要根据本地的修改!!!
        //wstring dsn = L"{SQL Server};Server=127.0.0.1;Database=demodb;";
        //wstring user = L"demodbo";
        //wstring passwd = L"qqqqqq";
        //wstringstream o;
        //o << L"Driver=" << dsn << L";UID=" << user << L";PWD=" << passwd;
        //connstr = o.str();

        //访问本地mdb数据库
        connstr = L"Driver={Microsoft Access Driver (*.mdb)};DBQ=.\\Demo.mdb;";
        //查询语句:select * from 花名册

        //访问本地电子表格
        //connstr = L"Driver={Microsoft Excel Driver (*.xls)};DriverID=790;DBQ=.\\";
        //注意读Excel,连接串中HDR=NO/YES,默认有,它则从第二行开始读地,SQL驱动连接,该属性添加到连接串中无效,放在查询语句中。
        //查询语句:select * from [demoexcel.xls][sheet1$];
        //或查询语句:select * from  [excel 5.0;hdr=no;imex=1;database=demoexcel.xls].[sheet1$] ";

        CODBC odbc;
        HRESULT hr = odbc.ConnectToDatabase(connstr);
        if (FAILED(hr))
        {
                wcout << "无法连接到指定数据源,请检查odbc配置源!" << endl;
                system("pause");
                return -1;
        }
        odbc.SqlCmd();
        odbc.DisConnectDatabase();
        system("pause");
    return 0;
}

HRESULT CODBC::ConnectToDatabase(wstring connstr) {
        HRESULT hr = S_OK;
        //分配环境句柄
        SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
        if (ret == SQL_ERROR) {
                wcout << L"不能分配环境句柄!" << endl;
                return E_FAIL;
        }
        //设置ODBC版本3.0
        ret = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
        //分配DB句柄
        ret = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
        if (ret == SQL_ERROR) {
                wcout << L"不能分配数据句柄!" << endl;
                return E_FAIL;
        }
        //无提示窗口驱动连接
        ret = SQLDriverConnect(hDbc,
                GetDesktopWindow(),
                (SQLWCHAR*)connstr.c_str(),
                SQL_NTS,
                NULL,
                0,
                NULL,
                SQL_DRIVER_NOPROMPT);//SQL_DRIVER_COMPLETE
        if (ret == SQL_ERROR) {
                wcout << L"驱动连接失败!返回码=" << ret << endl;
                DiagnosticRecord(hDbc, SQL_HANDLE_DBC, ret);
                return E_FAIL;
        }
        return hr;
}

void CODBC::SqlCmd() {
#define SQL_QUERY_SIZE      1024
        HRESULT hr = S_OK;
        char  szsqlcmdbuf[SQL_QUERY_SIZE];
        TCHAR wszsqlcmdbuf[SQL_QUERY_SIZE];
        wcout << L"提示:" << endl;
        wcout << L"..按行输入SQL查询语句" << endl;
        wcout << L"..输入go执行SQL查询语句" << endl;
        wcout << L"..输入bye退出"<< endl;
        wcout << L"1>";
        wstring sqlstr;
        wstringstream inputstr;
        int lineno = 1;
        while (1)
        {
            //获取sql输入行
                //注意:不要用win.getline来输入,否则中文表输入是查不到多字节建的中文表名
                //     为避免混淆,都多字节输入,再转到宽字节。
                cin.getline(szsqlcmdbuf, SQL_QUERY_SIZE - 1);
                //不区分大小写比较
                if (!_stricmp(szsqlcmdbuf, "bye")) break;
                if (!_stricmp(szsqlcmdbuf, "quit")) break;
                if (!_stricmp(szsqlcmdbuf, "go"))
                {
                        lineno = 0;
                        RETCODE     RetCode;
                        SQLSMALLINT sNumResults;
                        sqlstr = inputstr.str();
                        //wcout << L"sql="<<sqlstr<<endl;
                        inputstr.clear();
                        inputstr.str(L"");
                        RetCode = SQLAllocStmt(hDbc, &hStmt);
                        RetCode = SQLExecDirect(hStmt,(SQLWCHAR*)sqlstr.c_str(), SQL_NTS);
                        switch (RetCode)
                        {
                        case SQL_SUCCESS_WITH_INFO:
                        case SQL_SUCCESS:
                                wcout << L"返回信息列字段信息\n=====================" << endl;
                                SQLNumResultCols(hStmt, &sNumResults);
                                if (sNumResults > 0)        ShowStatementResult(hStmt);
                                else                                         DiagnosticRecord(hStmt, SQL_HANDLE_STMT, RetCode);
                                break;
                        case SQL_ERROR:
                                wcout << L"返回 SQL_ERROR" << endl;
                                DiagnosticRecord(hStmt, SQL_HANDLE_STMT, RetCode);
                                break;
                        default:wcout << L"未知的返回码= " << RetCode << endl;
                        }
                        SQLFreeStmt(hStmt, SQL_CLOSE);
                }
                else
                {
                        MultiByteToWideChar(CP_ACP, 0, szsqlcmdbuf, -1, wszsqlcmdbuf, SQL_QUERY_SIZE);
                        inputstr << wszsqlcmdbuf<<L" ";
                        wszsqlcmdbuf[0] = L'\0';
                }
                lineno++;
                wcout << lineno<<L">";
        }
        wcout << L"欢迎您再回来!" << endl;
        DisConnectDatabase();
}
void CODBC::DisConnectDatabase() {
        if (hStmt)
        {
                SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
        }
        if (hDbc)
        {
                SQLDisconnect(hDbc);
                SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
        }
        if (hEnv)
        {
                SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
        }
}
void CODBC::DiagnosticRecord(SQLHANDLE  hHandle,SQLSMALLINT hType,RETCODE RetCode)
{
        SQLSMALLINT iRec = 0;
        SQLINTEGER  iError=0;
        WCHAR       wszMessage[1000];
        WCHAR       wszState[SQL_SQLSTATE_SIZE + 1];
        wcout.imbue(locale("chs"));
        if (RetCode == SQL_INVALID_HANDLE)
        {
                wcout << L"不合法的句柄!\n";
                return;
        }
        while (SQLGetDiagRec(hType,
                hHandle,
                ++iRec,
                wszState,
                &iError,
                wszMessage,
                (SQLSMALLINT)(sizeof(wszMessage) / sizeof(WCHAR)),
                (SQLSMALLINT *)NULL) == SQL_SUCCESS)
        {
                wcout << L"状态:" << wszState << L"信息:" << wszMessage << L"错误:" << dec << iError << endl;
                wcout.clear();
                wcout.flush();
        }
}
void CODBC::ShowStatementResult(SQLHSTMT hstmt) {
        SQLRETURN retCode;
        SQLSMALLINT numColumn = 0, bufferLenUsed;
        wcout.imbue(locale("chs"));
        //注意:SQLRowCount函数只能显示INSERT,UPDATE,DELETE行数,不显示SELECT返回总数
        //SQLLEN cRowCount;
        //SQLRowCount(hstmt, &cRowCount);
        //wcout << L"修改,插入,删除行总数:" << cRowCount << endl;
        retCode = SQLNumResultCols(hstmt, &numColumn);
        wcout << L"记录字段总数:" << numColumn << endl;
        if (numColumn < 1) return;
       //分配记录行绑定数据内存
        struct DataBinding* columnData = (struct DataBinding*)malloc(numColumn * sizeof(struct DataBinding));
        int bufferSize = 1024;
        BYTE ColumnLabel[1024];
        BYTE ColumnTypeName[64];
        SQLSMALLINT nColumnLabelLen = 1024;
        SQLSMALLINT nColumnTypeNameLen = 64;
        SQLINTEGER  nSqlType = 0;
        SQLINTEGER  nSqlLength = 0;
        wcout << L"记录字段信息:" << endl;
        for (int i= 0; i < numColumn; i++) {
                //读取列名,类型名,类型
                retCode = SQLColAttribute(hstmt, (SQLUSMALLINT)i + 1, SQL_DESC_LABEL, ColumnLabel, (SQLSMALLINT)nColumnLabelLen, &bufferLenUsed, NULL);
                retCode = SQLColAttribute(hstmt, (SQLUSMALLINT)i + 1, SQL_DESC_TYPE_NAME, ColumnTypeName, (SQLSMALLINT)nColumnTypeNameLen, &bufferLenUsed, NULL);
                retCode = SQLColAttribute(hstmt, (SQLUSMALLINT)i + 1, SQL_DESC_TYPE, NULL, 0, NULL, (SQLPOINTER)&nSqlType);
                retCode = SQLColAttribute(hstmt, (SQLUSMALLINT)i + 1, SQL_DESC_LENGTH, NULL, 0, NULL, (SQLPOINTER)&nSqlLength);
                wcout << L"列(字段)名:" << (LPCWSTR)ColumnLabel << endl;
                wcout << L"列类型名称:" << (LPCWSTR)ColumnTypeName << endl;
                wcout << L"列类型含义:";
                switch (nSqlType) {
                case SQL_CHAR:            wcout << L"SQL_C_CHAR";break;
                case SQL_VARCHAR:         wcout << L"SQL_C_VARCHAR";break;
                case SQL_LONGVARCHAR:     wcout << L"SQL_C_LONGVARCHAR";break;
                case SQL_WCHAR:           wcout << L"SQL_C_WCHAR";break;
                case SQL_WVARCHAR:        wcout << L"SQL_C_WVARCHAR";break;
                case SQL_WLONGVARCHAR:    wcout << L"SQL_C_WLONGVARCHAR";break;
                case SQL_INTEGER:         wcout << L"SQL_C_LONG";break;
                case SQL_SMALLINT:        wcout << L"SQL_C_SHORT";break;
                case SQL_REAL:            wcout << L"SQL_C_FLOAT";break;
                case SQL_DOUBLE:          wcout << L"SQL_C_DOUBLE";break;
                case SQL_NUMERIC:         wcout << L"SQL_C_NUMERIC";break;
                case SQL_DATE:            wcout << L"SQL_C_DATE";break;
                case SQL_TIME:            wcout << L"SQL_C_TIME";break;
                case SQL_BINARY:          wcout << L"SQL_C_BINARY";break;
                case SQL_BIT:             wcout << L"SQL_C_BIT";break;
                case SQL_TINYINT:         wcout << L"SQL_C_TINYINT";break;
                case SQL_TIMESTAMP:       wcout << L"SQL_C_TIMESTAMP";break;
                case SQL_TYPE_DATE:       wcout << L"SQL_C_TYPE_DATE";break;
                case SQL_TYPE_TIME:       wcout << L"SQL_C_TYPE_TIME";break;
                case SQL_TYPE_TIMESTAMP:  wcout << L"SQL_C_TYPE_TIMESTAMP";break;
                default:
                        wcout << L"未定义=" << nSqlType;break;
                }
                cout << endl;
                wcout << L"字段最大长度:" << nSqlLength << endl;
                cout << endl;
        }
        //字段绑定 - 分配内存绑定  ,其实,它都能自动转换为SQL_C_CHAR,这里只是把它们列举出来
        for (int i = 0; i < numColumn; i++) {
                switch (nSqlType) {
                case SQL_CHAR:
                case SQL_VARCHAR:
                case SQL_LONGVARCHAR:
                case SQL_WCHAR:
                case SQL_WVARCHAR:
                case SQL_WLONGVARCHAR:
                        columnData[ i ].TargetType = SQL_C_CHAR;
                        columnData[ i ].BufferLength = (nSqlLength + 1);
                        columnData[ i ].TargetValuePtr = malloc(sizeof(char)*columnData[ i ].BufferLength);
                        break;
                case SQL_INTEGER:
                case SQL_SMALLINT:
                case SQL_REAL:
                case SQL_DOUBLE:
                case SQL_NUMERIC:
                case SQL_DATE:
                case SQL_TIME:
                case SQL_BINARY:
                case SQL_BIT:
                case SQL_TINYINT:
                case SQL_TIMESTAMP:
                case SQL_TYPE_DATE:
                case SQL_TYPE_TIME:
                case SQL_TYPE_TIMESTAMP:
                default:
                        columnData[ i ].TargetType = SQL_C_CHAR;
                        columnData[ i ].BufferLength = (bufferSize + 1);
                        columnData[ i ].TargetValuePtr = malloc(sizeof(unsigned char)*columnData[ i ].BufferLength);
                }
                // 绑定列   
                retCode = SQLBindCol(hstmt,
                        (SQLUSMALLINT)i + 1,
                        columnData[ i ].TargetType,
                        columnData[ i ].TargetValuePtr,
                        columnData[ i ].BufferLength,
                        &(columnData[ i ].StrLen_or_Ind)
                );
        }
        // 显示行
        int nDisplayCount = 10;
        int nLineCount=0;
        for (retCode = SQLFetch(hstmt); retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO; retCode = SQLFetch(hstmt)) {
                for (int j = 0; j < numColumn; j++) {
                        SQLPOINTER pp = columnData[j].TargetValuePtr;
                        int l = columnData[j].BufferLength;
                        int le = columnData[j].StrLen_or_Ind;
                        cout << (LPCSTR)columnData[j].TargetValuePtr << "\t";
                }
                cout << endl;
                if ((nLineCount%nDisplayCount)== (nDisplayCount-1)) {
                        wchar_t ch;
                        while (1)
                        {
                                wcout << L"已显示" << (nLineCount+1) << L"行,还有数据,继续显示吗(Y/N) ";
                                wcin.get(ch);
                                cout << endl;
                                if (towupper(ch) == L'Y'||towupper(ch) == L'N') break;
                        }
                        if (towupper(ch) == L'N') break;
                }
                nLineCount++;
        }
        wcout << endl;
        //释放内存
        for (int i = 0; i < numColumn; i++) {
                free(columnData[ i ].TargetValuePtr);
        }
        free(columnData);
}

测试用的两个数据库表mdb,excel 数据库之网络数据库 demodb.rar (16.06 KB, 下载次数: 0)

评分

参与人数 1驿站币 +2 热心值 +2 收起 理由
58_avatar_small 敏敏 + 2 + 2 很给力!

查看全部评分





上一篇:数据库之本地数据库
下一篇:找不到dshow.h头文件
81_avatar_middle
online_moderator 发表于 2019-5-13 12:37:08 | 显示全部楼层
拜读下,xx_player 最近的文章都很不错哦
您需要登录后才可以回帖 登录 | 加入驿站 qq_login

本版积分规则

关闭

站长提醒上一条 /2 下一条

QQ|小黑屋|手机版|VC驿站 ( 辽ICP备09019393号tongdun|网站地图wx_jqr

GMT+8, 2019-7-19 04:11

Powered by Discuz! X3.4

© 2009-2019 cctry.com

快速回复 返回顶部 返回列表