473,756 Members | 1,969 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Can inner join replace multiple loop selects?

MP
Hi
trying to begin to learn database using vb6, ado/adox, mdb format, sql
(not using access...just mdb format via ado)
i need to group the values of multiple fields
- get their possible variations(comb ination of fields),
- then act on each group in some way ...eg ProcessRs (oRs as RecordSet)...

the following query will get me the distinct groups
strSQL = "SELECT DISTINCT Field0, Field1, Field2, Field3 From " & TABLE_NAME
& _
" ORDER BY Field0, Field1, Field2, Field3"
Set oRs1 = moConn.Execute( strSQL)
Do While Not oRs1.EOF
Debug.Print "Group " & i
j = 0
For Each oFld In oRs1t.Fields
Debug.Print "Field" & j & vbTab & "-Value: " & oFld.Value
j = j + 1
Next oFld
oRs1.MoveNext
i = i + 1
Loop
....results in ....
Group 0
Field0-Value:A;Field1-Value:1;Field2-Value:0;Field3-Value:1NA;
Group 1
Field0-Value:A;Field1-Value:1;Field2-Value:1;Field3-Value:1NA;
Group 2
Field0-Value:A;Field1-Value:1;Field2-Value:1;Field3-Value:2L;
Group 3
Field0-Value:A;Field1-Value:1;Field2-Value:1;Field3-Value:3R;
Group 4
Field0-Value:A;Field1-Value:1;Field2-Value:1;Field3-Value:4B;
Group 5
Field0-Value:A;Field1-Value:1;Field2-Value:2;Field3-Value:1NA;
Group 6
Field0-Value:A;Field1-Value:1;Field2-Value:3;Field3-Value:1NA;
Group 7
Field0-Value:A;Field1-Value:1;Field2-Value:4;Field3-Value:1NA;

so I have 7 groups to process
do i need to loop back through the database 7 times to get all records for
each group?
Process Group0
Select * From Table_Name Where Field0 = A and Field1 = 1 and Field2 = 0 and
Field3 = 1Na
Process Group1
Select * From Table_Name Where Field0 = A and Field1 = 1 and Field2 = 1 and
Field3 = 1Na
Process Group2
Select * From Table_Name Where Field0 = A and Field1 = 1 and Field2 = 1 and
Field3 = 2L
Process Group3
Select * From Table_Name Where Field0 = A and Field1 = 1 and Field2 = 1 and
Field3 = 3R
Process Group4
Select * From Table_Name Where Field0 = A and Field1 = 1 and Field2 = 1 and
Field3 = 4B
....etc....
or is there some way to combine the grouping, and subsequent processing of
each distinct recordset group, all in one sql statement?

is there a one-step way to divide the recordset in that way?...some how
using aliases and joins when there's only one table???

something like this seems to work for the multiple loop way
Dim oRs2 As Recordset
Dim oFld2 As Field

'get the distinct group list
strSQL = "SELECT distinct fldTypeName, fldCondName, fldCondVar, fldCondLbr
From " & TABLE_NAME & _
" ORDER BY fldTypeName, fldCondName, fldcondvar, fldCondLbr"
Set oRs1 = moConn.Execute( strSQL)
If Not oRs1 Is Nothing Then
Do While Not oRs1.EOF
j = 0
strSQL = "Select * From " & TABLE_NAME & " WHERE "
For Each oFld In oRs1.Fields
strSQL = strSQL & oFld.Name & " = '" & oFld.Value & "' AND "
j = j + 1
Next oFld
'REMOVE LAST AND
strSQL = Left$(strSQL, Len(strSQL) - 4)
Set oRs2 = moConn.Execute( strSQL)

Do While Not oRs2.EOF
ProcessInnerGro up oRs2
oRs2.MoveNext
Loop

oRs1.MoveNext
i = i + 1
Loop

Thanks for any pointers.
Mark

