在VC中用OLE DB读写SQL Server中的BLOB字段

Sqlserver timestamp数据类使用介绍 在SQL Server中联机丛书是这样说的: SQL Server timestamp 数据类型与时间和日期无关。SQL Server timestamp 是二进制数字,它表明数据库中数据修改发生的相对顺序。实现 timestamp 数据类型最初是为了支持 SQL Server 恢复算法。每次修改页时,都会使用当前的 @@DBTS 值对其做一次标记,然后 @@DBTS 加1。这样做足以帮助恢复过程确定页修改的相对次序,但是 timestamp 值与时间没有任何关系。 我用口水话解释一下便于理解: 我们知道数字类型有个“标识”属性,设置好“标识种子”、“标识递增量”,每增加一条记录时,这个字段的 立即下载

在需要对数据库进行操作时,OLE DB总是被认为是一种效率最高但最难的方法。但是以我最近使用OLE DB的经验看来,OLE DB的效率高则高矣,但却一点都不难。说它难恐怕主要是因为可参考的中文资料太少,为了帮助以后需要接触OLE DB的同行,我撰写了这篇文章。本文包含如下内容:

1. OLE DB写数据库;
2. OLE DB读数据库;
3. OLE DB对二进制数据(text、ntext、image等)的处理。

首先来看看对SQL Server进行写操作的代码,有一定VC基础的读者应该可以很顺利地看懂。OLE DB写数据库,就是这么简单!

注:
1.以下代码中使用的模板类EAutoReleasePtr<T>与ATL中的CComPtr<T>类似,是一个在析构时自动调用Release的类。CComPtr<T>的代码在ATLBASE.H中定义。
2.以下代码均在UNICODE环境下编译,因为执行的SQL语句必须是UNICODE的。设置工程为UNICODE的方法是:首先在project->settings->C/C++的属性页中的Preprocessor中,删除_MBCS写入UNICODE,_UNICODE。然后在link属性页中Category中选择output,在Entry-Point symbol 中添加wWinMainCRTStartup。

EAutoReleasePtr<IDBInitialize> pIDBInitialize;
HRESULT hResult = ConnectDatabase( &pIDBInitialize, _T("127.0.0.1"), _T(“sa”), _T("password") );
if( FAILED( hResult ) )
{
    //失败,可能是因为数据库没有启动、用户名密码错等等
    return;
}

EAutoReleasePtr<IOpenRowset> pIOpenRowset;
hResult = CreateSession( pIDBInitialize, &pIOpenRowset );
if( FAILED( hResult ) )
{
    //出错
    return;
}

EAutoReleasePtr<ICommand> pICommand;
EAutoReleasePtr<ICommandText> pICommandText;
hResult = CreateCommand( pIOpenRowset, &pICommand, &pICommandText );
if( FAILED( hResult ) )
{
   //出错
    return;
}

hResult = ExecuteSQL( pICommand, pICommandText, _T("USE PBDATA") );
if( FAILED( hResult ) )
{
    //如果这里失败,那就是SQL语句执行失败。在此处,就是PBDATA还未创建
    return;
}

// 创建表
ExecuteSQL( pICommand, pICommandText, _T("CREATE TABLE 2005_1(Volume real NOT NULL,ID int NOT NULL IDENTITY)") );

// 添加记录
ExecuteSQL( pICommand, pICommandText, _T("INSERT INTO 2005_1 VALUES(100.0)") );

//...
 
其中几个函数的代码如下:

HRESULT ConnectDatabase( IDBInitialize** ppIDBInitialize, LPCTSTR pszDataSource, LPCTSTR pszUserID, LPCTSTR pszPassword )
{
    ASSERT( ppIDBInitialize != NULL && pszDataSource != NULL && pszUserID != NULL && pszPassword != NULL );

    UINT uTimeout = 15U; // 连接数据库超时(秒)
    TCHAR szInitStr[1024];
    VERIFY( 1023 >= wsprintf( szInitStr, _T("Provider=SQLOLEDB;Data Source=%s;Initial Catalog=master;User Id=%s;Password=%s;Connect Timeout=%u"), pszDataSource, pszUserID, pszPassword, uTimeout ) );
    //Initial Catalog=master指明连接成功后,"USE master"。

    EAutoReleasePtr<IDataInitialize> pIDataInitialize;
    HRESULT hResult = ::CoCreateInstance( CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,
            IID_IDataInitialize, ( void** )&pIDataInitialize );
    if( FAILED( hResult ) )
    {
        return hResult;
    }

    EAutoReleasePtr<IDBInitialize> pIDBInitialize;
    hResult = pIDataInitialize->GetDataSource( NULL, CLSCTX_INPROC_SERVER, ( LPCOLESTR )szInitStr,
              IID_IDBInitialize, ( IUnknown** )&pIDBInitialize );
    if( FAILED( hResult ) )
    {
        return hResult;
    }
    hResult = pIDBInitialize->Initialize( );
    if( FAILED( hResult ) )
    {
        return hResult;
    }
    * ppIDBInitialize = pIDBInitialize.Detach( );
    return S_OK;
}

