Home All Groups Group Topic Archive Search About
Author
2 Apr 2006 5:45 PM
Chris
Hi all

I have two simple tables:

tClients
  lID  // links to tAddress .lClientID
  FName
  LName

tAddress
  lID
  lClientID  // links to tClients.lID
  Address01
  Address02
  Address03
  etc, etc

Anyhow, I am trying to generate a results set that shows my all client
details (SELECT * FROM tClients) BUT... with an additional column tacked onto
the end that returns the number of addresses (in tAddress) for each client.
Something like this:

lID    FName    LName    NumAddresses
01    Chris    Hill    3
02    Alex    Wells    0
etc

I hope this makes sense.

Currently, I am doing:

SELECT * FROM tClients
populating a datatable, and then having to loop through each row and pass
the lID to a second stored proc to determine the number of addresses. In
short, it's pretty long winded and v. inefficient.

Any pointers/help would be massively appreciated

Author
2 Apr 2006 5:59 PM
Colin Dawson
hmmm,  how about

With AddressCount (
  Select
    lClientID,
   Count(*) NumAddresses
  From tAddress
  Group By lClientID
)
Select
  c.IID,
  c.FName,
  c.LName,
  ac.NumAddresses
From tClients c
Left Join AddressCount ac on ac.lClientID =  c.IID

Or for SQL 2000

Select
  c.IID,
  c.FName,
  c.LName,
  ac.NumAddresses
From tClients c
Left Join (
  Select
    lClientID,
   Count(*) NumAddresses
  From tAddress
  Group By lClientID ) ac on ac.lClientID =  c.IID

Colin.
Author
2 Apr 2006 9:20 PM
Chris
Hi Colin

Fantastic !!!

Thanks a million; works like a d-ream :)

I am using SQL Server 2000

Just one further query, if I may:

Can I wrap this up to say, only perform this for a client with a particular
ID.

I.e. in English "Show me the clients ID, FName, LName and NumAddress WHERE
the clientID is [blah]

Cheers
Chris

Show quote
"Colin Dawson" wrote:

> hmmm,  how about
>
> With AddressCount (
>   Select
>     lClientID,
>    Count(*) NumAddresses
>   From tAddress
>   Group By lClientID
> )
> Select
>   c.IID,
>   c.FName,
>   c.LName,
>   ac.NumAddresses
> From tClients c
> Left Join AddressCount ac on ac.lClientID =  c.IID
>
> Or for SQL 2000
>
> Select
>   c.IID,
>   c.FName,
>   c.LName,
>   ac.NumAddresses
> From tClients c
> Left Join (
>   Select
>     lClientID,
>    Count(*) NumAddresses
>   From tAddress
>   Group By lClientID ) ac on ac.lClientID =  c.IID
>
> Colin.
>
>
>
Author
2 Apr 2006 10:14 PM
Erland Sommarskog
Chris (Ch***@discussions.microsoft.com) writes:
> Just one further query, if I may:
>
> Can I wrap this up to say, only perform this for a client with a
> particular ID.
>
> I.e. in English "Show me the clients ID, FName, LName and NumAddress WHERE
> the clientID is [blah]

It's as simple as:

>> Select
>>   c.IID,
>>   c.FName,
>>   c.LName,
>>   ac.NumAddresses
>> From tClients c
>> Left Join (
>>   Select
>>     lClientID,
>>    Count(*) NumAddresses
>>   From tAddress
>>   Group By lClientID ) ac on ac.lClientID =  c.IID

WHERE lClientID = @clientid

--
Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se