Oct 17 '06
52 6342
MP
Thanks for staying with this David.
"David W. Fenton" <XX*******@dfen ton.com.invalid wrote in message
news:Xn******** *************** ***********@127 .0.0.1...
Without knowing what it is you want to do with each group, it's
impossible to say.
see my response to Lyle *if* you want to see a detailed explanation (which
probably no one wants to see <g>)
:-)
No, you just want the fields that identify the unique group. You'd
then use those values to run another SQL statement. Right now you're
opening a second recordset and handing it off to a subroutine.
ah! now I get what you're saying....I thought I *was* running another sql
statement with the line
set oRs2 = moConn.Execute( "Select * From TableName Where Field0 =
oRsDistinct .Field0.Value And Field1 = oRsDistinct .Field1.Value And Field2
= oRsDistinct .Field2.Value")

You're distinguishing between running an sql statement that doesn't return a
recordset versus returning a recordset and setting the values there

I don't know enough (obviously) about databases to see the difference
between the two methods.
I don't know why there is an apparent implication (based on comments by you
and lyle) that getting a recordset is a bad thing to do...i didn't know
that.
don't know what's in the subroutine, so I can't say if it's doable
with a SQL update or if you really do need to walk the recordset to
perform the calcuations and update the records.
as i say, see other post for detail...I think it's trickier than a simple
sql update but not sure...

It does seem like you have a denormalized structure because there's
way too much duplicate data. The rows in your DISTINCT query may
belong in another table, and the records in your current table would
then be linked as child records of the parent table.
you are absolutely correct there. Since this is just a quickie db written
on the fly for this one time calculation (and then disposed of) I didn't
take time to create multiple tables and relationships.. . it is a one table
db...entirely flat...bad database design I know...just didn't think it was
worth normalizing but maybe that's what's causeing me troubles.
I'm not sure how dividing into multiple tables would help with the sql
statement though, since I'm just beginning to learn this and at this point
am as dumb as a box of rocks...unfortu nately <g>
Maybe I need to look at that issue...
If you care to look at my pseudocode problem domain in the other post and
think normalizing would ease a solution I'd be happy to hear about it.

Thanks again for your insights
I appreciate you taking time to look at this
Mark
Oct 18 '06 #11
"MP" <no****@Thanks. comwrote in
news:YI******** ***********@tor nado.rdc-kc.rr.com:
"David W. Fenton" <XX*******@dfen ton.com.invalid wrote in message
news:Xn******** *************** ***********@127 .0.0.1...
[]
>No, you just want the fields that identify the unique group.
You'd then use those values to run another SQL statement. Right
now you're opening a second recordset and handing it off to a
subroutine.

ah! now I get what you're saying....I thought I *was* running
another sql statement with the line
set oRs2 = moConn.Execute( "Select * From TableName Where Field0 =
oRsDistinct .Field0.Value And Field1 = oRsDistinct .Field1.Value
And Field2
= oRsDistinct .Field2.Value")

You're distinguishing between running an sql statement that
doesn't return a recordset versus returning a recordset and
setting the values there
A SQL UPDATE operates on the data and changes the records, where a
SQL SELECT just returns data. A SQL UPDATE (or APPEND or DELETE)
does not return records because it isn't a SELECT -- it's an
operation that changes the records in a table.

A recordset can opened only on a SELECT statement. Once you've
returned the records, then you have to walk through them one by one
to make changes record by record (see below).
I don't know enough (obviously) about databases to see the
difference
between the two methods.
I don't know why there is an apparent implication (based on
comments by you and lyle) that getting a recordset is a bad thing
to do...i didn't know that.
Because updates to a recordset are done sequentially, by stepping
through the records in the recordset.

A SQL UPDATE operates on a set of records and is much, much faster
than the equivalent operation of walking through all the records and
changing them one by one.

This is a basic mistake that many people with more programming than
database experience often make. They are accustomed to thinking
sequentially and don't understand the set operations aroung which
all of SQL is built.

[]
>It does seem like you have a denormalized structure because
there's way too much duplicate data. The rows in your DISTINCT
query may belong in another table, and the records in your
current table would then be linked as child records of the parent
table.

you are absolutely correct there. Since this is just a quickie db
written on the fly for this one time calculation (and then
disposed of) I didn't take time to create multiple tables and
relationships.. . it is a one table db...entirely flat...bad
database design I know...just didn't think it was worth
normalizing but maybe that's what's causeing me troubles. I'm not
sure how dividing into multiple tables would help with the sql
statement though, since I'm just beginning to learn this and at
this point am as dumb as a box of rocks...unfortu nately <g>
No, just inexperienced.
Maybe I need to look at that issue...
If you care to look at my pseudocode problem domain in the other
post and think normalizing would ease a solution I'd be happy to
hear about it.
My eyes glazed over and I skimmed through it without reaching a
state of enlightenment. :)