HRESULT CreateSession( IDBInitialize* pIDBInitialize, IOpenRowset** ppIOpenRowset )
{
    ASSERT( pIDBInitialize != NULL && ppIOpenRowset != NULL );
    EAutoReleasePtr<IDBCreateSession> pSession;
    HRESULT hResult = pIDBInitialize->QueryInterface( IID_IDBCreateSession, ( void** )&pSession );
    if( FAILED( hResult ) )
    {
        return hResult;
    }
    EAutoReleasePtr<IOpenRowset> pIOpenRowset;
    hResult = pSession->CreateSession( NULL, IID_IOpenRowset, ( IUnknown** )&pIOpenRowset );
    if( FAILED( hResult ) )
    {
        return hResult;
    }
    * ppIOpenRowset = pIOpenRowset.Detach( );
    return S_OK;
}

HRESULT CreateCommand( IOpenRowset* pIOpenRowset, ICommand** ppICommand, ICommandText** ppICommandText )
{
    ASSERT( pIOpenRowset != NULL && ppICommand != NULL && ppICommandText != NULL );
    HRESULT hResult;
    EAutoReleasePtr<ICommand> pICommand;
    {
        EAutoReleasePtr<IDBCreateCommand> pICreateCommand;
        hResult = pIOpenRowset->QueryInterface( IID_IDBCreateCommand, ( void** )&pICreateCommand );
        if( FAILED( hResult ) )
        {
            return hResult;
        }
 
        hResult = pICreateCommand->CreateCommand( NULL, IID_ICommand, (IUnknown**)&pICommand );
        if( FAILED( hResult ) )
        {
            return hResult;
        }
    }
    EAutoReleasePtr<ICommandText> pICommandText;
    hResult = pICommand->QueryInterface( &pICommandText );
    if( FAILED( hResult ) )
    {
        return hResult;
    }
    * ppICommand = pICommand.Detach( );
    * ppICommandText = pICommandText.Detach( );
    return S_OK;
}

HRESULT ExecuteSQL( ICommand* pICommand, ICommandText* pICommandText, LPCTSTR pszCommand, LONG* plRowsAffected )
{
    ASSERT( pICommand != NULL && pICommandText != NULL && pszCommand != NULL && pszCommand[0] != 0 );

    HRESULT hResult = pICommandText->SetCommandText( DBGUID_DBSQL, ( LPCOLESTR )pszCommand );
    if( FAILED( hResult ) )
    {
        return hResult;
    }
    LONG lAffected;
    hResult = pICommand->Execute( NULL, IID_NULL, NULL, plRowsAffected == NULL ? &lAffected : plRowsAffected, ( IUnknown** )NULL );
    return hResult;
}

以上就是写数据库的全部代码了,是不是很简单呢?下面再来读的。

// 先用与上面代码中一样的步骤获取pICommand,pICommandText。此处省略

HRESULT hResult = pICommandText->SetCommandText( DBGUID_DBSQL, ( LPCOLESTR )_T("SELECT Volume FROM 2005_1 WHERE ID = @@IDENTITY") ); //取我们刚刚添加的那一条记录
if( FAILED( hResult ) )
{
    return;
}

LONG lAffected;
EAutoReleasePtr<IRowset> pIRowset;
hResult = pICommand->Execute( NULL, IID_IRowset, NULL, &lAffected, ( IUnknown** )&pIRowset );

if( FAILED( hResult ) )
{
    return;
}

EAutoReleasePtr<IAccessor> pIAccessor;
hResult = pIRowset->QueryInterface( IID_IAccessor, ( void** )&pIAccessor );

if( FAILED( hResult ) )
{
    return;
}

// 一个根据表中各字段的数值类型而定义的结构,用于存储返回的各字段的值
struct CLoadLastFromDB
{
     DBSTATUS dwdsVolume;
     DWORD     dwLenVolume;
     float          fVolume;
};

// 此处我们只查询了一个字段。如果要查询多个字段,CLoadLastFromDB中要添加相应的字段定义,下面的dbBinding也要相应扩充。dbBinding[].iOrdinal要分别指向各个字段,dbBinding[].wType要根据字段类型赋合适的值。

