Passing a SQL ntext field into VB

Here's the stored proc:
(Body is the ntext field)

CREATE Procedure usp_GetMessage
(
      @MessageID int,
   
      @Subject varchar(255) output,
      @Body ntext output,
      @Deliver char(12) output,
      @Marketing varchar(50) output,
      @Name varchar(50) output,
      @Email varchar(100) output,
      @OrgID int output
)
As
      Select @Subject = Subject, @Body = Body, @Deliver = convert(char(12), Deliver), @Marketing = Marketing, @Name = Name, @Email = Email, @OrgID = OrgID
      From Messages
      Where ID = @MessageID

SQL freaks out saying the assignment operator can't take ntext as an argument.

This SP gets called from a VB COM object (here's some of the call):

    With cmd
        .ActiveConnection = dsn
        .CommandType = ADODB.adCmdStoredProc
        .CommandText = "usp_GetMessage"
        .Parameters.Append .CreateParameter("@MessageID", adInteger, adParamInput, 4, MsgID)
        .Parameters.Append .CreateParameter("@Subject", adVarChar, adParamOutput, 255)
        .Parameters.Append .CreateParameter("@Body", adVarChar, adParamOutput, 4000)

As you can see from that I orginally had the field as nvarchar but I'm trying to change it to ntext so it can hold larger pieces of text.

So, I need to know how to get an ntext field (body) to be delivered to VB through the above described code.

forloopAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

agarwal_rahulCommented:
Hi,

nText, Text and Image data types are handled as BLOB objects, thus for using this datatypes from VB, u will have to use AppendChunk and GetChunk methods of ADO.

Here is an e.g which have all the functions required.

Hope it helps
Rahul

HOWTO: Sample Functions Demonstrating GetChunk and AppendChunk
ID: Q194975

 

--------------------------------------------------------------------------------
The information in this article applies to:

ActiveX Data Objects (ADO), versions 1.5, 2.0

--------------------------------------------------------------------------------


SUMMARY
This article describes reading and writing Binary Large Objects (BLOBs) using GetChunk and AppendChunk methods against fields in ADO. It also includes sample code using the NWIND sample database.



MORE INFORMATION
The GetChunk and AppendChunk methods work with the LongVarChar and LongVarBinary column types, also known as TEXT and IMAGE columns, in Microsoft SQL Server, and as MEMO and OLE fields in Microsoft Jet databases. You can identify these columns in ADO by testing the Type property of a Field for the values adLongVarChar and adLongVarBinary. You can also test the Attributes property of a Field for the adFldLong flag:


If fld.Attributes And adFldLong Then
   ' You can use GetChunk/AppendChunk
Long columns are commonly referred to as BLOBs (Binary Large OBjects) even though they may contain text data. The sample code below provides two routines, BlobToFile and FileToBlob.
BlobToFile
BlobToFile determines the data type of the field and which of three methods to use to write the BLOB data to a disk file. If the BLOB data is small enough, it will reference the field value in its entirety without calling GetChunk. If the BLOB size is unknown, it will call WriteFromUnsizedBinary or WriteFromUnsizedText to write the data. This is less efficient in terms of making extra copies of the data in local memory than the WriteFromBinary and WriteFromText routines that are used when the size of the BLOB data is known:

BlobToFile                Calls one of the below routines to use GetChunk
    WriteFromBinary         Writes a LongVarBinary of known size to disk
    WriteFromUnsizedBinary  Writes a LongVarBinary on unknown size
    WriteFromText           Writes a LongVarChar of known size
    WriteFromUnsizedText    Writes a LongVarChar of unknown size
FileToBlob
FileToBlob determines whether to use AppendChunk or directly assign the data to the BLOB field based on the size of the file. Because the size of the file can always be determined, there are no "Unsized" routines as there are in the BlobToFile sample code:

FileToBlob          Calls one of the below routines to use AppendChunk
    ReadToBinary      Reads a file into a LongVarBinary column
    ReadToText        Reads a file into a LongVarChar column
Example
The sample code for BlobToFile and FileToBlob is stored in a Module, while the test code is behind the default form. The test code uses each of the three methods to save to disk the Photo (IMAGE/OLE/LongVarBinary) and Notes (TEXT/MEMO/LongVarChar) fields for Andrew Fuller from the Employees table of the NWIND database. It then reads the files back in and creates six new records, reading each of the three sets of files via the two different read methods.
Preparing the data
In Microsoft Access or other tool, open NWIND.MDB.


Open the Employees table (or form) and locate "Andrew Fuller."


Paste the contents of a large text file (between 30000 and 60000 bytes) into the Notes field.


Save the changes and exit Access.


Add an ODBC datasource that points to the NWIND.MDB file.


Sample Code
WARNING: ANY USE BY YOU OF THE CODE PROVIDED IN THIS ARTICLE IS AT YOUR OWN RISK. Microsoft provides this code "as is" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose. Microsoft does not support modifications to this code.
Create a new Visual Basic project and add Project | References to:
Microsoft ActiveX Data Objects 1.5 Library or Microsoft ActiveX Data Objects 2.0 Library



Add two CommandButtons (cmdSave and cmdLoad) to the default form(Form1).


Add the following code. You will have to change the connect string supplied on the "cn.Open" line:

      Option Explicit

      Private Sub CmdSave_Click()
      Dim cn As ADODB.Connection, rs As ADODB.Recordset, SQL As String
        Set cn = New ADODB.Connection
        Set rs = New ADODB.Recordset
        cn.CursorLocation = adUseServer
        cn.Open "dsn=nwind_jet"   ' *** change this ***
        SQL = "SELECT * FROM Employees WHERE LastName='Fuller'"
        rs.Open SQL, cn, adOpenStatic, adLockReadOnly
      '
      ' Save using GetChunk and known size.
      ' FieldSize (ActualSize) > Threshold arg (16384)
      '
        BlobToFile rs!Photo, "c:\photo1.dat", rs!Photo.ActualSize, 16384
        BlobToFile rs!Notes, "c:\notes1.txt", rs!Notes.ActualSize, 16384
      '
      ' Save using GetChunk and unknown size.
      ' FieldSize not specified.
      '
        BlobToFile rs!Photo, "c:\photo2.dat"
        BlobToFile rs!Notes, "c:\notes2.txt"
      '
      ' Save without using GetChunk
      ' FieldSize (ActualSize) < Threshold arg (defaults to 1Mb)
      '
        BlobToFile rs!Photo, "c:\photo3.dat", rs!Photo.ActualSize
        BlobToFile rs!Notes, "c:\notes3.txt", rs!Notes.ActualSize

        rs.Close
        cn.Close
      End Sub

      Private Sub CmdLoad_Click()
      Dim cn As ADODB.Connection, rs As ADODB.Recordset, SQL As String
        Set cn = New ADODB.Connection
        Set rs = New ADODB.Recordset
        cn.CursorLocation = adUseServer
        cn.Open "dsn=ole_db_nwind_jet"
        SQL = "SELECT * FROM Employees"
        rs.Open SQL, cn, adOpenKeyset, adLockOptimistic
      '
      ' Load using AppendChunk
      '
        rs.AddNew
        rs!FirstName = "Test"
        rs!LastName = "Fuller11"
        FileToBlob "c:\photo1.dat", rs!Photo, 16384
        FileToBlob "c:\notes1.txt", rs!Notes, 16384
        rs.Update

        rs.AddNew
        rs!FirstName = "Test"
        rs!LastName = "Fuller21"
        FileToBlob "c:\photo2.dat", rs!Photo, 16384
        FileToBlob "c:\notes2.txt", rs!Notes, 16384
        rs.Update

        rs.AddNew
        rs!FirstName = "Test"
        rs!LastName = "Fuller31"
        FileToBlob "c:\photo3.dat", rs!Photo, 16384
        FileToBlob "c:\notes3.txt", rs!Notes, 16384
        rs.Update

      '
      ' Load without using AppendChunk
      '
        rs.AddNew
        rs!FirstName = "Test"
        rs!LastName = "Fuller12"
        FileToBlob "c:\photo1.dat", rs!Photo
        FileToBlob "c:\notes1.txt", rs!Notes
        rs.Update

        rs.AddNew
        rs!FirstName = "Test"
        rs!LastName = "Fuller22"
        FileToBlob "c:\photo2.dat", rs!Photo
        FileToBlob "c:\notes2.txt", rs!Notes
        rs.Update

        rs.AddNew
        rs!FirstName = "Test"
        rs!LastName = "Fuller32"
        FileToBlob "c:\photo3.dat", rs!Photo
        FileToBlob "c:\notes3.txt", rs!Notes
        rs.Update

        rs.Close
        cn.Close
      End Sub



Add a new module to the project (Module1) with the following code:

      Option Explicit

      Const BLOCK_SIZE = 16384

      Sub BlobToFile(fld As ADODB.Field, ByVal FName As String, _
                     Optional FieldSize As Long = -1, _
                     Optional Threshold As Long = 1048576)
      '
      ' Assumes file does not exist
      ' Data cannot exceed approx. 2Gb in size
      '
      Dim F As Long, bData() As Byte, sData As String
        F = FreeFile
        Open FName For Binary As #F
        Select Case fld.Type
          Case adLongVarBinary
            If FieldSize = -1 Then   ' blob field is of unknown size
              WriteFromUnsizedBinary F, fld
            Else                     ' blob field is of known size
              If FieldSize > Threshold Then   ' very large actual data
                WriteFromBinary F, fld, FieldSize
              Else                            ' smallish actual data
                bData = fld.Value
                Put #F, , bData  ' PUT tacks on overhead if use fld.Value
              End If
            End If
          Case adLongVarChar
            If FieldSize = -1 Then
              WriteFromUnsizedText F, fld
            Else
              If FieldSize > Threshold Then
                WriteFromText F, fld, FieldSize
              Else
                sData = fld.Value
                Put #F, , sData  ' PUT tacks on overhead if use fld.Value
              End If
            End If
        End Select
        Close #F
      End Sub

      Sub WriteFromBinary(ByVal F As Long, fld As ADODB.Field, _
                          ByVal FieldSize As Long)
      Dim Data() As Byte, BytesRead As Long
        Do While FieldSize <> BytesRead
          If FieldSize - BytesRead < BLOCK_SIZE Then
            Data = fld.GetChunk(FieldSize - BLOCK_SIZE)
            BytesRead = FieldSize
          Else
            Data = fld.GetChunk(BLOCK_SIZE)
            BytesRead = BytesRead + BLOCK_SIZE
          End If
          Put #F, , Data
        Loop
      End Sub

      Sub WriteFromUnsizedBinary(ByVal F As Long, fld As ADODB.Field)
      Dim Data() As Byte, Temp As Variant
        Do
          Temp = fld.GetChunk(BLOCK_SIZE)
          If IsNull(Temp) Then Exit Do
          Data = Temp
          Put #F, , Data
        Loop While LenB(Temp) = BLOCK_SIZE
      End Sub

      Sub WriteFromText(ByVal F As Long, fld As ADODB.Field, _
                        ByVal FieldSize As Long)
      Dim Data As String, CharsRead As Long
        Do While FieldSize <> CharsRead
          If FieldSize - CharsRead < BLOCK_SIZE Then
            Data = fld.GetChunk(FieldSize - BLOCK_SIZE)
            CharsRead = FieldSize
          Else
            Data = fld.GetChunk(BLOCK_SIZE)
            CharsRead = CharsRead + BLOCK_SIZE
          End If
          Put #F, , Data
        Loop
      End Sub

      Sub WriteFromUnsizedText(ByVal F As Long, fld As ADODB.Field)
      Dim Data As String, Temp As Variant
        Do
          Temp = fld.GetChunk(BLOCK_SIZE)
          If IsNull(Temp) Then Exit Do
          Data = Temp
          Put #F, , Data
        Loop While Len(Temp) = BLOCK_SIZE
      End Sub

      Sub FileToBlob(ByVal FName As String, fld As ADODB.Field, _
                     Optional Threshold As Long = 1048576)
      '
      ' Assumes file exists
      ' Assumes calling routine does the UPDATE
      ' File cannot exceed approx. 2Gb in size
      '
      Dim F As Long, Data() As Byte, FileSize As Long
        F = FreeFile
        Open FName For Binary As #F
        FileSize = LOF(F)
        Select Case fld.Type
          Case adLongVarBinary
            If FileSize > Threshold Then
              ReadToBinary F, fld, FileSize
            Else
              Data = InputB(FileSize, F)
              fld.Value = Data
            End If
          Case adLongVarChar
            If FileSize > Threshold Then
              ReadToText F, fld, FileSize
            Else
              fld.Value = Input(FileSize, F)
            End If
        End Select
        Close #F
      End Sub

      Sub ReadToBinary(ByVal F As Long, fld As ADODB.Field, _
                       ByVal FileSize As Long)
      Dim Data() As Byte, BytesRead As Long
        Do While FileSize <> BytesRead
          If FileSize - BytesRead < BLOCK_SIZE Then
            Data = InputB(FileSize - BytesRead, F)
            BytesRead = FileSize
          Else
            Data = InputB(BLOCK_SIZE, F)
            BytesRead = BytesRead + BLOCK_SIZE
          End If
          fld.AppendChunk Data
        Loop
      End Sub

      Sub ReadToText(ByVal F As Long, fld As ADODB.Field, _
                     ByVal FileSize As Long)
      Dim Data As String, CharsRead As Long
        Do While FileSize <> CharsRead
          If FileSize - CharsRead < BLOCK_SIZE Then
            Data = Input(FileSize - CharsRead, F)
            CharsRead = FileSize
          Else
            Data = Input(BLOCK_SIZE, F)
            CharsRead = CharsRead + BLOCK_SIZE
          End If
          fld.AppendChunk Data
        Loop
      End Sub



Run the project and click the cmdSave button.


In the C:\ directory, you should find the following files:

notes1.txt
notes2.txt
notes3.txt

photo1.dat
photo2.dat
photo3.dat

The three "photo" files should be the same size as each other. The three
"notes" files should be the same size as each other.



Click the cmdLoad button.


Open the database using Access and you should see six additional employees with photos and notes loaded back correctly.


Notes
Following are some suggestions for using BLOBs with ADO. These parallel many of the suggestions in the following Microsoft Knowledge Base article:
Q153238 HOWTO: Use GetChunk and AppendChunk Methods of RDO Object
It is more efficient in terms of retrieval of BLOB data to simply store the data in files on the server with a pointer in the main record (or you can use some sort of structured directory/file naming system based on the primary key value). This has the advantage of (a) eliminating server overhead, (b) allowing the files to be stored on a second server, (c) allowing network security attributes to be set on individual files, and (d) allowing retrieval of files even when the server is down. This is especially true if the files are some sort of document type, such as bitmaps (.bmp), word processor files (.doc), or spreadsheets (.xls) where you can point the host application directly to the file on the server.


When using certain providers, most notably ODBC to SQL Server and other databases, you may have to take special care in retrieving BLOB data, such as placing BLOB columns at the end of the field list and referencing all non-BLOB fields prior to access BLOB columns. This will depend on a number of factors, such as:
Provider (typically ODBC)


Back-end server


Cursor Location (typically client)


Cursor Type


Whether you're selecting from a VIEW or getting records returned from a stored procedure.





Because this depends on a variety of factors, below is a guide if you are having problems with BLOB columns:
Try a native OLE DB provider instead of an ODBC provider.


Use Server-side cursors (such as adOpenKeyset).


Select the Primary Key column(s) in addition to any other columns.


Select the BLOB columns last. Select individual fields, not "*".


Access all non-BLOB columns first (store them if necessary).


Access BLOB columns in the order specified. You may only be able to reference it once before the cursor loses its value.


When editing a BLOB column using the AppendChunk method, you may have to edit at least one non-BLOB column in your recordset as well. BLOBs are typically not updateable with Static or Forward-only cursors on ODBC datasources.


If you use ODBC to Jet, you can't update a recordset returned by a stored procedures (QueryDef) at all because the driver forces them to be read-only.


The Microsoft Oracle OLE DB provider does not currently support random Access to BLOB data with server-side cursors - the BLOB column must appear to the end of the SELECT clause.


With the ODBC cursor library, it is not possible to use the GetChunk or AppendChunk methods on a recordset returned from a stored procedure. This is because the BLOB data is not normally retrieved with the rest of the data in order to save bandwidth. When a stored procedure creates a recordset, the cursor driver cannot determine how to query for the BLOB data after the fact because it cannot determine the base tables or key fields to use. Server-side cursors alleviate this problem but limit you to a single statement per stored procedure (a SQL Server restriction).

The fact that users want to update their BLOB column demands that they expose their base tables and create the cursor by using a standard select statement from that base table. This would be true even if you were coding directly to ODBC (not an ADO thing).



REFERENCES
For additional information, please see the following articles in the Microsoft Knowledge Base:

Q185958 HOWTO: Use ADO GetChunk/AppendChunk with Oracle for BLOB Data
Q173611 IX: ADO GetChunk Method Breaks Blob Data for DBCS
Q189415 FILE: AdoChunk.exe Using GetChunk and AppendChunk in Visual C++
Using Data Access Objects:
Q103257 ACC: Reading, Storing, & Writing Binary Large Objects (BLOBs)
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
MTroutwineCommented:
Here is a way of calling a stored procedure using ADO that does not require you to use any Parameter or Command objects and should retrieve the nText value with little code.  Instead use a Recordset object and code it like this:

With rstRecordset
    .ActiveConnection = dsn
    .Source = "EXECUTE usp_GetMessage " & MessageValue
    .Open
    ' Assign values to local variables:
    strSubject = !Subject
    strBody = !Body
    strDeliver = !Deliver
    strMarketing = !Marketing
    strName = !Name
    strEmail = !Email
    intOrgID = !OrgID
end with

Obviously if you are itterating through records you will need other code but this should get you going!

0
forloopAuthor Commented:
MTroutwine:
You don't mention how to get the stored proc to give an ntext parameter. I'm still hung up on getting that to work.

BTW, this ntext parameter is then passed into VBScript(ASP).
0
jgriegerCommented:
Have any of you bothered to try this:

With oCmd
.ActiveConnection = oConn
.CommandText = "sp_someproc"
.Parameters.Refresh
.Parameters(1).Size = 2147483647
.Parameters(1) = myBigLongNTEXTString

.Execute
End With

It actually works. I use it all the time. Though, I'm using SQL Server 2000 as the DB.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic Classic

From novice to tech pro — start learning today.