--
David W. Fenton http://www.dfenton.com/
usenet at dfenton dot com http://www.dfenton.com/DFA/
Oct 19 '06 #12
MP

"David W. Fenton" <XX*******@dfen ton.com.invalid wrote in message
news:Xn******** *************** ***********@127 .0.0.1...
My eyes glazed over and I skimmed through it without reaching a
state of enlightenment. :)
:-)
Now we're finally at the real root of the problem!<g>
I cant' figure out a way to describe the "6 level deep decision tree" i have
without giving the whole ugly enchilada!

questioner: Can I do this with sql?
answerer: What are you trying to do?
questioner: description of problem
answerer: My eyes just glazed over.
:-)

let me try this
table1
fields(3) fldSide fldLength fldNumber
3 records
Right 12
Left 12
Left 16
so i have two pieces of data on each record and have to calculate the third
piece and enter it

Set rsLength = Select Distinct fldLength gives (12 16)
While not rsLength.eof
sql update where fldLength = rsLength.fldLen gth
if fldSide = Left then
fldNumber = i
elseif fldSide = Right then
fldNumber = i & "x"
End if
i = i +1
Wend

is there a way to do that without the distinct length set ?

the result should look like
fields(3) fldSide fldLength fldNumber
Right 12 1x
Left 12 1
Left 16 2

would you like some coffee to go with those glazed donuts er... eyes.
:-)

thanks for any ideas
Mark
Oct 19 '06 #13
"MP" <no****@Thanks. comwrote in
news:%Q******** ***********@tor nado.rdc-kc.rr.com:
table1
fields(3) fldSide fldLength fldNumber
3 records
Right 12
Left 12
Left 16
so i have two pieces of data on each record and have to calculate
the third piece and enter it

Set rsLength = Select Distinct fldLength gives (12 16)
While not rsLength.eof
sql update where fldLength = rsLength.fldLen gth
if fldSide = Left then
fldNumber = i
elseif fldSide = Right then
fldNumber = i & "x"
End if
i = i +1
Wend
OK. You'll need a UDF to return the counter. That can take any field
as an argument and will discard the value. It would be something
like this:

Function ReturnCounter(v arInput As Variant, _
Optional bolInitialize As Boolean = False) As Long
Static lngCounter As Long

If bolInitialize Then lngCounter = 0
lngCounter = lngCounter + 1

ReturnCounter = lngCounter
End Function

Before running your query, you'd initialize your counter by calling:

ReturnCounter(N ull, True)

Then for each row of the query, you'd use:

ReturnCounter([somefield])

to get the new counter value.

The query would be something like this:

UPDATE table1
SET table1.fldNumbe r = ReturnCounter(f ldNumber)
& IIf([fldSide] = "Right", "x", Null)
WHERE table1.fldLengt h = rsLength!fldLen gth

If that doesn't make sense, ask and I'll explain.

--
David W. Fenton http://www.dfenton.com/
usenet at dfenton dot com http://www.dfenton.com/DFA/
Oct 19 '06 #14
MP

"David W. Fenton" <XX*******@dfen ton.com.invalid wrote in message
news:Xn******** *************** ***********@127 .0.0.1...
"MP" <no****@Thanks. comwrote in
news:%Q******** ***********@tor nado.rdc-kc.rr.com:
table1
fields(3) fldSide fldLength fldNumber
3 records
Right 12
Left 12
Left 16
so i have two pieces of data on each record and have to calculate
the third piece and enter it

Set rsLength = Select Distinct fldLength gives (12 16)
While not rsLength.eof
sql update where fldLength = rsLength.fldLen gth
if fldSide = Left then
fldNumber = i
elseif fldSide = Right then
fldNumber = i & "x"
End if
i = i +1
Wend

OK. You'll need a UDF to return the counter. That can take any field
as an argument and will discard the value. It would be something
like this:

Function ReturnCounter(v arInput As Variant, _
Optional bolInitialize As Boolean = False) As Long
Static lngCounter As Long

If bolInitialize Then lngCounter = 0
lngCounter = lngCounter + 1

ReturnCounter = lngCounter
End Function