DBBINDING dbBinding[1];
dbBinding[0].iOrdinal            = 1;   // Volume 字段的位置,从 1 开始
dbBinding[0].obValue           = offsetof( CLoadLastFromDB, fVolume );
dbBinding[0].obLength         = offsetof( CLoadLastFromDB, dwLenVolume );
dbBinding[0].obStatus         = offsetof( CLoadLastFromDB, dwdsVolume );
dbBinding[0].pTypeInfo       = NULL;
dbBinding[0].pObject           = NULL;
dbBinding[0].pBindExt         = NULL;
dbBinding[0].dwPart            = DBPART_VALUE | DBPART_STATUS | DBPART_LENGTH;
dbBinding[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
dbBinding[0].eParamIO       = DBPARAMIO_NOTPARAM;
dbBinding[0].cbMaxLen       = 0;
dbBinding[0].dwFlags          = 0;
dbBinding[0].wType            = DBTYPE_R4; // float就是DBTYPE_R4,int就是DBTYPE_I4。参见MSDN
dbBinding[0].bPrecision       = 0;
dbBinding[0].bScale             = 0;

HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBBINDSTATUS dbs[1];
hResult = pIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, 1, dbBinding, sizeof( CLoadLastDataFromDB ), &hAccessor, dbs );

if( FAILED( hResult ) )
{
    return;
}

ASSERT( dbs[0] == DBBINDSTATUS_OK );
ULONG uRowsObtained = 0;
HROW  hRows[1];                          // 这里我们只查询了最新的那一条记录
HROW* phRows = hRows;
CLoadLastFromDB rmd;
hResult = pIRowset->GetNextRows( NULL, 0, 1, &uRowsObtained, &phRows );
if( SUCCEEDED( hResult ) && uRowsObtained != 0U )
{
    hResult = pIRowset->GetData( phRows[0], hAccessor, &rmd );
    if( FAILED( hResult ) )
    {
        ASSERT( FALSE );
    }
    ASSERT( rmd.dwdsVolume == DBSTATUS_S_OK );
    // rmd.fVolume 就是我们要取的值
}

pIRowset->ReleaseRows( uRowsObtained, phRows, NULL, NULL, NULL );
pIAccessor->ReleaseAccessor( hAccessor, NULL );
pIAccessor.Release( );
pIRowset.Release( );
 
读操作也完成了,是不是仍然很简单呢?下面我们再来看看最麻烦的二进制数据(text、ntext、image等)的读写。要实现BLOB数据的读写,我们需要一个辅助的类,定义如下:

class CSequentialStream : public ISequentialStream    // BLOB 数据访问类
{
public:
    CSequentialStream( );
    virtual ~CSequentialStream( );
    virtual BOOL Seek( ULONG uPosition );
    virtual BOOL Clear( );
    virtual ULONG GetLength( ) { return m_uBufferUsed; };
    virtual operator void* const( ) { return m_pBuffer; };
    STDMETHODIMP_( ULONG ) AddRef( ) { return ++ m_uRefCount; };
    STDMETHODIMP_( ULONG ) Release( ) { ASSERT( m_uRefCount != 0U ); -- m_uRefCount; if( m_uRefCount == 0U ) { delete this; } return m_uRefCount; };
    STDMETHODIMP QueryInterface( REFIID riid, LPVOID* ppv );
    STDMETHODIMP Read( void __RPC_FAR* pv, ULONG cb, ULONG __RPC_FAR* pcbRead );
    STDMETHODIMP Write( const void __RPC_FAR* pv, ULONG cb, ULONG __RPC_FAR* pcbWritten );
void    ResetPosition( ) { m_uPosition = 0U; };
HRESULT PreAllocBuffer( ULONG uSize );

private:
    ULONG m_uRefCount;     // reference count
    void* m_pBuffer;           // buffer
    ULONG m_uBufferUsed;  // buffer used
    ULONG m_uBufferSize;   // buffer size
    ULONG m_uPosition;       // current index position in the buffer
};

实现如下:

CSequentialStream::CSequentialStream( ) : m_uRefCount( 0U ), m_pBuffer( NULL ), m_uBufferUsed( 0U ), m_uBufferSize( 0U ), m_uPosition( 0U )
{
    AddRef( );
}

CSequentialStream::~CSequentialStream( )
{
    Clear( );
}

HRESULT CSequentialStream::QueryInterface( REFIID riid, void** ppv )
{
    if( riid == IID_IUnknown || riid == IID_ISequentialStream )
    {
        * ppv = this;
        ( ( IUnknown* )*ppv )->AddRef( );
        return S_OK;
    }
    * ppv = NULL;
    return E_NOINTERFACE;
}

