Join query with flattened result - sql-server-2008

I have the following entities
AddressType is simply an enum field that define if the Email is Personal/Work/Other.
Is it possible to do a query that returns a flattened result like the one in the following sample?
CustomerID Full Name Personal Email Work Email
----------- -------------- ----------------- -----------------------
1 John Doe johndoe#hotmail.com john.doe#company.com

select c.CustomerID,
c.FullName as [Full Name],
epersonal.AddressText as [Personal Email],
ework.AddressText as [Work Email]
from Customer c
left outer join Email epersonal on c.CustomerID = epersonal.CustomerID
and epersonal.AddressType = 'personal'
left outer join Email ework on c.CustomerID = ework.CustomerID
and epersonal.AddressType = 'work'

Two main choices:
1) Select it as typical (with two rows, one for each email), then use the pivot operator to flatten.
Example of pivot (I call it an example as I wrote it in notepad. It may be slightly wrong, but it should point you the right way):
select
CustomerID,
FullName
[1] as WorkEmail,
[2] as HomeEmail
from
(select
c.CustomerID, c.FullName, e.AddressText, e.AddressType
from
Customer c
join emails e on e.CustomerID = c.CustomerID) as Source
pivot (
AddressText
FOR AddressType in ([1], [2])
)
2) Join to the email table twice, once for each type of address. Suggest outer joins so if one is missing you still get the other.

Related

How to remove matches between unioned tables with joined tables

I have 2 Tables
emails and fields
emails is just a list of emails and their unique identifiers and fields is in fifth normal form (i think) with a column of values(that ID data in the second column),a data column, and ids that link to the uuids in the first table.
For example:
Table 1:
ID EMAIL
-- -----
1a2 Test#Test
2a3 email#email|
3a4 add#add
Table 2:
value Data ID
----- ------ ---
1 123 Main 1a2
2 John Smith 1a2
3 US 1a2
4 555-555-5555 1a2
In this case, table 2 only shows the data for "1a2" because they were the only one to fill them out.
So I'm trying to make a table, that couples email to the corresponding country codes AND also provides the emails that haven't registered country codes as a Null value
I've tried using UNION to display all the emails from Table 1 on Table 2 after joining table 1 and 2 on ID and then using where for the value but that shows all the emails that have the proper country code and then duplicates them in the UNIONed portion as well.
This is an example of what I'm getting:
EMAIL COUNTRY CODE
----- ------------
test#test US
test#test NULL
email#email NULL
add#add NULL
You'll notice that test#test is duplicated due to the union not having the proper filter
My code looks like:
select
e.email as "Email",
f.value as "Country Code"
from
email e
join
fields f
ON e.id = f.id
where
f.value = '3'
[[and f.data like concat({{CountryCode}},'%')]]
-- curly brackets are for user entered variables
Anyways, all said and done. I'm looking for a table that appears something like this with No Duplicates
EMAIL COUNTRY CODE
----- ------------
test#test US
email#email NULL
add#add NULL
You are storing your user metadata in key value format. One approach here is to left join the user table to a subquery which pivots to find the country:
SELECT
e.EMAIL,
f.country_code
FROM email e
LEFT JOIN
(
SELECT ID, MAX(CASE WHEN value = 3 THEN Data END) AS country_code
FROM fields
GROUP BY ID
) f
ON e.ID = f.ID;
Demo
You can try using max() aggregation and group by -
select
e.email as "Email",
max(f.value) as "Country Code"
from
email e
left join
fields f
ON e.id = f.id
where
f.value = '3'
[[and f.data like concat({{CountryCode}},'%')]]
group by e.email
You can make the field you're interested in part of your LEFT JOIN condition.
SELECT
e.EMAIL,
f.data as countryCode
FROM email e
LEFT JOIN fields f ON f.value=3 AND e.ID=f.ID
Users with a value for that field will return it. Users without will simply be NULL due to it being a LEFT join.
If you want to do a condition on the fields value, then you'll need to include a check for null as well.
SELECT
e.EMAIL,
f.data as countryCode
FROM email e
LEFT JOIN fields f ON f.value=3 AND e.ID=f.ID
WHERE
f.data IS NULL
OR f.data='US'
Well I feel pretty silly after posting this, but I found the solution and am going to post it in case someone else bumps into the issue.
left join
fields f
ON e.id = f.id
where
f.value = '3' OR f.value is null
[[and f.data like concat({{CountryCode}},'%')]]
I just needed to add an "OR" for null values. Sorry if I wasted anyone's time, I was clearly overcomplicating things and managed to achieve what I was trying to do without a union at all.

How to apply conditional join in EF Core?

Let's say I have a table Contacts, Customers, and Suppliers. Contacts has a column ContactTypeId and ExternalContactId. So you could end up with either:
var extContacts = from ct in _context.Contacts
join cu in _context.Customers
on ct.ExternalContactId equals cu.Id
Or I could have this as well
var extContacts = from ct in _context.Contacts
join su in _context.Suppliers
on ct.ExternalContactId equals su.Id
Here, I've showed only 2 joins to illustrate my point. In my case, I have 5 tables.
Is there any way, I can make just one call, join Contacts table depending on the ContactTypeId? Otherwise I may have to make more than 1 call (up to 5).
Something like:
var extContacts = from ct in _context.Contacts
join (
//if type is Customer --> Join with Customers table
//if type is supplier --> join with Suppliers table
)
select name, phone, address //fields common to both tables
Thanks for helping