Books Online for SQL Server 2005 at
http://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books.mspx
Books Online for SQL Server 2000 at
http://www.microsoft.com/sql/prodinfo/previousversions/books.mspx
Author
2 Apr 2006 11:08 PM
Colin Dawson
Show quote
"Erland Sommarskog" <esq***@sommarskog.se> wrote in message
news:Xns979ACA2768DDYazorman@127.0.0.1...
> Chris (Ch***@discussions.microsoft.com) writes:
>> Just one further query, if I may:
>>
>> Can I wrap this up to say, only perform this for a client with a
>> particular ID.
>>
>> I.e. in English "Show me the clients ID, FName, LName and NumAddress
>> WHERE
>> the clientID is [blah]
>
> It's as simple as:
>
>>> Select
>>>   c.IID,
>>>   c.FName,
>>>   c.LName,
>>>   ac.NumAddresses
>>> From tClients c
>>> Left Join (
>>>   Select
>>>     lClientID,
>>>    Count(*) NumAddresses
>>>   From tAddress
>>>   Group By lClientID ) ac on ac.lClientID =  c.IID
>
> WHERE lClientID = @clientid
>
> --
> Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se
>
> Books Online for SQL Server 2005 at
> http://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books.mspx
> Books Online for SQL Server 2000 at
> http://www.microsoft.com/sql/prodinfo/previousversions/books.mspx

I'd just like to add one tiny detail, it's better to use alias names at all
times, unless there's no join operation.
Keeping with this I'd add the same where clause as Erland, but with one
modification to clarify what the code is actually doing,  it will help to
make maintenance easier as you won't need to guess which table the data
column is suppose to come from.  Here's the modified line   (add it to the
end of the SQL Statement)...

Where c.IID = @clientid

I'd prefer to use c.IID as opposed to lClientID, the reason for this is that
there is a left outer join in operation here.  There is a chance that the
client has no addresses stored.  In this case you will still need the result
line, but with the NumAddresses returning a 0.  If you place the where
clause into the statement so that lCientID is used for the filter, if there
are no addresses you'll get no result row at all!

Regards

Colin.
Author
3 Apr 2006 10:18 PM
Erland Sommarskog
Colin Dawson (newsgro***@cjdawson.com) writes:
> "Erland Sommarskog" <esq***@sommarskog.se> wrote in message
>> WHERE lClientID = @clientid
>
> I'd just like to add one tiny detail, it's better to use alias names at
> all times, unless there's no join operation.

Wholly agree. I was Posting too quickly, too close to bedtime, I gues.




--
Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se

Books Online for SQL Server 2005 at
http://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books.mspx
Books Online for SQL Server 2000 at
http://www.microsoft.com/sql/prodinfo/previousversions/books.mspx
Author
2 Apr 2006 10:46 PM
Hugo Kornelis
On Sun, 2 Apr 2006 10:45:02 -0700, Chris wrote:

Show quote
>Hi all
>
>I have two simple tables:
>
>tClients
>  lID  // links to tAddress .lClientID
>  FName
>  LName
>
>tAddress
>  lID
>  lClientID  // links to tClients.lID
>  Address01
>  Address02
>  Address03
>  etc, etc
>
>Anyhow, I am trying to generate a results set that shows my all client
>details (SELECT * FROM tClients) BUT... with an additional column tacked onto
>the end that returns the number of addresses (in tAddress) for each client.
>Something like this:
>
>lID    FName    LName    NumAddresses
>01    Chris    Hill    3
>02    Alex    Wells    0
>etc
>
>I hope this makes sense.

Hi Chris,

The best solution is to redesign the Address table. Your current design
is not normalised. Instead of having a large number of columns Address1,
Address2, ..., you should have a column AddressNo and a column Address,
like this:

CREATE TABLE Addresses
         (ClientID int NOT NULL,
          AddressNo tinyint NOT NULL,
          Address varchar(200) NOT NULL,
          PRIMARY KEY (ClientID, AddressNo),
          FOREIGN KEY (ClientID) REFERENCES Clients(ClientID)
             ON UPDATE CASCADE ON DELETE CASCADE
         )

And then, the query would simply be

SELECT ID, FirstName, LastName,
      (SELECT COUNT(*)
       FROM   Addresses
       WHERE  Addresses.ClientID = Clients.ClientID) AS NumAddresses
FROM   Clients


With your current design, you're stuck with an ugly kludge like