BOOL CSequentialStream::Seek( ULONG uPosition )
{
    ASSERT( uPosition < m_uBufferUsed );
    m_uPosition = uPosition;
    return TRUE;
}

BOOL CSequentialStream::Clear( )
{
    m_uBufferUsed = 0U;
    m_uBufferSize = 0U;
    m_uPosition = 0U;
    ( m_pBuffer != NULL ? CoTaskMemFree( m_pBuffer ) : 0 );
    m_pBuffer = NULL;
    return TRUE;
}

HRESULT CSequentialStream::PreAllocBuffer( ULONG uSize )
{
    if( m_uBufferSize < uSize )
    {
        m_uBufferSize = uSize;
        m_pBuffer = CoTaskMemRealloc( m_pBuffer, m_uBufferSize );
        if( m_pBuffer == NULL )
        {
            Clear( );
            return STG_E_INSUFFICIENTMEMORY;
        }
    }
    return S_OK;
}

HRESULT CSequentialStream::Read( void* pv, ULONG cb, ULONG* pcbRead )
{
    ( pcbRead != NULL ? ( * pcbRead = 0U ) : 0 );
    if( pv == NULL ) { return STG_E_INVALIDPOINTER; }
    if( cb == 0U ) { return S_OK; }

    ASSERT( m_uPosition <= m_uBufferUsed );
    ULONG uBytesLeft = m_uBufferUsed - m_uPosition;

    if( uBytesLeft == 0U ) { return S_FALSE; } //no more bytes

    ULONG uBytesRead = ( cb > uBytesLeft ? uBytesLeft : cb );
    memcpy( pv, ( BYTE* )m_pBuffer + m_uPosition, uBytesRead );
    m_uPosition += uBytesRead;

    ( pcbRead != NULL ? ( * pcbRead = uBytesRead ) : 0 );
    return ( cb != uBytesRead ? S_FALSE : S_OK );
}

HRESULT CSequentialStream::Write( const void* pv, ULONG cb, ULONG* pcbWritten )
{
    if( pv == NULL ) { return STG_E_INVALIDPOINTER; }
    ( pcbWritten != NULL ? ( * pcbWritten = 0U ) : 0 );
    if( cb == 0U ){ return S_OK; }

    ASSERT( m_uPosition <= m_uBufferUsed );
    if( m_uBufferSize < m_uPosition + cb )
    {
        m_uBufferSize = m_uPosition + cb;
        m_pBuffer = CoTaskMemRealloc( m_pBuffer, m_uBufferSize );
        if( m_pBuffer == NULL )
        {
            Clear( );
            return STG_E_INSUFFICIENTMEMORY;
        }
    }
    m_uBufferUsed = m_uPosition + cb;
    memcpy( ( BYTE* )m_pBuffer + m_uPosition, pv, cb );
    m_uPosition += cb;
    ( pcbWritten != NULL ? ( * pcbWritten = cb ) : 0 );
    return S_OK;
}

下面我们开始往一个包含ntext字段的表中添加记录。假设这个表(News)的结构为:ID int NOT NULL IDENTITY、Title nchar(80)、 Contents ntext。

// 先将记录添加进去,ntext字段留空。我们稍后再更新ntext的内容。
HRESULT hResult = ExecuteSQL( pICommand, pICommandText, _T("INSERT INTO News VALUES(''TEST'','''')") );

DBPROP dbProp;
dbPropSet.guidPropertySet = DBPROPSET_ROWSET;
dbPropSet.cProperties        = 1;
dbPropSet.rgProperties       = &dbProp;

DBPROPSET dbPropSet;
dbPropSet.rgProperties[0].dwPropertyID     = DBPROP_UPDATABILITY;
dbPropSet.rgProperties[0].dwOptions         = DBPROPOPTIONS_REQUIRED;
dbPropSet.rgProperties[0].dwStatus           = DBPROPSTATUS_OK;
dbPropSet.rgProperties[0].colid                  = DB_NULLID;
dbPropSet.rgProperties[0].vValue.vt           = VT_I4;
V_I4( &dbPropSet.rgProperties[0].vValue ) = DBPROPVAL_UP_CHANGE;
   
EAutoReleasePtr<ICommandProperties> pICommandProperties;
hResult = pICommandText->QueryInterface( IID_ICommandProperties, ( void** )&pICommandProperties );

// 设置 Rowset 属性为“可以更新某字段的值”
hResult = pICommandProperties->SetProperties( 1, &dbPropSet );

