Wrox Programmer Forums
Go Back   Wrox Programmer Forums > Visual Basic > VB 2005 > Pro Visual Basic 2005
|
Pro Visual Basic 2005 For advanced Visual Basic coders working in version 2005. Beginning-level questions will be redirected to other forums, including Beginning VB 2005.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the Pro Visual Basic 2005 section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old March 28th, 2007, 12:45 PM
Authorized User
 
Join Date: Feb 2004
Posts: 16
Thanks: 0
Thanked 0 Times in 0 Posts
Default ADO with text files, problem doing JOIN

I've been working for several days to solve various problems using text files as tables in ADO.
Up until now I've solved them by myself but this one is proving to be tricky. I'm working with
VB.net in VS 5.

Problem: Do a join on two tables, tables are text files in the same directory, there is a correct
schema.ini file describing the text files, one file is fixedlength and the other is csvdelimited.
I have no control over the fixedlength file, it comes from our IBM mainframe. (There are a few
reasons that I don't want to import the text files into real Access tables; let's assume that
isn't an option! I don't want MS access involved at all, not even with linked text-tables and stored
procedures. That will be a last, last, LAST resort.)

This (non-join) code works great:

Code:
connectionstring = "Driver={Microsoft Text Driver (*.txt; *.csv)};DBQ=" & 
StrTxtDbPath & ";Extended Properties=""text;HDR=NO;FMT=FixedLength"""

Using connection As New Odbc.OdbcConnection(connectionstring)
  connection.Open()

  'amtcredit = + or -
  'invamt = double with IMPLIED decimal, needs to be /100 later
  sqlstring = "select agency, invnum, sum(cdbl(amtcredit & invamt))
 as charge from currpacedata.txt group by agency, invnum order by agency;"

  Dim reader As Odbc.OdbcDataReader = command.ExecuteReader()
  If reader.HasRows Then
    Do While reader.Read()
      money = Format(reader("charge") / 100, "###,###,##0.00")
      Console.WriteLine(reader("agency") & "  " & reader("invnum") &
 "      " & "\$              ".Substring(1, 15 - money.Length) & money)
    Loop
  End If
  reader.Close()
end using