Before running your query, you'd initialize your counter by calling:

ReturnCounter(N ull, True)

Then for each row of the query, you'd use:

ReturnCounter([somefield])

to get the new counter value.

The query would be something like this:

UPDATE table1
SET table1.fldNumbe r = ReturnCounter(f ldNumber)
& IIf([fldSide] = "Right", "x", Null)
WHERE table1.fldLengt h = rsLength!fldLen gth

If that doesn't make sense, ask and I'll explain.

--
Thank you, I'll chew on that for a while
The only thing I don't understand off the top is why pass the field into the
function since it's not used?
why not
Function ReturnCounter(O ptional bolInitialize As Boolean = False) As Long
Static lngCounter As Long
If bolInitialize Then lngCounter = 0
lngCounter = lngCounter + 1
ReturnCounter = lngCounter
End Function

Then to init
ReturnCounter(T rue)
and in query
SET table1.fldNumbe r = ReturnCounter() etc
???

Thanks again
Mark


Oct 19 '06 #15
"MP" <no****@Thanks. comwrote in
news:zo******** **********@torn ado.rdc-kc.rr.com:
>
"David W. Fenton" <XX*******@dfen ton.com.invalid wrote in message
news:Xn******** *************** ***********@127 .0.0.1...
>"MP" <no****@Thanks. comwrote in
news:%Q******* ************@to rnado.rdc-kc.rr.com:
table1
fields(3) fldSide fldLength fldNumber
3 records
Right 12
Left 12
Left 16
so i have two pieces of data on each record and have to
calculate the third piece and enter it

Set rsLength = Select Distinct fldLength gives (12 16)
While not rsLength.eof
sql update where fldLength = rsLength.fldLen gth
if fldSide = Left then
fldNumber = i
elseif fldSide = Right then
fldNumber = i & "x"
End if
i = i +1
Wend

OK. You'll need a UDF to return the counter. That can take any
field as an argument and will discard the value. It would be
something like this:

Function ReturnCounter(v arInput As Variant, _
Optional bolInitialize As Boolean = False) As Long
Static lngCounter As Long

If bolInitialize Then lngCounter = 0
lngCounter = lngCounter + 1

ReturnCounter = lngCounter
End Function

Before running your query, you'd initialize your counter by
calling:

ReturnCounter(N ull, True)

Then for each row of the query, you'd use:

ReturnCounter([somefield])

to get the new counter value.

The query would be something like this:

UPDATE table1
SET table1.fldNumbe r = ReturnCounter(f ldNumber)
& IIf([fldSide] = "Right", "x", Null)
WHERE table1.fldLengt h = rsLength!fldLen gth

If that doesn't make sense, ask and I'll explain.

--

Thank you, I'll chew on that for a while
The only thing I don't understand off the top is why pass the
field into the function since it's not used?
Because if you don't have a different value passed to it for each
row, the expression will be evaluated only once, and thus return the
same value for every row. By passing the value, you're telling the
Access Expression Service "Hey! The value returned can change with
each row, so you can't just check it once!"

--
David W. Fenton http://www.dfenton.com/
usenet at dfenton dot com http://www.dfenton.com/DFA/
Oct 20 '06 #16
MP

"David W. Fenton" <XX*******@dfen ton.com.invalid wrote in message
news:Xn******** *************** ***********@127 .0.0.1...
<snip>
Function ReturnCounter(v arInput As Variant, _
Optional bolInitialize As Boolean = False) As Long
Static lngCounter As Long

If bolInitialize Then lngCounter = 0
lngCounter = lngCounter + 1

ReturnCounter = lngCounter
End Function
<snip>
Because if you don't have a different value passed to it for each
row, the expression will be evaluated only once, and thus return the
same value for every row. By passing the value, you're telling the
Access Expression Service "Hey! The value returned can change with
each row, so you can't just check it once!"

--
David W. Fenton http://www.dfenton.com/
usenet at dfenton dot com http://www.dfenton.com/DFA/
ok that's interesting
is a udf passed to a jet db different in that way from a standard vb
function?
I'm not aware of AccessExpressio nService I'll have to do some googleing to
catch up I see.
Thanks for the info
Is ReturnCounter a "defined function name" to Access that it knows that when
the value of the field passed into it changes then it reacts?