hResult = pICommandText->SetCommandText( DBGUID_DBSQL, ( LPCOLESTR )L"SELECT Contents FROM News WHERE ID = @@IDENTITY" );

LONG lAffected;
EAutoReleasePtr<IRowsetChange> pIRowsetChange;
hResult = pICommand->Execute( NULL, IID_IRowsetChange, NULL, &lAffected, ( IUnknown** )&pIRowsetChange );

EAutoReleasePtr<IAccessor> pIAccessor;
hResult = pIRowsetChange->QueryInterface( IID_IAccessor, ( void** )&pIAccessor );

struct BLOBDATA
{
    DBSTATUS           dwStatus;
    DWORD              dwLength;
    ISequentialStream* pISeqStream;
};

// 有关DBOBJECT、DBBINDING的设置,建议参考MSDN,很容易懂。
DBOBJECT dbObj;
dbObj.dwFlags = STGM_READ;
dbObj.iid         = IID_ISequentialStream;

DBBINDING dbBinding;
dbBinding.iOrdinal   = 1;                              // BLOB 字段的位置,从 1 开始
dbBinding.obValue    = offsetof( BLOBDATA, pISeqStream );
dbBinding.obLength   = offsetof( BLOBDATA, dwLength );
dbBinding.obStatus   = offsetof( BLOBDATA, dwStatus );
dbBinding.pTypeInfo  = NULL;
dbBinding.pObject    = &dbObj;
dbBinding.pBindExt   = NULL;
dbBinding.dwPart     =  DBPART_VALUE | DBPART_STATUS | DBPART_LENGTH;
dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
dbBinding.eParamIO   = DBPARAMIO_NOTPARAM;
dbBinding.cbMaxLen   = 0;
dbBinding.dwFlags    = 0;
dbBinding.wType      = DBTYPE_IUNKNOWN;
dbBinding.bPrecision = 0;
dbBinding.bScale     = 0;

HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBBINDSTATUS dbs;
hResult = pIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, 1, &dbBinding, sizeof( BLOBDATA ), &hAccessor, &dbs );

EAutoReleasePtr<IRowset> pIRowset;
hResult = pIRowsetChange->QueryInterface( IID_IRowset, ( void** )&pIRowset );

ULONG uRowsObtained = 0;
HROW* phRows = NULL;
hResult = pIRowset->GetNextRows( NULL, 0, 1, &uRowsObtained, &phRows );

CSequentialStream* pss = new CSequentialStream;
pss->PreAllocBuffer( 1024 );                           // 预先分配好内存,并读入数据
pss->Write( pszSomebuffer, 512, NULL );        // pss->Write可以连续调用
pss->Write( pszSomebuffer+512, 512, NULL );
pss->ResetPosition( );

BLOBDATA bd;
bd.pISeqStream = ( ISequentialStream* )pss;
bd.dwStatus    = DBSTATUS_S_OK;
bd.dwLength    = pss->GetLength( );

// 将 BLOB 数据写入到数据库
hResult = pIRowsetChange->SetData( phRows[0], hAccessor, &bd );

pIAccessor->ReleaseAccessor( hAccessor, NULL );
pIRowset->ReleaseRows( uRowsObtained, phRows, NULL, NULL, NULL );

// pss was released by pIRowsetChange->SetData.

这样,我们就完成了一条记录的添加。读取BLOB字段的代码跟上面的完全类似,只要把
hResult = pIRowset->GetNextRows( NULL, 0, 1, &uRowsObtained, &phRows );
后面的那些改成下面的代码即可。

BLOBDATA bd;
hResult = pIRowset->GetData( phRows[0], hAccessor, &bd );
if( bd.dwStatus == DBSTATUS_S_ISNULL )
{
    // 此字段为空
}
else if( bd.dwStatus != DBSTATUS_S_OK || bd.pISeqStream == NULL )
{
    // 失败
}
else
{
    // 从系统分配的 ISequentialStream 接口读入 BLOB 数据
    BYTE szReadBuffer[1024];
    for( ULONG uRead = 0U; ; )
    {
        if( FAILED( bd.pISeqStream->Read( szReadBuffer, 1024, &uRead ) ) )
        {
            break;
        }
        //szReadBuffer中就包含了BLOB字段的数据
        if( uRead != 1024 )
        {
            break;
        }
    }
    bd.pISeqStream->Release( );
}
pIAccessor->ReleaseAccessor( hAccessor, NULL );
pIRowset->ReleaseRows( uRowsObtained, phRows, NULL, NULL, NULL );

至此,要讲的已全部讲完,希望对你能有所帮助。文中贴出的代码都是可以复制使用的,只是某些地方需要加入返回值判断、错误处理代码。