SELECT c.ID, c.FirstName, c.LastName,
       CASE WHEN a.Address1 IS NOT NULL THEN 1 ELSE 0 END
     + CASE WHEN a.Address2 IS NOT NULL THEN 1 ELSE 0 END
     + CASE WHEN a.Address3 IS NOT NULL THEN 1 ELSE 0 END
     + CASE WHEN a.Address4 IS NOT NULL THEN 1 ELSE 0 END
     + CASE WHEN a.Address5 IS NOT NULL THEN 1 ELSE 0 END
     + ......
     + CASE WHEN a.Address999 IS NOT NULL THEN 1 ELSE 0 END AS
NumAddresses
FROM   Clients AS c
LEFT OUTER JOIN Addresses AS a
ON     a.ClientID = c.ClientID


--
Hugo Kornelis, SQL Server MVP
Author
2 Apr 2006 11:14 PM
Colin Dawson
Show quote
"Hugo Kornelis" <h***@perFact.REMOVETHIS.info.INVALID> wrote in message
news:5qk0329s4lhqs3hpmtricbdtcalnvsrj1d@4ax.com...
> On Sun, 2 Apr 2006 10:45:02 -0700, Chris wrote:
>
>>Hi all
>>
>>I have two simple tables:
>>
>>tClients
>>  lID  // links to tAddress .lClientID
>>  FName
>>  LName
>>
>>tAddress
>>  lID
>>  lClientID  // links to tClients.lID
>>  Address01
>>  Address02
>>  Address03
>>  etc, etc
>>
>>Anyhow, I am trying to generate a results set that shows my all client
>>details (SELECT * FROM tClients) BUT... with an additional column tacked
>>onto
>>the end that returns the number of addresses (in tAddress) for each
>>client.
>>Something like this:
>>
>>lID    FName    LName    NumAddresses
>>01    Chris    Hill    3
>>02    Alex    Wells    0
>>etc
>>
>>I hope this makes sense.
>
> Hi Chris,
>
> The best solution is to redesign the Address table. Your current design
> is not normalised. Instead of having a large number of columns Address1,
> Address2, ..., you should have a column AddressNo and a column Address,
> like this:
>
> CREATE TABLE Addresses
>         (ClientID int NOT NULL,
>          AddressNo tinyint NOT NULL,
>          Address varchar(200) NOT NULL,
>          PRIMARY KEY (ClientID, AddressNo),
>          FOREIGN KEY (ClientID) REFERENCES Clients(ClientID)
>             ON UPDATE CASCADE ON DELETE CASCADE
>         )
>
> And then, the query would simply be
>
> SELECT ID, FirstName, LastName,
>      (SELECT COUNT(*)
>       FROM   Addresses
>       WHERE  Addresses.ClientID = Clients.ClientID) AS NumAddresses
> FROM   Clients
>
>
> With your current design, you're stuck with an ugly kludge like
>
> SELECT c.ID, c.FirstName, c.LastName,
>       CASE WHEN a.Address1 IS NOT NULL THEN 1 ELSE 0 END
>     + CASE WHEN a.Address2 IS NOT NULL THEN 1 ELSE 0 END
>     + CASE WHEN a.Address3 IS NOT NULL THEN 1 ELSE 0 END
>     + CASE WHEN a.Address4 IS NOT NULL THEN 1 ELSE 0 END
>     + CASE WHEN a.Address5 IS NOT NULL THEN 1 ELSE 0 END
>     + ......
>     + CASE WHEN a.Address999 IS NOT NULL THEN 1 ELSE 0 END AS
> NumAddresses
> FROM   Clients AS c
> LEFT OUTER JOIN Addresses AS a
> ON     a.ClientID = c.ClientID
>
>
> --
> Hugo Kornelis, SQL Server MVP

Hi Hugo.

I disagree with this. Whilst I agree that the OP's table design does not
lend itself to the easiest interpretation; I do think that you've
misinterpreded the table design. (very easily done as there's no DDL
included)

Chris can you confirm this?

If my understanding is correct the Address01, Address02, Address03 columns
are supposed to be columns which will contain eash line of the address, not
the entire address.

I've made this assumption based on the way that the original question was
asked.

Regards

Colin.
Author
3 Apr 2006 7:21 AM
Chris
Hi Guys

First off, thanks for all the help. Really Appreciated :)

As for my tAddress table, Colin is right :