And produces:
AC     0107001  $      1,897.75
AD     0107002  $        933.33
AE     0107003  $        366.00
AG     0107004  $        138.19
AU     0107005  $      1,832.00
etc.
HOWEVER! This (join) code does not work :(

Code:
  'amtcredit = + or -
  'invamt = double with IMPLIED decimal, needs to be /100 later
  sqlstring = "SELECT Currpacedata.txt.[agency], AgencyMapping.txt.[agencyname] 
FROM Currpacedata.txt inner join AgencyMapping.txt 
ON Currpacedata.txt.[agency] = AgencyMapping.txt.[agency];"

  Dim reader As Odbc.OdbcDataReader = command.ExecuteReader()
  If reader.HasRows Then
    Do While reader.Read()
      money = Format(reader("charge") / 100, "###,###,##0.00")
      Console.WriteLine(reader("agency") & "     " & reader("agencyname") & 
"     " & reader("invnum") & "  " & 
"\$              ".Substring(1, 15 - money.Length) & money)
    Loop
  End If
  reader.Close()

The SQL string is the problem. You will notice that I have bracketed the tablenames. I also tried
other combinations of bracketing: [table].[field] and [table].field.

With SQL string in the above code I get this error on the command.ExecuteReader() line:
"ERROR [42000] [Microsoft][ODBC Text Driver] Syntax error in JOIN operation". Microsoft.com
gives only a generic description - not helpful!

I tried bracketing the table/field names because when I did NOT bracket them I got the same
JOIN syntax error message and I hypothesized that "SELECT Currpacedata.txt.agency, ..."
might be problematic for the SQL parser.

I had first tried SQL syntax similar to this:
"select cpd.agency, ag.agencyname from currpacedata.txt cpd inner join agencymapping.txt ag
ON cpd.agency = ag.agency".

While I believe that is correct SQL syntax it generated this error:
"ERROR [07002] [Microsoft][ODBC Text Driver] Too few parameters. Expected 1."

If anyone out there has experience or theories I will surely be grateful for what help,
answers or pointers you can give.

-Ken
 
Old March 30th, 2007, 10:40 AM
Friend of Wrox
 
Join Date: Nov 2004
Posts: 1,621
Thanks: 1
Thanked 3 Times in 3 Posts
Default

One thought. You could use Access to generate the SQL, then throw it overboard once you have effective syntax...

Note: You can make postings easier to read by putting a pair of square braces “[]” with “code” in them at the start of the code, and another set with a slash “[/]” and “code” following the slash (“.../code]”). (I have to spell it out, or the website will take action on it instead of letting you see what I'm typing...)
That will make your code fixed spacing, and will retain formatting spaces.

Result:

This (non-join) code works great:
Code:
connectionstring = "Driver={Microsoft Text Driver (*.txt; *.csv)};" & _
                   "DBQ=" & StrTxtDbPath & ";" & _
                   "Extended Properties=""text;HDR=NO;FMT=FixedLength"""

Using connection As New Odbc.OdbcConnection(connectionstring)
    connection.Open()

    'amtcredit = + or -
    'invamt = double with IMPLIED decimal, needs to be /100 later
    sqlstring = "select agency, invnum, " & _
                "       sum(cdbl(amtcredit & invamt)) as  charge " & _
                "from   currpacedata.txt " & _
                "group by agency, invnum " & _
                "order by agency;"

    Dim reader As Odbc.OdbcDataReader = command.ExecuteReader()
    If reader.HasRows Then
        Do While reader.Read()
            money = Format(reader("charge") / 100, "###,###,##0.00")
            Console.WriteLine(reader("agency") & "  " & _
                              reader("invnum") & "      " & _
                              "\$              ".Substring(1, 15 - money.Length) & _
                              money)
        Loop
    End If
    reader.Close()
end using
And produces:
Code:
AC     0107001  $      1,897.75
AD     0107002  $        933.33
AE     0107003  $        366.00
AG     0107004  $        138.19
AU     0107005  $      1,832.00
etc.
HOWEVER! This (join) code does not work :
Code:
  'amtcredit = + or -
  'invamt = double with IMPLIED decimal, needs to be /100 later
  sqlstring = "SELECT Currpacedata.txt.[agency], AgencyMapping.txt.[agencyname] " & _
              "FROM   Currpacedata.txt " & _
              "       inner join AgencyMapping.txt " & _
              "       on Currpacedata.txt.[agency] = AgencyMapping.txt.[agency];"

  Dim reader As Odbc.OdbcDataReader = command.ExecuteReader()
  If reader.HasRows Then
      Do While reader.Read()
          money = Format(reader("charge") / 100, "###,###,##0.00")
          Console.WriteLine(reader("agency") & "     " & _
                            reader("agencyname") & "     " & _
                            reader("invnum") & "  " & _
                            "\$              ".Substring(1, 15 - money.Length) & _
                            money)
      Loop
  End If
  reader.Close()
 
Old April 2nd, 2007, 09:50 AM
Authorized User
 
Join Date: Feb 2004
Posts: 16
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Thanks Brian. I had already tried that idea and this is the trouble I ran into...

When you link a text file named "file1.txt" to an access database it will name the table "file1".
I can then create the query, switch to SQL view and copy/paste the code into my VB program
- no trouble there.

But obviously I will have to fix the table name references:
Code:
select file1.field1, file2.field2 from file1 
inner join file2 on file1.field1 = file2.field1 ...
must be changed to
Code:
select file1.txt.field1, file2.txt.field2 from file1.txt 
inner join file2.txt on file1.txt.field1 = file2.txt.field1 ...
and that SQL is not kosher according to the ODBC text driver, apparently.

I tried renaming the table in Access to file1.txt but I got an error message that the
name did not following object naming rules (No "." allowed). Pretty much back to square one, eh?
 
Old April 2nd, 2007, 11:09 AM
Friend of Wrox
 
Join Date: Nov 2004
Posts: 1,621
Thanks: 1
Thanked 3 Times in 3 Posts
Default

What is the database management system holding the data?
 
Old April 4th, 2007, 11:05 AM
Authorized User
 
Join Date: Feb 2004
Posts: 16
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Hi Brian,

No DBMS, they're just text files, 1 fixed width and 2 csv.

-Ken
 
Old April 4th, 2007, 02:25 PM
Friend of Wrox
 
Join Date: Nov 2004
Posts: 1,621
Thanks: 1
Thanked 3 Times in 3 Posts
Default

(OH yeah... you did mention that these are text files in your original post, didn’t you...)

Try aliasing the datasources
Code:
    "SELECT CP.agency,   AM.agencyname " & _
    "FROM   Currpacedata.txt AS CP" & _
    "       INNER JOIN AgencyMapping.txt AS AM" & _
    "       ON CP.agency = AM.agency;"
    (You might be able to eliminate the actual word "AS," using implicit aliasing.)
 
Old April 5th, 2007, 08:50 AM
Authorized User
 
Join Date: Feb 2004
Posts: 16
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Hi again Brian,

I tried aliasing the table names as one of my first experiments - although without the "AS"
since I wasn't aware it could be used except to (re)assign a field name. That experiment failed.

Actually I think I was fighting more than one problem. Getting the details of the SQL
statement right was a big one; on another forum someone indicated I could use this form:
Code:
select [currpacedata#txt].agency, [agencymapping#txt].agencyname 
  from [currpacedata#txt] inner join [agencymapping#txt]
  on [currpacedata#txt].agency = [agencymapping#txt].agency
  group by [currpacedata#txt].agency, [agencymapping#txt].agencyname;

That same contributor suggested that I use the Jet engine instead of the textfile driver.
That unusual coding worked when I *didn't* do a join but not when I did. After some additional
testing (read "flailing around"), and no small amount of cursing, I hypothesized that there
was a problem when doing a join between a CSV and a fixedformat file - and my theory paid off!
I switched the agencymapping.txt "table" to a fixed length table (and modified schema.ini) and
the join worked!

I want to do an experiment to try to get the mixed-format join to work -- I'll try modifying the
connect string to do that. Once I find out all the details I'll post a full review of the solution
here. While this has been exceedingly frustrating it's also the kind of problem I love to solve.
It's been great to have two smart guys giving me ideas and of course you two will get credit when
I post the solution.
 
Old April 5th, 2007, 09:52 AM
Authorized User
 
Join Date: Feb 2004
Posts: 16
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Brian,

My experimentation is over. I am confident that you cannot do a join between text files
using the ODBC text driver or the Jet engine if the text files are not all of the same format.
That is, they must all be CSV format or fixed format, not a mixture.

That problem is what made my quest last so many days. If my original files were both fixed format
I'd have been great a week ago! I have no control over the fixed format file, it comes
to me from the mainframe. But who in their right mind would create a new file that wasn't CSV?
So naturally when I was creating the "tables" I needed to join with I made them CSVs, especially
since the source for the data was given to me in Excel files and they export nicely to CSV
but not at all to fixed width.

It also doesn't matter if you use the mysterious "#" format or alias the tables
in the SQL statement. So,

Code:
sqlstring = "SELECT [Currpacedata#txt].agency, [agencymapping2#txt].agencyname, 
[Currpacedata#txt].invnum, Sum(CDbl([amtcredit] & [invamt])) AS charge
 FROM Currpacedata.txt INNER JOIN AgencyMapping2#txt 
ON [Currpacedata#txt].agency = [agencymapping2#txt].agency 
GROUP BY [Currpacedata#txt].agency, [agencymapping2#txt].agencyname,
[Currpacedata#txt].invnum;"
and
Code:
sqlstring = "SELECT cpd.agency, agencyname, invnum, 
Sum(CDbl([amtcredit] & [invamt])) AS charge 
FROM Currpacedata#txt cpd INNER JOIN AgencyMapping2.txt ag 
ON cpd.agency = ag.agency GROUP BY cpd.agency, agencyname, invnum;"
are exactly the same and the latter is a lot easier to type!

I'll post a full report after my programming changes are done. I have to determine the best way
to ensure that the two tables I need to join with are fixed format.
 
Old April 5th, 2007, 10:43 AM
Friend of Wrox
 
Join Date: Nov 2004
Posts: 1,621
Thanks: 1
Thanked 3 Times in 3 Posts
Default

That sounds reasonable (regarding mixing formats).
Perhaps you could read in one of the files, then read it back out to a temp file in the right format...

(If you wouldn't mind, could you edit your post with the really long lines so the post is not so wide?
 Makes it hard to read; I have to scroll sideways to read every line...)
 
Old April 16th, 2007, 03:02 PM
Authorized User
 
Join Date: Feb 2004
Posts: 16
Thanks: 0
Thanked 0 Times in 0 Posts
Default

BACKGROUND:

Since I spent about 1 1/2 weeks total finding information about using
text files as data tables and then getting the whole scenario working
properly, and since several people in this forum and the Wrox forum
responded to me with help and suggestions, I felt a responsiblity to
post a very thorough description of the problem and the solution.

The overall project was to take a flat, fixed width data file that gets
produced monthly by our IBM mainframe billing system and convert that
into an XML file for importing into the PeopleSoft General Ledger. I work
for the Department of Information Technology (DOIT) for the State of CT.
My agency bills all the other agencies for technology resource usage each
month. The XML conversion was not an issue, I finished that a few months
ago - and then they changed the spec - no problem I fixed that up in a few hours.

Then my boss asked me about Audit Control reports. Audit control reports??
Nobody had ever given me a spec for those and I didn't even know they existed!
Well, they did and when I saw them it was apparent that each report could be
generated extremely easily with one SQL statement. The problem was, I had a text
file. Additional problem, the text file didn't have some fields I needed, but it
had two nice foreign keys if only I could do a join.

You're asking, "Why didn't you put the damned records into a database and save
yourself a lot of time, you stupid git?!" The short answer is "politics." This
is a tiny program. It runs in less than one second. We use it once each month.
If I had introduced a database into the mix then I would be forced by agency
policy to implement a 3-tier architecture. That is serious overkill for my very
important but teensy little program! I ONLY needed the database for the
reporting so I could use SQL to do a join and use the SUM function with a couple
of GROUP BYs. It made no sense to enter the gates of political hell just to
generate three pages worth of audit control reports!

PROBLEM:

Do a join on two tables, tables are text files in the same directory,
there is a correct schema.ini file describing the text files, one file is
fixedlength and the other is csvdelimited. I have no control over the
fixedlength file, it comes from our IBM mainframe. (I don't want databases
involved at all, not even Access with linked text-tables. That will be a last,
last, LAST resort.)

RESEARCH:

I knew there were Microsoft drivers that allowed you to use text files as data
tables. I'd never done it. I tried searching MS site but didn't come up with any
hits. I tried googling the same search term. I then tried many other search
terms and *finally* found a few references at various sites including MS.


100% of those references, taken by themselves, sucked. Not a single one of them
gave a complete example, many of them leaving out quite crucial information,
like the schema.ini file, for instance. So I spent some time gathering the
little pieces of the picture into a workable whole. My first experiment, a
SELECT statement that counted the records, was successful! Next I counted a
subset of records with a WHERE clause and again the answer was correct. Hoorah
for me! Right?

Naturally, no, not right :( because the next thing I tried was the needed join
and that failed miserably with an error message that was quite general in nature
and therefore wasn't helpful. I did many more experiments and then decided it
was time to seek help in two forums I was familiar with, one on the MS site and
one at Wrox.

FINAL RESULT / SOLUTION:

Several days, many curses, several forum posts, and more experiments than I can
count later and here is the cause of all my problems: You CANNOT do a join
between a CSVformat file and a Fixedlength file. The drivers do not support it,
period. If you need to do joins you will have to have all of your text files be
of the same type, CSV or Fixed.

TUTORIAL:

Here is what I intend to be a thorough tutorial on using the text drivers in
order to be able to run SQL SELECT statements on text files. I believe that
other kinds of SQL statements like INSERT and UPDATE and DELETE will not work
although I have not tried them. But I think I read that the drivers provide
“read-only” access to the text files.

In the world of databases, a DATABASE holds TABLES and TABLES hold FIELDS.
Since text files are not structured entities, they’re just rows of plain text
with some defined order to them, the text drivers fudge the concept of a
database a little. They consider the directory to be the database and every
file in them to be potential tables.

SCHEMA.INI
In order for the text drivers to understand what is in the text files you need
to create a plain-text file called SCHEMA.INI in the *same directory* as the
text files you want to use as tables. Each text file you want to use as a table
will be described in the schema.ini file. The text driver will read this file
to allow it to understand where the FIELDS are and what data type they are.


This is the SCHEMA.INI file that I used:
Code:
[AgencyMapping.txt]
Format=FixedLength
ColNameHeader=False
MaxScanRows=0

Col1=agency Text width 2
col2=f1 text width 1
Col3=coreagency text width 6
col4=f2 text width 1
Col5=agencyname text width 32


[GLCodeToChartfield2Mapping.txt]
Format=FixedLength
ColNameHeader=False
MaxScanRows=0

Col1=glcode Text width 4
col2=f1 text width 1
Col3=chartfield2 text width 7
col4=f2 text width 1
Col5=cfield2descr text width 30


[currpacedata.txt]
Format=FixedLength
ColNameHeader=False
MaxScanRows=0

Col1=agency Text Width 2
Col2=series Text Width 1
Col3=f1 Text width 1
col4=invnum text width 7
col5=f2 text width 2
col6=invamt text width 9
col7=amtcredit text width 1
col8=f3 text width 2
col9=invqty text width 14
col10=qtycredit text width 1
col11=f4 text width 1
col12=glcode text width 4
col13=f5 text width 1
col14=xferdescr text width 34


[AgencyMapping.csv]
Format=CSVDelimited
ColNameHeader=False
MaxScanRows=0

Col1=agency Text
Col2=coreagency, text
Col3=agencyname, text


[GLCodeToChartfield2Mapping.csv]
Format=CSVDelimited
ColNameHeader=False
MaxScanRows=0

Col1=glcode, Text
Col2=chartfield2, text
Col3=cfield2descr, text
You can see that I’ve defined three TABLES, AgencyMapping.txt,
GLCodeToChartfield2Mapping.txt, and currpacedate.txt. I also included the
definitions for the delimited versions of AgencyMapping.csv and
GLCodeToChartfield2Mapping.csv. You’ll note that for my uses I set all the
field types to TEXT, but that is not the only choice. See
(http://msdn.microsoft.com/library/de...a_ini_file.asp)
for notes on schema.ini and data types.


THE CONNECTION STRING:

I don’t like using bound data controls. That’s just a preference from my VB3 and
VB5 days when very often the bound controls were less efficient than coding
with data objects directly. Feel free to adapt this any way you like; there must
be 10 ways to open a table or run a query using the various database objects.
This one is adapted from one of the examples I found.


These two connection strings work equally well. The first specifies the TEXT
driver and the second specifies the JET engine.

Code:
Dim sqlstring As String
Dim connectionstring As String
Dim StrTxtDbPath As String
Dim money As String
Dim ReportString As String

StrTxtDbPath = frmKomandToCore.WorkingDirectory & "\"

connectionstring = "Driver={Microsoft Text Driver (*.txt; *.csv)};
DBQ=" & StrTxtDbPath & ";Extended Properties=""text;HDR=NO;FMT=FixedLength"""

connectionstring = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & StrTxtDbPath & 
";Extended Properties=""text;HDR=NO"""
Please note that the path you supply for the location of the text files *must*
end with the “\” character. The rest of the example uses the Jet engine only
because I got the code working with that driver and was too lazy to switch back,
especially for no good reason. If you decide to use the text driver your data
objects will be from the system.data.odbc class instead of the system.data.oledb
class.

Some comments about the SQL JOIN statement are in order because there’s some
unusual syntax that can appear. It’s no mystery that when you’re doing a join,
that if there are fields with the same name in both tables you need to pre-pend
those fields’ names in the SQL statement with the table name:
Code:
Select tbl1.fld1, tbl1.fld2, fld3 
from tbl1 inner join tbl2 
on tbl1.fld1 = tbl2.fld1 where …..
Or
Select tbl1.fld1, tbl1.fld2, fld3 
from tbl1, tbl2 where tbl1.fld1 = tbl2.fld1 and …..
Now the general syntax for a SELECT statement with a text-file table is this:
Code:
Select fld1, fld2, … fldN from table1.txt where …
So if you extend that to a JOIN operation on two text files you get:
Code:
Select table1.txt.fld1, table1.txt.fld2, fld3 
from table1.txt 
inner join table2.txt 
on table1.txt.fld1 = table2.txt.fld1 where …
And as you might guess that causes some problems. The SQL parser doesn’t
understand how to deal with “table1.txt.fld1.”

There are two ways around this problem, a special syntax and table aliasing. So
these two SQL statements are equivalent when doing joins on text-file tables.
(The word “as” when aliasing a table is [wrong/optional] and may be non-standard
SQL; it’s new to me.)
Code:
Select [table1#txt].fld1, [table1#txt].fld2, fld3 
from table1.txt inner join table2.txt 
on [table1#txt].fld1 = [table2#txt].fld1 where …
Or
Select t1.fld1, t1.fld2, fld3 from table1.txt (as) t1 
inner join table2.txt (as) t2 on t1.fld1 = t2.fld1 where …
The guts of the routine that produces one of the audit control reports is below.
You should be able to use the information in this post to use delimited or fixedlength
text files as data tables with no trouble.

You may safely ignore the quirks of my data and concentrate on
what’s being done and how it’s being accomplished.

Code:
Using connection As New OleDbConnection(connectionstring)
  connection.Open()

  '= = = = = = = = = = = = = = = = = = = = = = = = =
  'Generate Billing By Client report
  '= = = = = = = = = = = = = = = = = = = = = = = = =
  'sqlstring = "SELECT [Currpacedata#txt].agency, [agencymapping#txt].agencyname, 
[Currpacedata#txt].invnum, Sum(CDbl([amtcredit] & [invamt])) AS charge
FROM Currpacedata#txt INNER JOIN AgencyMapping#txt ON [Currpacedata#txt].agency = [agencymapping#txt].agency 
GROUP BY [Currpacedata#txt].agency, [agencymapping#txt].agencyname, [Currpacedata#txt].invnum;"

  sqlstring = "SELECT cpd.agency, agencyname, invnum, Sum(CDbl([amtcredit] & [invamt])) AS charge 
FROM Currpacedata.txt cpd INNER JOIN AgencyMapping.txt ag 
ON cpd.agency = ag.agency 
GROUP BY cpd.agency, agencyname, invnum;"

  Dim command As New OleDbCommand(sqlstring, connection)

  Dim reader As OleDbDataReader = command.ExecuteReader()
  If reader.HasRows Then
    Do While reader.Read()
      money = Format(reader("charge") / 100, "###,###,##0.00")
      ReportString &= reader("agency").ToString & "     " & reader("agencyname").ToString & 
utils.StringOf(" ", 40).Substring(1, 40 - reader("agencyname").ToString.Length) & 
reader("invnum").ToString & "     " & "\$              ".Substring(1, 15 - money.Length) & 
money & vbCrLf
    Loop
  End If
  reader.Close()

  'spit out ReportString to the console and/or a text file and you’re done

Thanks for all the help to:
BrianWren of the Wrox forum (http://p2p.wrox.com/topic.asp?TOPIC_ID=58350)
and
Cybertaz69 and Clint from (http://forums.microsoft.com/MSDN/Sho...44779&SiteID=1)





Similar Threads
Thread Thread Starter Forum Replies Last Post
ADO.NET dataset with join divinyl Crystal Reports 4 January 26th, 2006 03:12 AM
Copying files using VBScript in ADO 3.0 munrrob Classic ASP Basics 2 October 26th, 2004 05:35 AM
Oracle 8i inner join and left join problem puteri_84 Oracle 2 August 19th, 2004 07:14 AM
ADO.net + include files eddiema ADO.NET 3 December 12th, 2003 03:43 AM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.