I was thinking it was just a 'user defined function with an arbitrary
name...but then it wouldnt' have that magic ability to react when it's first
argument changed....
otherwise I thought it would increment each time it's called and I would
only call it when i needed the next number and reset it for each group.
If bolInitialize Then lngCounter = 0
lngCounter = lngCounter + 1
if that were a regular vb function that would increment each time it's
called with a false initialize argument wouldn't it?

Thanks again for your help on this
I think I'm starting to 'get' some things about how you can use sql more
efficiently

Mark
Oct 21 '06 #17
MP

"Lyle Fairfield" <ly***********@ aim.comwrote in message
news:11******** ************@i3 g2000cwc.google groups.com...
This updates a new (created separately) field that identifies each
order in the Northwind Orders Table in a manner similar (I'm guessing)
to your description of what you want to do. Note, that it does not
bother with identifying unique values first. Perhaps you can find some
pointers in it. I do not know if this is how I might do the job in my
own work. I doubt that this code is exemplary and I expect a few lines
may be redundant. Regardless, it works (here in Canada).

Sub blah()
Thanks I'll work with that and see what I can use.

Thanks
Mark
Oct 21 '06 #18
"MP" <No****@Thanks. comwrote in
news:45******** *************** @news.nationwid e.net:
"David W. Fenton" <XX*******@dfen ton.com.invalid wrote in message
news:Xn******** *************** ***********@127 .0.0.1...
<snip>
> Function ReturnCounter(v arInput As Variant, _
Optional bolInitialize As Boolean = False) As Long
Static lngCounter As Long

If bolInitialize Then lngCounter = 0
lngCounter = lngCounter + 1

ReturnCounter = lngCounter
End Function
<snip>
>Because if you don't have a different value passed to it for each
row, the expression will be evaluated only once, and thus return
the same value for every row. By passing the value, you're
telling the Access Expression Service "Hey! The value returned
can change with each row, so you can't just check it once!"

ok that's interesting
is a udf passed to a jet db different in that way from a standard
vb function?
No, there's no difference. Any function you invoke in a row of a
query that takes no parameter from that row will be evaluated only
once for the query as a whole, not for each row of the query.
I'm not aware of AccessExpressio nService I'll have to do some
googleing to catch up I see.
Queries are Jet objects, while a VBA function or a UDF are VBA
commands. Jet can't evaluate those functions by itself (that's why
an ODBC connection to an Access query using UDFs cannot be run; try
it with a Word Merge using and ODBC connection to an Access query
with one or more UDFs), so it utilizes the Access Expression Service
to handle them.
Is ReturnCounter a "defined function name" to Access that it knows
that when the value of the field passed into it changes then it
reacts?
The Access Expression Service does not examine the inner workings of
a function. It only asks the question: How many times do I need to
evaluate this function? If it's called in every row, but there the
functions have no arguments that draw values from each row (i.e.,
the function output is not dependent on any values in each of the
rows returned), then the function can be evaulated once. If the
function takes one or more arguments from each row, it has to be
re-evaluated for each row of the query.
I was thinking it was just a 'user defined function with an
arbitrary name...but then it wouldnt' have that magic ability to
react when it's first argument changed....
There's nothing magic about the name. The "magic" is that the
Expression Service can tell from the structure of the function call,
because it takes a row-based argument, that it needs to re-evaluate
the function's return value for every row returned by the query.
otherwise I thought it would increment each time it's called and I
would only call it when i needed the next number and reset it for
each group.
Try it. You'll see.

--
David W. Fenton http://www.dfenton.com/
usenet at dfenton dot com http://www.dfenton.com/DFA/
Oct 21 '06 #19
MP

"David W. Fenton" <XX*******@dfen ton.com.invalid wrote in message
news:Xn******** *************** **********@127. 0.0.1...
SNIP
>
There's nothing magic about the name. The "magic" is that the
Expression Service can tell from the structure of the function call,
because it takes a row-based argument, that it needs to re-evaluate
the function's return value for every row returned by the query.
Got it. That's the magic...the Expression Service(which I had never heard
of till now)
Thanks for the clear explanation
Try it. You'll see.
I'm playing with the update query now and trying to iron out all my errors
:-)
Oct 21 '06 #20

This thread has been closed and replies have been disabled. Please start a new discussion.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.