从零到一使用PXE实现系统的无人值守自动化安装的保姆级教程 本文是《从零到一使用PXE实现系统的无人值守自动化安装的保姆级教程》亲自实操可用,本文从PXE是什么?有啥用?及其亲自实践出通过以国产银河麒麟系统为例从零到一的搭建出PXE服务器端(适用于其他系统),且测试了该搭建的PXE服务端自动化无人值守安装系统的保姆级实操教程。适用于大批量设备系统安装部署场景。 阅读详情

相关推荐

从零构建安全通信:mbedtls与ECDSA在嵌入式系统中的实战指南

本文详细介绍了如何在嵌入式系统中使用mbedtls和ECDSA实现安全通信,重点探讨了ECDSA相比RSA的优势、mbedtls的模块化裁剪策略、密钥安全存储方案以及完整的签名验签流程。通过实战案例和优化技巧,帮助开发者在资源受限环境下构建高效可靠的安全通信系统。

weixin_28338005的博客 685

C++开发基础之基于SQLite数据库开发的基础知识

SQLite是一个自包含、无服务器的SQL数据库引擎。轻量级:SQLite库非常小巧,适合嵌入到应用程序中。零配置:SQLite不需要独立的服务器进程或配置文件。单文件存储:整个数据库存储在单个文件中,方便管理和迁移。ACID事务支持:支持原子性、一致性、隔离性和持久性的事务操作。本篇博客提供了C++与SQLite数据库交互的基础知识和操作示例。希望能够帮助您在C++开发中有效地使用SQLite数据库。

dotnet研习社 3303

Uncertainty-Aware Mean Teacher(UA-MT)

本文提出了一种新的基于不确定性的半监督学习框架(UA-MT),通过额外利用未标记的数据从3D MR图像中分割左心房。具体地说,本文建立了一个教师模型和一个学生模型,学生模型通过最小化标注数据上的分割监督损失和所有输入数据上的与教师模型预测输出的一致性损失进行优化。如果没有未标记输入中的注释,教师模型的预测目标可能不可靠且有噪声。:在估计不确定性 U 的指导下,我们过滤掉相对不可靠**(高不确定性),整个体积** U 的不确定性**是 {u} ∈ RH×W ×D。过滤掉高不确定性的,也就是熵大于某个值的。

Alexa_的博客 1870

Visual C++中使用OLE DB读写SQL Server简明指南

   在需要对数据库进行操作时,OLE DB总是被认为是一种效率最高但最难的方法。但是以我最近使用OLE DB的经验看来,OLE DB的效率高则高矣,但却一点都不难。说它难恐怕主要是因为可参考的中文资料太少,为了帮助以后需要接触OLE DB的同行,我撰写了这篇文章。本文包含如下内容:    1. OLE DB写数据库;    2. OLE DB读数据库;    3. OLE DB对二进制数据(text、ntext、image等)的处理。    首先来看看对SQL Server进行写操作的代码,有一定VC基础

CRDBreeze的专栏 919

【Godot4自学手册】第一节配置Godot运行环境

Godot是一款自由开源、由社区驱动的 2D 和 3D 游戏引擎!它是一款功能丰富的跨平台游戏引擎,可以通过统一的界面创建 2D 和 3D 游戏。它提供了一套全面的通用工具,因此用户可以专注于制作游戏,而无需重新发明轮子。游戏可以一键导出到多个平台,包括主流的桌面平台(Linux、macOS、Windows)、移动平台(Android、iOS)、基于 Web 的平台以及主机平台。

zhaoyang314的博客 5984

国民技术N32G45X低功耗问题之更改全局变量和栈在RAM中的地址

最近项目中用的MCU是国民技术的N32G45X,MCU的资源很丰富,RAM也很大,配套的库和例程也比较全,用起来还是不错的,但是在调试低功耗的时候还是费了不少劲。 首先由于项目低功耗需要,只能选择停机模式2(STOP2)。根据数据手册的描述,在此模式下外部低速时钟开启, RTC运行, R-SRAM保持,所有I/O状态保持,独立看门狗处于关闭状态,25℃的条件下电流为10个微安。 用的这款MCU的RAM是144KB,分为SRAM(128KB)和R-SRAM( Retention SRAM – 16KB),如果

狂想家 2486

基于Visual C++的OLE DB技术