Address01
Address02
Address03

Correspond to a line in one address, not a complete address.

It would of been better if I'd described the table like this:

tAddress
  lID
  cAddressLine1
  cAddressLine2
  cAddressLine3
  cTown
  cPostcode

Sorry for any confusion.

Is my table structure a poor one?

REgards

Chris



Show quote
"Colin Dawson" wrote:

>
> "Hugo Kornelis" <h***@perFact.REMOVETHIS.info.INVALID> wrote in message
> news:5qk0329s4lhqs3hpmtricbdtcalnvsrj1d@4ax.com...
> > On Sun, 2 Apr 2006 10:45:02 -0700, Chris wrote:
> >
> >>Hi all
> >>
> >>I have two simple tables:
> >>
> >>tClients
> >>  lID  // links to tAddress .lClientID
> >>  FName
> >>  LName
> >>
> >>tAddress
> >>  lID
> >>  lClientID  // links to tClients.lID
> >>  Address01
> >>  Address02
> >>  Address03
> >>  etc, etc
> >>
> >>Anyhow, I am trying to generate a results set that shows my all client
> >>details (SELECT * FROM tClients) BUT... with an additional column tacked
> >>onto
> >>the end that returns the number of addresses (in tAddress) for each
> >>client.
> >>Something like this:
> >>
> >>lID    FName    LName    NumAddresses
> >>01    Chris    Hill    3
> >>02    Alex    Wells    0
> >>etc
> >>
> >>I hope this makes sense.
> >
> > Hi Chris,
> >
> > The best solution is to redesign the Address table. Your current design
> > is not normalised. Instead of having a large number of columns Address1,
> > Address2, ..., you should have a column AddressNo and a column Address,
> > like this:
> >
> > CREATE TABLE Addresses
> >         (ClientID int NOT NULL,
> >          AddressNo tinyint NOT NULL,
> >          Address varchar(200) NOT NULL,
> >          PRIMARY KEY (ClientID, AddressNo),
> >          FOREIGN KEY (ClientID) REFERENCES Clients(ClientID)
> >             ON UPDATE CASCADE ON DELETE CASCADE
> >         )
> >
> > And then, the query would simply be
> >
> > SELECT ID, FirstName, LastName,
> >      (SELECT COUNT(*)
> >       FROM   Addresses
> >       WHERE  Addresses.ClientID = Clients.ClientID) AS NumAddresses
> > FROM   Clients
> >
> >
> > With your current design, you're stuck with an ugly kludge like
> >
> > SELECT c.ID, c.FirstName, c.LastName,
> >       CASE WHEN a.Address1 IS NOT NULL THEN 1 ELSE 0 END
> >     + CASE WHEN a.Address2 IS NOT NULL THEN 1 ELSE 0 END
> >     + CASE WHEN a.Address3 IS NOT NULL THEN 1 ELSE 0 END
> >     + CASE WHEN a.Address4 IS NOT NULL THEN 1 ELSE 0 END
> >     + CASE WHEN a.Address5 IS NOT NULL THEN 1 ELSE 0 END
> >     + ......
> >     + CASE WHEN a.Address999 IS NOT NULL THEN 1 ELSE 0 END AS
> > NumAddresses
> > FROM   Clients AS c
> > LEFT OUTER JOIN Addresses AS a
> > ON     a.ClientID = c.ClientID
> >
> >
> > --
> > Hugo Kornelis, SQL Server MVP
>
> Hi Hugo.
>
> I disagree with this. Whilst I agree that the OP's table design does not
> lend itself to the easiest interpretation; I do think that you've
> misinterpreded the table design. (very easily done as there's no DDL
> included)
>
> Chris can you confirm this?
>
> If my understanding is correct the Address01, Address02, Address03 columns
> are supposed to be columns which will contain eash line of the address, not
> the entire address.
>
> I've made this assumption based on the way that the original question was
> asked.
>
> Regards
>
> Colin.
>
>
>
Author
3 Apr 2006 8:39 AM
Colin Dawson
<snip>
Show quote
> It would of been better if I'd described the table like this:
>
> tAddress
>  lID
>  cAddressLine1
>  cAddressLine2
>  cAddressLine3
>  cTown
>  cPostcode
>
> Sorry for any confusion.
>
> Is my table structure a poor one?
>
> REgards
>
> Chris
>
</snip>

