views:

2266

answers:

9

I think this should be easy, but it's evading me. I've got a many-to-many relationship between Accounts and Account Groups. An Account can be in zero or more Groups, so I'm using the standard join table.

Accounts 
--------
ID
BankName
AcctNumber
Balance

AccountGroups
-------------
ID
GroupName

JoinAccountsGroups
------------------
AID
GID

I'm using MS Access, FWIW. Also, this is for a low-bandwidth situation, so code optimization isn't as important as simplicity/readability.

I'm using php as a presentation layer, so a bare-bones result from Access is fine.

As for what to do with the multi-result situation, I actually have two things I'm trying to build. The first lists all the groups in one column thus:

Bank       AcctNum       Balance    Groups
--------|--------------|----------|----------------
Citi       930938        400        Payroll
HSBC       8372933       100        Monthly, Payroll
Wells      09837         800        -
Chase      8730923       250        Monthly

The second is a master-detail list:

Name          AcctNum    Balance
------------|----------|----------
Payroll (2)              500
  Citi        930938     400
  HSBC        8372933    100         
..................................
Monthly (2)              350
  HSBC        8372933    100         
  Chase       8730923    250
..................................
Wells         09837      800

For the master-detail, my plan was just to get a big result set from the db, and munge it in php as needed. Since there's going to be some significant post-processing in php anyway, maybe I should just do three separate queries and do the joining there. (Since I'm more comfortable with that language.)

A: 
SELECT act.acctid AS AcctId, bankName, acctNumber, Balance, 
       NVL(jag.gid, '-') AS GroupID, NVL(gp.groupname, '-') AS GroupName
FROM accounts act 
     LEFT OUTER JOIN JointAccountGroups jag ON (act.id = jag.aid) 
     LEFT OUTER JOIN AccounGroups gp ON (jag.gid = gp.id)

NVL is a function that means "if first argument is null, return the second argument; otherwise, return first argument". NVL happens to be how Oracle do it -- all DBs have something like that, but it doesn't have a standard name; look up how Access does it.

SquareCog
Or just leave the null where it is, and display it as '-' in the presentation layer.
bobince
Naturally :-). I don't know if he *has* a presentation layer, so I threw that in for completeness. But if one can achieve separation of presentation and data, one should, you are correct.
SquareCog
NVL could be the similar to COALESCE in T-SQL, I'm not an Oracle guy.
tyndall
In Access its Nz() but I'm not sure thats available via the JET driver
AnthonyWJones
No, NZ is not available to Jet, use IIF(jag.gid IS NULL, '-', jag.gid)
onedaywhen
A: 

SQL Server uses ISNULL() for that purpose. I'm not sure whether that works in Access.

Dave Nichol
Nz() (i.e "null to zero", though the second argument can be any data type).
David-W-Fenton
Oops. Yes, as Anothony Jones points out below, Nz() is not available via ODBC or OLEDB, only within Access.
David-W-Fenton
Yes, it is easy to confuse features of Access with features of Jet, isn't it? :)
onedaywhen
A: 

Yeah, I'll use the presentation layer for the NULL.

But I must be missing something. I'm getting the same error from yours as from my original attempts:

Syntax error (missing operator) in query expression '(act.id = jag.aid) 
                  LEFT OUTER JOIN accountgroups gp ON (jag.gid = gp.id)'
sprugman
Maybe there is a syntax limitation on the number of "join" phrases in Access? Have you tried just forcing a join using the Where clause? (where act.id=jag.aid)? This only gives you an inner join, you'd have to join the results back to accounts and get the missing ones..
SquareCog
Please edit your question with addition details, do not add details using an answer. Note also your output spec still needs revising.
AnthonyWJones
The Jet database engine optimizes implicit joins (using WHERE clauses) exactly the same as the equivalent JOIN, so there's no advantage to replacing an explicit join wint an implicit join. I've never in all my years of using Access bumped up against any limit on the number of joins.
David-W-Fenton
In Jet, join a one-row table to itself 33 times and you'll get a "Query is too complex" error. The character count for the SQL is only K1.5 characters and the resultset would be just one row, so the number of joins must be the limiting factor.
onedaywhen
For Jet you must put each join in parens e.g. (LEFT OUTER JOIN JointAccountGroups jag ON act.id = jag.aid) LEFT OUTER JOIN AccounGroups gp ON jag.gid = gp.id
onedaywhen
Oops, I meant FROM (accounts AS act LEFT OUTER JOIN JointAccountGroups AS jag ON act.id = jag.aid) LEFT OUTER JOIN AccounGroups AS gp ON jag.gid = gp.id
onedaywhen
A: 

How about:

SELECT act.acctid AS AcctId, bankName, acctNumber, Balance, 
      jag.gid AS GroupID, gp.groupname AS GroupName
FROM accounts AS act 
     LEFT OUTER JOIN JointAccountGroups AS jag ON act.id = jag.aid
     LEFT OUTER JOIN AccounGroups AS gp ON jag.gid = gp.id

Does this give you an error? An error that is easier to figure out perhaps?

tyndall
That's the same as OR without the NVL, and with a few more AS's thrown in, right? That's actually what I put in....
sprugman
A: 

I think in Access you may want to try something like:

FROM (accounts AS act 
     LEFT OUTER JOIN JointAccountGroups AS jag ON act.id = jag.aid)
     LEFT OUTER JOIN AccounGroups AS gp ON jag.gid = gp.id

I don't know why the parenthesis matter, but I tried a test with that and it seemed to fix it.

Tina Orooji
A: 

This

SELECT a.BankName, a.AcctNumber, a.Balance, ag.GroupName
FROM (Accounts a 
      LEFT JOIN JoinAccountsGroups jag 
      ON a.ID = jag.AID) 
      LEFT JOIN AccountGroups ag
      ON jag.GID = ag.GroupName;

Will select the data for the first table, however to concatenate the groups (Monthly, Payroll), you would need a User Defined Function (UDF), wich would not be available to Jet, so processing in PHP would be necessary.

You may wish to read Understanding SQL Joins. It refers to MySQL but applies to Jet, for the most part.

Remou
A: 

Another Try:

SELECT act.acctid AS AcctId, bankName, acctNumber, Balance, 
    jag.gid AS GroupID, gp.groupname AS GroupName
FROM accounts AS act 
   LEFT OUTER JOIN JointAccountGroups AS jag ON act.id = jag.aid
   LEFT INNER JOIN AccounGroups AS gp ON jag.gid = gp.id

Access might be having trouble with the two OUTER JOINS so I made the second one an INNER JOIN which should work

tyndall
+1  A: 

Another thought... why not use the Query Designer in Access. This should take about 30 seconds to design the "View". Then go look at the SQL it wrote.

tyndall
cuz I don't really know how to use it, so my brief attempts at that have failed. :)
sprugman
The query designer can be very useful, but will not set up the correct joins for this query, you would need to know the join type. The query design window also produces buggy sql for more complex queries.
Remou
A: 

Not only use the query designer as suggested earlier, but also use the MS Access relationship tool to record the relationship between the two foreign keys (AID, GID) and the primary keys they reference.

This makes doing natural joins in the query designer almost child's play. You can even use a query wizard in the situation you outlined.

Once you have the query built, why not use the query as a record source instead of using the tables?

The one thing I would do in PHP would be converting the multiple results into a comma separated list.

Walter Mitty