OLE DB是Microsoft开发的一种高性能的、基于COM的数据库连接技术。OLE DB 和其他Microsoft数据库连接技术的不同之处在于其提供通用数据访问的能力。所谓的通用数据访问一般具备两种功能:其一是“分布式查询”或统一访问多(分布式)数据源的功能;其二是让非DBMS数据源能被数据库应用程序访问的功能。 分布式查询 即统一访问多(分布式)数据源数据的能力。数据源既可以为同一类型(例如两个单独的Access数据库),也可以为不同类型(例如一个SQL Server数据库和一个Access数据库),

richer_live的博客 563

海康威视工业相机网络配置实战:从MVS软件操作到IP地址规划

本文详细介绍了海康威视工业相机的网络配置实战,从MVS软件操作到IP地址规划。重点讲解了IP设置的重要性、Windows系统下的网络配置步骤、避免IP冲突的技巧,以及MVS软件中的相机IP配置方法。帮助用户快速掌握工业相机的网络连接技术,确保稳定高效的图像采集。

weixin_30296405的博客 486

VC++ 实战OLEDB编程(一)

 OLEDB作为目前最全面,最强大的Windows平台下的数据库编程接口,其资料在网上却少之又少,这着实有些让人纳罕。 现在很多的应用软件系统都要和数据库打交道,没有一个好的强大的数据库编程接口作为支撑,这些系统的功能,性能,安全性等等都将是不可想象的事情。当然我们还可以选择OLEDB之上的ADO接口来作为我们的编程接口,由于ADO是基于OLEDB的上层封装,ADO比之OLEDB最大的优势就在

1829

AI大模型探索之路-实战篇4:深入DB-GPT数据应用开发框架调研

在当今的人工智能时代,大模型技术的迅猛发展为各行各业带来了前所未有的变革。这些大模型,以其强大的语言理解和生成能力,正在逐步成为智能化应用的核心。然而,如何高效地利用这些大模型,构建出满足各种需求的应用,仍然是一个具有挑战性的问题。DB-GPT,作为一个开源的AI原生数据应用开发框架,应运而生,旨在简化大模型应用的开发过程,让构建智能化应用变得触手可及。本文将深入介绍DB-GPT的核心功能、关键特性,并通过实战操作,展示如何利用DB-GPT进行数据应用开发。DB-GPT是一个开源的AI原生数据应用开发框架。

寻道AI,探索AI无限可能! 1万+

基于AI大模型实现自然语言转SQL、SQL解释、SQL优化和SQL转换

随着大数据和人工智能技术的发展,数据分析和数据挖掘在各行各业中的应用越来越广泛。SQL(Structured Query Language)作为关系数据库的查询语言,是数据分析的重要工具。然而,SQL语言的复杂性和专业性使得许多非技术背景的用户难以直接使用,阻碍了数据的广泛利用。为了降低SQL的使用门槛,许多研究者和企业开始探索如何通过自然语言处理(NLP)技术,将自然语言转化为SQL查询语句,从而让用户可以通过自然语言与数据库进行交互。

AI天才研究院 2239

Sqlserver中的timestamp

项目中抽取sqlserver数据库数据,遇到了timestamp类型数据,网上查到比较好的,记录下 博客地址:SqlServer数据库中的时间戳(TimeStamp)类型_xiaomin的博客-优快云博客_sqlserver timestamp 插入1.公开数据库中自动生成的唯一二进制数字的数据类型。  2.timestamp 通常用作给表行加版本戳的机制。  3.存储大小为 8 个字节。 不可为空的 timestamp 列在语义上等价于 binary(8) 列。可为空的 timestamp 列在语义.

如若時光倒流 2610

超声波传感器(CH101&ch201) - Ⅰ

压电微机械超声换能器(PMUT)和超声波信号处理的可编程片上系统(SoC)集成在一起的传感器

nicole088的专栏 2796

在VC中用OLE DB读写SQL Server中的BLOB字段 (转)

在VC中用OLE DB读写SQL Server中的BLOB字段 (转)[@more@] 本人曾遇到一个非常棘手的问题,在VC中用OLE db读写sql SERVER中的BLOB字段时,尝试了几乎所有的绑定方法,读是没有问题,但...

csd3176的博客 175

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

VC在Sql Server数据库中如何存取二进制图片信息

转载地址:VC在Sql Server数据库中如何存取二进制图片信息  http://blog.hehehehehe.cn/a/1396.htm 在VC6.0中我们通常用Ado的Field 对象的GetChuck和AppendChunk来读写Blob对象,但是这样做要写很多的代码,其实ado给我们提供了一个更易操作的对象那就是 Stream Object,通过它我们可以更容易的操作数据库中的Blo

lijun5635的专栏 2221

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

VC++数据库编程核心技术:ODBC/OLE DB接口选型与实战调优