SQL Genius need .. Complex MySQL query

I am trying to optimise my php by doing as much work on the MySQL server as possible. I have this sql query which is pulling data out of a leads table, but at the same time joining two tags tables to combine the result. I am looking to add a company which is linked through a relations table.
So the table that holds the relationship between the two is relations_value which simply states (I add example data)
parenttable (companies) | parentrecordid (10) | childtable (leads) | childrecordid (1)
the companies table has quite a few columns but the only two relevant are;
id (10) | companyname (my company name)
So this query currently grabs everything I need but I want to bring the companyname into the query:
SELECT leads.id,
GROUP_CONCAT(c.tag ORDER BY c.tag) AS tags,
leads.status,
leads.probability
FROM `gs_db_1002`.leads
LEFT JOIN ( SELECT *
FROM tags_module
WHERE tagid IN ( SELECT id
FROM tags
WHERE moduleid = 'leads' ) ) as b
ON leads.id = b.recordid
LEFT JOIN `gs_db_1002`.tags as c
ON b.tagid = c.id
GROUP BY leads.id,
leads.status,
leads.probability
I need to be able to go into the relations_values table and pull parenttable and parentrecordid by selecting childtable = leads and childrecordid = 1 and somehow join these so that I am able to get companyname as a column in the above query...
Is this possible?
I have created a sqlfiddle: sqlfiddle.com/#!2/023fa/2 So I am looking to add companies.companyname as column to the query.
I don't know what your primary keys and foreign keys are that link each table together.. if you could give a better understanding of what ID's are linked to eachother it would make this a lot easier... however i did something that does return the correct result... but since all of the ID's are = 1 then it could be incorrect.
SELECT
leads.id, GROUP_CONCAT(c.tag ORDER BY c.tag) AS tags,
leads.status, leads.probability, companyname
FROM leads
LEFT JOIN (
SELECT * FROM tags_module WHERE tagid IN (
SELECT id FROM tags WHERE moduleid = 'leads' )
) as b ON leads.id = b.recordid
LEFT JOIN tags as c ON b.tagid = c.id
LEFT JOIN relations_values rv on rv.id = b.recordid
LEFT JOIN companies c1 on c1.createdby = rv.parentrecordid
GROUP BY leads.id,leads.status, leads.probability

Database Merging Tables

I just want to ask how to merge two tables.
Because I have a table guest and company both of them acquire a reservation.
I want to join both of them and label them as customers and I want to add a new column customer type which will say guest, if my company is null... and company, if my guest is null...
Here's an example:
guest(g_id(PK), name, guest details etc....)
company(c_id(PK), name, company details etc...)
reservation(r_id(PK), g_id(fk), c_id(fk), reservation details etc...)
When I join them (assuming guest has acquired a reservation) my table looks like
reservation_id|company name|guest name|reservation details
1 null <name> <details>
2 null <name> <details>
What I want is to make it like this:
reservation_id|customer name|cust_type|reservation details
1 <name> <guest> <details>
2 <name> <company> <details>
One possible solution would be to first create a query for each type of reservation (which you might already have) and add the cust_type there, then union the two together. So the initial queries would be something like:
guest_reservations:
SELECT reservation.r_id, guest.name, "guest" AS cust_type, {other fields}
FROM guest, reservation INNER JOIN guest ON guest.g_id = reservation.g_id;
company_reservations:
SELECT reservation.r_id, company.name, "company" AS cust_type {other fields}
FROM company, reservation INNER JOIN company ON company.c_id = reservation.c_id;
Then you could union the two queries together, something like below.
SELECT reservation.r_id, guest.name, "guest" AS cust_type, {other fields}
FROM guest, reservation INNER JOIN guest ON guest.g_id = reservation.g_id
UNION SELECT reservation.r_id, company.name, "company" AS cust_type {other fields}
FROM company, reservation INNER JOIN company ON company.c_id = reservation.c_id;
Of course remember to either remove {other fields} or add in whatever else you need there.
Not sure the schema is the best but consider the following tables:
Res (resId, description, cid, gid, details)
1 Guest Res 1 NULL Just some guy
2 Comp Res NULL 1 Corporate jerk
Guest (gid, name)
1 Chuck
Company (cid, name)
1 Tenth
Check out the parameters of the select query:
1. r.rid : id of reservation table
2. isnull(c.name, g.name) : gets the non null value from either company or guest
3. isnull(replace(c.name, c.name, 'Company'), 'Guest') as type
Fanciness...if c.name is something, then 'Company', otherwise 'Guest'
4. r.details : reservation details
You need the outer join because of the nulls, this gives you a complete view of your data.
left outer join company c on r.cid = c.cid
left outer join guest g on r.gid = g.gid
So if you stick it all together you get what you are looking for:
select r.rid, r.description, isnull(c.name, g.name),
isnull(replace(c.name, c.name, 'Company'), 'Guest') as type,
r.details from res r
left outer join company c on r.cid = c.cid
left outer join guest g on r.gid = g.gid
1 Guest Res Chuck Guest Just some guy
2 Comp Res Tenth Company Corporate jerk