It's a good logical structure.  The problem was the confusion that it
caused.   There is one thing missing from the table stucture, but this
really depends on how you are using the table.   There's nothing to identify
what type of address your are referring to.  For example, I have a contact
manager database  this contains a virtual business card (a vCard if you
like)  Here's the table structure...

Contact  --> ContactAddress  --> Address
                                               --> AddressType

The AddressType table is a lookup list that can be placed in a
combo/listbox.  This helps to identify the type of address, i.e. if it's a
home/work or other address.  This table has an AddressTypeID column and a
description.

The Address table contains that actual address, just like your tAddress
table.  I'll use AddressID for the primary key. This table will contain a
distinct list of addresses.  These addresses can be used for any purpose.
The Contact table contains the contact - name, date of birth and a few other
details, and a ContactID.

The ContactAddress table is the key to my structure.  This table looks a
little like this....

Create Table ContactAddress(
  ContactID,
  AddressID,
  AddressTypeID

  Constraint PK_ContactAddress Primary Key Clustered( ContactID, AddressID )
)

The idea is that I can tie many addresses to a single contact.  If I have
several contacts at the same address there is still only one address row.
The Address type ID can be used in mailshots, to set the preference for the
destination of letters.

i.e. Type to send business stuff to the work address if not, then go to the
home address, and so on.

Regards

Colin.
Author
3 Apr 2006 1:52 PM
Chris
<snip>
It's a good logical structure.  The problem was the confusion that it
caused.   There is one thing missing from the table stucture, but this
really depends on how you are using the table.   There's nothing to identify
what type of address your are referring to.
</snip>

Yeah, sorry for not explaining it too well first time round, but I am glad
that it passes muster as a table structure : )
I see yr point about id'ing the type of address - I never thought of that. I
too, use this as part of a contacts db and get round the address for mail
merges by having another boolean column to indicate which address, from many,
is the preferred mailing address.

Regards
Chris

Show quote
"Colin Dawson" wrote:

> <snip>
> > It would of been better if I'd described the table like this:
> >
> > tAddress
> >  lID
> >  cAddressLine1
> >  cAddressLine2
> >  cAddressLine3
> >  cTown
> >  cPostcode
> >
> > Sorry for any confusion.
> >
> > Is my table structure a poor one?
> >
> > REgards
> >
> > Chris
> >
> </snip>
>
> It's a good logical structure.  The problem was the confusion that it
> caused.   There is one thing missing from the table stucture, but this
> really depends on how you are using the table.   There's nothing to identify
> what type of address your are referring to.  For example, I have a contact
> manager database  this contains a virtual business card (a vCard if you
> like)  Here's the table structure...
>
> Contact  --> ContactAddress  --> Address
>                                                --> AddressType
>
> The AddressType table is a lookup list that can be placed in a
> combo/listbox.  This helps to identify the type of address, i.e. if it's a
> home/work or other address.  This table has an AddressTypeID column and a
> description.
>
> The Address table contains that actual address, just like your tAddress
> table.  I'll use AddressID for the primary key. This table will contain a
> distinct list of addresses.  These addresses can be used for any purpose.
> The Contact table contains the contact - name, date of birth and a few other
> details, and a ContactID.
>
> The ContactAddress table is the key to my structure.  This table looks a
> little like this....
>
> Create Table ContactAddress(
>   ContactID,
>   AddressID,
>   AddressTypeID
>
>   Constraint PK_ContactAddress Primary Key Clustered( ContactID, AddressID )
> )
>
> The idea is that I can tie many addresses to a single contact.  If I have
> several contacts at the same address there is still only one address row.
> The Address type ID can be used in mailshots, to set the preference for the
> destination of letters.
>
> i.e. Type to send business stuff to the work address if not, then go to the
> home address, and so on.
>
> Regards
>
> Colin.
>
>
>

AddThis Social Bookmark Button