数据库编程是软件开发中的基础技术,其核心在于通过标准接口实现应用程序与数据库系统之间的高效、安全通信。在Windows平台下,VC++提供了ODBC和OLE DB两种主流数据访问接口,它们分别基于不同的设计原理:ODBC作为通用数据库访问标准,通过统一的API层屏蔽不同数据库的差异;而OLE DB则基于COM组件模型,提供更底层的灵活性和性能潜力。掌握这些接口的技术价值在于,开发者能够根据应用场景(如金融交易系统、工业控制软件等对性能、稳定性要求极高的领域)选择最合适的方案,并实现连接池管理、事务控制、二进

weixin_34277853的博客 380

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

VC中使用ADO实现BLOB数据的存取

在VC中使用ADO实现BLOB数据的存取     -|walrus 发表于 2006-6-22 9:30:00

wjw806[旺旺]的专栏 1137

SQL Server存储图像数据的策略与方法

目前对于图像数据的管理大都采用表+实体的方法,即图像数据以文件形式存放于指定的计算机目录下,在数据库表中只反映图像数据文件的存储路径。这种管理模式,给数据的维护增加了难度,同时,也给数据的安全带来一定的隐患。因此,要真正做到各类数据在数据库中安全管理,研究和探索直接将图像数据存储在数据库关系表中的方法是非常必要的。  笔者在Visual Basic 6.0开发环境中,采用客户机/服务器的工作方式,

William's blog 1298

VC 实现adox2

 在上一篇文章《ADO第一次亲密接触》中我们详细介绍了ADO基本的操作方法,在实际的开发过程中我们常常需要存储较大的二进制数据对象,比如:图像、音频文件、或其它二进制数据,这些数据我们称之为二进制大对象BLOB(Binary Large Object),其存取的方式与普通数据有所区别。本文将介绍利用ADO在数据库中存取BLOB数据的具体实现过程,并给出实现图像存取显示的完整示例工程。二、前期准

junnan321的专栏 934

VC++在Sql Server数据库中如何存取图片信息

在VC6.0中我们通常用Ado的Field 对象的GetChuck和AppendChunk来读写Blob对象,但是这样做要写很多的代码,其实ado给我们提供了一个更易操作的对象那就是Stream Object,通过它我们可以更容易的操作数据库中的Blob对象,而且可以直接把Blob对象从数据库保存到本地文件,或者直接读取文件写入到数据库中。下面就详细描述如何操作。 首先申明Stream对象

933

vc mysql保存图片_sql server 存储图片

StreamPtr pStm;pStm.CreateInstance("ADODB.Stream");variant_t varOptional(DISP_E_PARAMNOTFOUND,VT_ERROR);pStm->PutType(adTypeBinary);//类型为二进制//得到字段内容的大小longlDataSize=m_pRecordset->GetFields...

weixin_42560067的博客 158

vc mysql保存图片_如何把图片存/取至数据库中!! 转自优快云

答案:如何把图片放到数据库中!!---------------------------------------------------------------首先我们建立一张名为userinfo的表,包含三个字段:id,username,old,photo,其中photo是一个可以存储二进制数据的字段。2.1在SQLSERVER中我们可以在QueryAnalyzer中直接输入如下语句创建:C...

weixin_42134117的博客 278

数据库存储大对象

         在用Visual C 编写应用程序时,常会遇到如何存取数据库中大对象的问题。大对象文档以二进制数据形式保存在BLOB类型的字段中,这些大对象可能是Word、Execl或图片文件等,目前多数数据库都支持BLOB类型的字段。   VC存取这些大对象数据有众多方法,如OLE、ActiveX等,事实上VC的MFC提供了一个很方便的实现方法,即利用MFC提供的CLongBinary类可

Q_Jizi(七之子)的专栏 1879

VC++数据库编程核心技术:MFC ODBC类深度解析与实战优化

数据库编程是连接应用程序与数据存储的核心技术,其本质在于通过标准化的接口协议实现高效、安全的数据交换。在C++生态中,ODBC作为跨数据库的统一接口基石,提供了驱动程序管理器与驱动程序的标准化调用层,确保了应用程序与多种数据库的兼容性。这一原理为上层封装框架奠定了技术基础,其价值在于平衡了开发效率与底层控制力,尤其适用于对性能与资源有严苛要求的金融、工业控制等场景。MFC ODBC类库正是基于此构建的经典封装,通过CDatabase和CRecordset等核心类,将ODBC API的面向过程调用转化为面向对

weixin_33739646的博客 825
上一篇: IBM Thinkpad 2007-DH1
下一篇: DVD结构
jonfei
博客等级 码龄25年 10粉丝 56原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值