Conditional Select in MySQL

I am working on a mysql query and its giving me headache!
The Scenario:
I am building a website where people can select industries they are interested in (NOTIFY_INDUSTRY). I join the selected values and store in a database field.
Example: a member selects agriculture (id = 9) and oil and gas (id = 13). I join them as 9-13 and store in the database.
Users can select several industries, not limited to two.
Also, members can select an industry (COMPANY_INDUSTRY) it belongs in assuming Information Technology which is stored in the database too.
Sample table (members):
ID
EMAIL
COMPANY_NAME
COMPANY_INDUSTRY
NOTIFY_INDUSTRY
The problem:
When a new user registers on the website, mail (the mails are sent on daily basis) is sent to existing users who have the new user's industry (COMPANY_INDUSTRY) as one of their interested industries (NOTIFY_INDUSTRY).
What i have done:
$sql="select id, email
from members
where notify_industry in (
select company_industry
from members
where datediff($today, date_activated) <= 1)"
This does not select the right members and i do not know the right way to go about it
EDIT - Exact Problem with current output:
Does not return any row, even when it should.
Assuming the new user's company_industry is 9, and there is an existing user with notify_industry: 10-9-20; it is meant to return the existing members email as the new member is in the existing member's categories of interest; but i get blanks
As #Shiplu pointed out, this is largely a normalization issue. Despite what some people seem to think, multi-value columns are murder to try to get right.
Your basic issue is:
You have members, who are interested in one or more companies/industries, which belong to one or more industries. You table structure should probably start as:
Industry
===============
id -- autoincrement
name -- varchar
Company
==============
id -- autoincrement
name -- varchar
Company_Industry
===============
companyId -- fk reference to Company.id
industryId -- fk reference to Industry.id
Member
===============
id -- autoincrement
name -- varchar
email -- varchar
Member_Interest_Industry
=========================
memberId -- fk reference to Member.id
industryId -- fk reference to Industry.id
Member_Interest_Company
========================
memberId -- fk reference to Member.id
companyId -- fk reference to Company.id
To get all companies a member is interested in (directly, or through an industry), you can then run something like this:
SELECT a.name, a.email, c.name
FROM Member as a
JOIN Member_Interest_Company as b
ON b.memberId = a.id
JOIN Company as c
ON c.id = b.companyId
WHERE a.id = :inputParm
UNION
SELECT a.name, a.email, d.name
FROM Member as a
JOIN Member_Interest_Industry as b
ON b.memberId = a.id
JOIN Company_Industry as c
ON c.industryId = b.industryId
JOIN Company as d
ON d.id = c.companyId
WHERE a.id = :inputParm
You should redesign the tables, as others have suggested.
However, barring that, there is a gross hack you can do:
SET sql_mode = 'ANSI';
SELECT notify_members.id, notify_members.email
FROM members notify_members
INNER JOIN members new_members
WHERE CURRENT_DATE - new_members.date_activated <= 1
AND
new_members.company_industry RLIKE ('[[:<:]](' || REPLACE(notify_members.notify_industry, '-', '|') || ')[[:>:]]');
Yuck. Basically, you turn 9-13 into the MySQL regular expression [[:<:]](9|13)[[:>:]], which matches 9, 13, 13-27-61, etc., but does not match 19-131 and the like. (This supports a compound COMPANY_INDUSTRY field, too.)
Use join SQL syntax rather than a select in style..
You need to join the members table to itself.
Currently:
select id, email
from members where notify_industry in
(select company_industry
from members
where datediff($today, date_activated) <= 1
)
Use this style:
select m1.id, m1.email
from members m1
inner join members m2 on m1.company_industry = m.notify_industry
where datediff($today, m2.date_activated) <= 1
Note the use of aliasing to m1 and m2 to help understand which id and emails are returned.
This may get a little ugly but you could try the following
WARNING This will make a Cartesian Product worthy of any Mad Scientist
SELECT NotifyIndustry.id,NotifyIndustry.email
FROM
(
SELECT CONCAT('-',COMPANY_INDUSTRY,'-') company FROM members
WHERE datediff($today, date_activated) <= 1)"
) CompanyIndustry
INNER JOIN
(
SELECT CONCAT('-', NOTIFY_INDUSTRY,'-') who_to_notify
FROM members
) NotifyIndustry
ON LOCATE(company,who_to_notify)>0;
probably not the fastest query ever but this should do the job:
select m_to_notify.id, m_to_notify.email
from members m_to_notify
join members m_new_member
on '-' || m_to_notify.notify_industry || '-'
like '%-' || m_new_member.company_industry || '-%'
where datediff($today, m_new_memberdate_activated) <= 1)