views:

71

answers:

3

OK I am going to try to explain this the best I can and maybe someone will understand it. I have a CRM application I am building and have the following tables: contacts, email, phone, website and address. I have been trying to create a Query that gathers all the info into one result set. I have kind of found a way that works 99.9% but I think I am missing something for that 1% to work and have killed myself trying to find it.

My query looks like this:

SELECT 
contacts.full_name,
contacts.title,
contacts.company,
contacts.background,
GROUP_CONCAT( email.email_type ORDER BY  email.email_type)as email_type,
GROUP_CONCAT( email.email ORDER BY  email.email_type)as email,
GROUP_CONCAT( phone.phone_type ORDER BY phone.phone_type)as phone_type,
GROUP_CONCAT( phone.phone ORDER BY phone.phone_type)as phone,
GROUP_CONCAT( website.website_type ORDER BY website.website_type)as website_type,
GROUP_CONCAT( website.website ORDER BY website.website_type)as website,
GROUP_CONCAT( address.type ORDER BY  address.type ) as address_type,
GROUP_CONCAT( address.address_street ORDER BY  address.type ) as street,
GROUP_CONCAT( address.address_city ORDER BY  address.type ) as city,
GROUP_CONCAT( address.address_state ORDER BY  address.type ) as state,
GROUP_CONCAT( address.address_zip ORDER BY  address.type ) as zip,
GROUP_CONCAT( address.address_country ORDER BY  address.type) as country
FROM
contacts
Left Join email ON contacts.id = email.contact_id
Left Join phone ON contacts.id = phone.contact_id
Left Join website ON contacts.id = website.contact_id
Left Join address ON contacts.id = address.contact_id
GROUP BY
contacts.id
ORDER BY
contacts.id ASC

Now like i said it works like 99.9% of the way I want it to but here is the result set: (now this is a simulated result object but it follows what currently is spit out after the query.)

stdClass Object
(
    [full_name] => John Mueller
    [title] => President
    [company] => Mueller Co.
    [background] => This is the contacts background info.
    [email_type] => 1,1,1,1
    [email] => [email protected],[email protected],[email protected],[email protected]
    [phone_type] => 1,2,3,4
    [phone] => (123) 555-1212,(123) 555-1213,(123) 555-1214,(123) 555-1215
    [website_type] => 1,1,1,1
    [website] => www.mc.com,www.mc.com,www.mc.com,www.mc.com
    [address_type] => 1,1,1,1
    [street] => {address_1},{address_1},{address_1},{address_1}
    [city] => {city_1},{city_1},{city_1},{city_1}
    [state] => {state_1},{state_1},{state_1},{state_1}
    [zip] => {zip_1},{zip_1},{zip_1},{zip_1}
    [country] => 
)

Now as you can see the result acts like I want it to except for when on one the items has multiple valid items, i.e. in this case John has 4 types of phone numbers and this causes the other records in the DB to multiply accordingly. So in this case you get 4 of the same item in all the other supporting tables.

I have tried everything I can and maybe it can't be done but I thought I would try one more time and see if anyone would look at it and say oh yea you're missing 5 letters that will make it work or something. At this point even a "your stupid that won't work" woud be great too.

Thanks again for any help anyone can offer!

UPDATE:

I feel Like such a noob now, I pulled a classic mistake: i checked my result without full verification, but at the same time by the information i provided it was not clear. I'll explain my original solution worked except that when i had 3 work (type=1) numbers i would end up with a result like phone_type => 1 and phone => 555-1212,555-1213,555-1214 this is fine but when i have 2 work and 1 home the type identifiers were useless, but i never said that you can have more than one of any type for a contact so actually both of my answer below are correct, with that said the sql was a bit malformed but i knew what he was saying so it actually worked better and correct on the nose. BTW just pulling distint from the type fields wouldn't do it either ... I tried that.

Patial new query solution:

SELECT

contacts.full_name,
contacts.title,
contacts.company,
contacts.background,

inner_phone.phone,
inner_phone_type.phone_type

FROM
contacts

left Join (SELECT phone.contact_id, GROUP_CONCAT(phone.phone ORDER BY phone.phone_type) as phone FROM phone GROUP BY phone.contact_id ) inner_phone ON contacts.id = inner_phone.contact_id
left Join (SELECT phone.contact_id, GROUP_CONCAT(phone.phone_type ORDER BY phone.phone_type) as phone_type FROM phone GROUP BY phone.contact_id ) inner_phone_type ON contacts.id = inner_phone_type.contact_id

ORDER BY
contacts.id ASC

Thank you for your answers, and as a side note I just canceled that overpriced Experts Exchange this is so much easier to use and find what you are looking for and best of all free ;) - thanks again.

A: 

Wouldn't it be easier to get the 'base' information to the application first (the fields that do not repeat) and then use separate queries for the repeating information using sub-forms within the main form?

Or, as a compromise, create a view that displays up to N number of repeating fields.

Nitrodist
Well that is what I am currently doing but I thought that if I got it into a single result set it would lessen the stress on the server if this became a massive DB, I guess that is part two of my question: If it is just better to keep it the way I have it or would I see a performance increase doing something like this? Thanks for your answer!
BrandonS
+1  A: 

Throw DISTINCT in there.

GROUP_CONCAT(DISTINCT email.email_type ORDER BY email.email_type)as email_type,

Ref

Dan Williams
well I was off by 3 letters DISTINCT is 8 dude i love you man works perfectly! awesome!
BrandonS
i'm sorry not your fault you did not know i had multiple types for each record a better explanation is above. i wish i could split the credit but his did exactly what i needed sorry again. but your answer was correct.
BrandonS
Actually I like the sub-select better too (jdacobson). I'd guess the optimizer is going to do a better job with a sub-query then throwing a DISTINCT in all those SELECT items. Very happy to see a EE convert :)
Dan Williams
+1  A: 

The problem is, as soon as you join on PHONE (in your example), the query returns four records. Using GROUP_CONCAT in the top-level SELECT list isn't going to fix that. First solution that comes to mind is to use inner queries to guarantee you'll only return one record for a given contact.

SELECT 
contacts.full_name,
[...]
inner_phone.phones
FROM contacts 
LEFT JOIN (SELECT GROUP_CONCAT(phone ORDER BY phone_type) AS phones, contact_id FROM phone) inner_phone ON contacts.ID = inner_phone.contact_id
[... etc. for other tables ...]

That should put you on the right track, unless MySQL has vastly different subquery behavior from MSSQL/Oracle...

djacobson
thanks man you were right worked perfectly after i actually sat and listened to what you said and started checking the results against my master record. Thanks for the solution again.
BrandonS