PostgreSQL - Creating temporary columns instead of multiple rows - sql

I'm working with PostgreSQL customer records.
My task is to export customer records.
I have another table aside from customers with contact information.
One contact item per line (phone, value or email, value, etc.).
When I join and relate the data, I pull multiple records per customer ID (if more than 1 comm type for each customer, example phone and email).
How could I instead of making another row for each comm type, put the info into a temporary column like a phone column, a fax column, and an email column -- then have only 1 row for each customer.
edit -- you guys are masters
Database tables w/ columns:
account {
id
accountid
title (company name?)
shiptoaddress_id (links to address table)
billtoaddress_id (links to address table)
}
address {
city
country
state
name (company name?)
street1
street2
street3
zip
id
}
comlink {
isdeleted
type [Phone,Fax,E-mail,Cell,IM,FaceBook,Twitter,LinkedIn,Web Site,Other]
value
id
party_id (links to party table)
}
party {
isdeleted [t,f]
firstname
lastname
prefix
salutation
suffix
id
address_id
jobtitle_id
}
party_comlinks {
party_id (links to party table ex: fname lname...)
comlinks_id (links to comlink table phone, email, etc 1 item per row)
}
So what I want to do is pull all customers with the following data:
customer.id, customer.accountid, customer.title, shipping.name, shipping.street1, shipping.street2, shipping.street3, shipping.city, shipping.state, shipping.zip, billing.name, billing.street1 (etc), billing.city, billing.state, billing.zip, party.contactperson (as party.firstname + party.lastname) AND have phone, email, fax
I'm not sure if this will be possible as in the system I think you can have multiple shipping info, billing info, contact info for each customer... however where each shipping info and billing info is the same, I only want one row of phone, email, fax, etc and not multiple rows for each phone, email, fax, etc.
Clear as mud, right? :-)
edit -- may have gotten it now, but would still appreciate input
SELECT account.id, account.accountid, account.status, account.title AS "customer",
party.firstname AS "firstname", party.lastname as "lastname", address.name AS "billname",
address.street1 AS "billtostreet1", address.street2 AS "billtostreet2", address.city as "billtocity",
address.state AS "billtostate", address.zip AS "billtozip", address2.name AS "shiptoname",
address2.street1 as "shiptostreet1", address2.street2 AS "shiptostreet2", address2.city AS "shiptocity",
address2.state AS "shiptostate", address2.zip AS "shiptozip",
((SELECT a.value
FROM public.comlink a, party_comlinks b
WHERE b.party_id=party.id AND b.comlinks_id=a.id AND a.type='Phone')) AS "phone",
((SELECT a.value
FROM public.comlink a, party_comlinks b
WHERE b.party_id=party.id AND b.comlinks_id=a.id AND a.type='Fax')) AS "fax",
((SELECT a.value
FROM public.comlink a, party_comlinks b
WHERE b.party_id=party.id AND b.comlinks_id=a.id AND a.type='E-Mail')) AS "email"
FROM ( public.account account
INNER JOIN public.party party ON account.contact_id = party.id )
INNER JOIN public.comlink comlink ON party.id = comlink.party_id
INNER JOIN public.address address ON account.billtoaddress_id = address.id
INNER JOIN public.contact contact ON account.contact_id = contact.id
LEFT JOIN public.contact_shiptoaddress contact_shiptoaddress ON contact.id = contact_shiptoaddress.contact_id
LEFT JOIN public.address address2 ON contact_shiptoaddress.shiptoaddress_id = address2.id
WHERE account.isdeleted = 'f'
--WHERE ((comlink.type = 'E-Mail'))
--AND ((account.walkin is null OR (NOT ( account.walkin ))))
--AND ((NOT ( (account."status" = 'CustomerStatusInactive') )))
--AND ((account."prospect" is null
--OR (NOT ( account."prospect" ))))
ORDER BY account.id ASC
Actually, the INNER JOIN's were not what I needed... I needed to do LEFT JOIN's as described below.

You'll have to do this by explicitly joining against the contact table multiple times. There is now way to have a "dynamic" set of output columns depending on the data.
Example:
SELECT customers.name, phone_contacts.value AS phone, fax_contacts.value AS fax, ...
FROM customers
LEFT JOIN (SELECT * FROM contacts WHERE contact_type = 'phone') AS phone_contacts ON ...
LEFT JOIN (SELECT * FROM contacts WHERE contact_type = 'fax') AS fax_contacts ON ...

Related

how to eliminiate the duplicates from SQL table which has two type of data?

I have a table in my database called email. this will store the email address of all customers based on the type of email they provide. there is a column email type which tells the email is business or personal. when a customer provide business email address we will update it under type business and keep the personal email type as Null. when we try to join this table with my query it gives me duplicate data in email column as the email table has two columns(one as NULL, other one has email address). I cannot give a condition where emailaddress is not null because there are customer who doesn't have provided any email address so both of their emailtype will be null. so those records will be avoided from my query. please help me how to pull the correct email address without pulling the deplicate
I have tried using the condition where emailaddress is not null. but the query result is avoiding the data where both the emailtypes are null
select distinct
c.rtlrcontractidentifier as RetailerID,
CT.Fullname as Fullname,
ad.address1 as Address,
ad.City,
ad.provinceorstate as State,
ad.postalcode as Zip
, e.emailaddress
from contract c
inner join siteidentification si on si.siteoid=c.siteoid
inner join consumer cs on cs.consumeroid=c.billingconsumeroid
inner join contact ct on ct.contactoid=cs.billingcontactoid
left Join Address ad on ad.FKObjectOID = ct.contactOID
and ad.fktableobjectoid=1049
left join email e on e.contactoid=ct.contactoid
where c.rtlrcontractidentifier in('AG74113048'
)
I need to get the emailaddress which is updated but not with the duplicate(null). also should get the data where there is no emailaddress on both emailtypes.
What happened is that you have rows in email table, with contact_oid, and email_type
It is not specified, but I assume your email table:
contact_oid, email_type, email_address
For business emails, it will be email_type = 'B' and for personal emails, the email_type = NULL. To find only emails record that is available, instead of placing a WHERE on the email_type, use the contact_oid to determine if the row exists or not.
In this case, just changing the LEFT JOIN to INNER JOIN should work. If this is not the case, then provide some sample data to work on.
SELECT DISTINCT
c.rtlrcontractidentifier AS RetailerID
, ct.Fullname AS Fullname
-- , e.emailtype --> To show the email type
, e.emailaddress
FROM contracts AS c
INNER JOIN consumer AS cs ON cs.consumeroid = c.billingconsumeroid
INNER JOIN contact AS ct ON ct.contactoid = cs.billingcontactoid
--> Change from LEFT JOIN to INNER JOIN
INNER JOIN email AS e ON e.contactoid = ct.contactoid
WHERE c.rtlrcontractidentifier IN ('AG74113048');
I believe that another way to avoid duplicates is to specify two columns for business email and personal email:
SELECT DISTINCT
c.rtlrcontractidentifier as RetailerID,
.
.
.
be.emailaddress as businesss_email,
pe.emailaddress as personal_email
FROM contract c
.
.
.
LEFT JOIN email be on be.contactoid=ct.contactoid
LEFT JOIN email pe on pe.contactoid=ct.contactoid
WHERE c.rtlrcontractidentifier in('AG74113048') AND
be.email_type = 'Business' AND
pe.email_type = 'Personal'
If you don't like having the two columns, you should be able to combine them with:
SELECT DISTINCT
c.rtlrcontractidentifier as RetailerID,
.
.
.
ISNULL(be.emailaddress, pe.emailaddress) AS emailaddress
FROM contract c
.
.
.
LEFT JOIN email be on be.contactoid=ct.contactoid
LEFT JOIN email pe on pe.contactoid=ct.contactoid
WHERE c.rtlrcontractidentifier in('AG74113048') AND
be.email_type = 'Business' AND
pe.email_type = 'Personal'
In the above query, you will get the business email if it's not NULL, otherwise you will get the personal address.

How to work in case in join condition

How to find city when ContactID is provided and condition is if ContactID is coming as 123 then it will look whether it is P or C, If P then it will go to Person table and returns City(USA) as output and If C then it will go to Company table and gives City(AUS) as output.
NB: all tables contain thousands of record and City value comes from run time.
Unless you're dynamically generating the query (i.e. using some language other than SQL to execute it) then you need to join on both tables anyway. If you're joining on both tables then there's no need for a CASE statement:
select *
from contacts co
left outer join person p
on co.contactid = p.contactid
and co.person_company = 'P'
left outer join company c
on co.contactid = c.contactid
and co.person_company = 'C'
You'll start noting an issue here, for every column from PERSON and COMPANY you're going to have to add some business logic to work out which table you want the information from. This can get very tiresome
select co.contactid
, case when p.id is not null then p.name else c.name end as name
from contacts co
left outer join person p
on co.contactid = p.contactid
and co.person_company = 'P'
left outer join company c
on co.contactid = c.contactid
and co.person_company = 'C'
Your PERSON and COMPANY tables seem to have exactly the same information in them. If this is true in your actual data model then there's no need to split them up. You make the determination as to whether each entity is a person or a company in your CONTACTS table.
Creating additional tables to store data in this manner is only really helpful if you need to store additional data. Even then, I'd still put the data that means the same thing for a person or a companny (i.e. name or address) in a single table.
If there's a 1-2-1 relationship between CONTACTID and PID and CONTACTID and CID, which is what your sample data implies, then you have a number of additional IDs, which have no value.
Lastly, if you're not restricting that only companies can go in the COMPANY table and individuals in the PERSON table. You need the PERSON_COMPANY column to exist in both PERSON and COMPANY, though as a fixed string. It would be more normal to set up this data model as something like the following:
create table contacts (
id integer not null
, contact_type char(1) not null
, name varchar2(4000) not null
, city varchar2(3)
, constraint pk_contacts primary key (id)
, constraints uk_contacts unique (id, contact_type)
);
create table people (
id integer not null
, contact_type char(1) not null
, some_extra_info varchar2(4000)
, constraint pk_people primary key (id)
, constraint fk_people_contacts
foreign key (id, contact_type)
references contacts (id, contact_type)
, constraint chk_people_type check (contact_type = 'P')
);
etc.
you can LEFT JOIN all 3 tables and the using a CASE statement select the one that you need based on the P or C value
SELECT
CASE c.[Person/Company]
WHEN 'P' THEN p.NAME
WHEN 'C' THEN a.Name
END AS Name
FROM Contact c
LEFT JOIN Person p on p.ContactId = c.ContactId
LEFT JOIN Company a on a.ContachId = c.ContactId
Ben's answer is almost right. You might want to check that the first join has no match before doing the second one:
select c.*, coalesce(p.name, c.name) as p.name
from contacts c left outer join
person p
on c.contactid = p.contactid and
c.person_company = 'P' left join
company co
on c.contactid = co.contactid and
c.person_company = 'C' and
p.contactid is null;
This may not be important in your case. But in the event that the second join matches multiple rows and the first matches a single row, you might not want the additional rows in the output.

How to write a SQL query for the below scenario

Sales
order id : 1(primary key)
Billing address id -250
Shipping address id -285
Address table has the below entries
id :250
Addressline1 : XXX
Addressline2 :YYY
id :285
Addressline1 : AAA
Addressline2 :BBB
How to write a query to retrieve the order id, billing address, shipping address in a single query ?
Use the following query with join. The below is a sample code.
select order.orderid,
ad1.Addressline1,
ad1.Addressline2,
ad2.Addressline1,
ad2.Addresslinne2
from order
join address ad1 on ad1.id=order.billingaddressid
join address ad2 on ad2.id=order.shippingaddressid
You can join to the address table multiple times (this uses an outer join -- depends on potential null values):
select s.id,
billing.Addressline1,
shipping.Addressline1
from sales s
left join address billing on s.billingaddressid = billing.id
left join address shipping on s.shippingaddressid = shipping.id
A Visual Explanation of SQL Joins

Select duplicated field name

If I have the following scenario
Table that store people
id_person, name, age (...)
And a table that stores address of people
id_address, id_person, city
If I run a query like this
select * from people P left join address A on P.id_person = A.id_person
I'm getting id_person === null in result set (because there IS a person, but no address has been recorded it, which is fine).
The null is comming from the table address. Is it possible to solve this without doing select field1, field2, field3 ... (lots os fields)?
Example
Person
id_person Name
1 John
2 Steve
Address
id_address id_person city
1 1 'AnyCity'
When I run a query like this
select * from people P left join address A on P.id_person = A.id_person
where P.name = 'Steve'
His id_person is returning null
You mean you only want the id_person from the people table, not from the address table (which sometimes is NULL)?
select p.id_person, p.name, p.age, a.id_address, a.city
from people P left join address A ON P.id_person = A.id_person
Is it possible to solve this without doing select field1, field2, field3 ... (lots os fields)
No - you either use * or identify the fields. You could select all fields from one table and then cherry pick from the other table:
select P.*, A.address, A.City, ...
from people P
left join address A where P.id_person = A.id_person

Using conditions in joins

I've below 3 tables from which need to fetch address for the records.
1. Address AD
---> It has all the address values(addrline1,addrline2,state,etc..) alongwith AddressID column
2. Address_Employee AE
---> It has EmpID,AddressID,AddressTypeID columns
There can be multiple instances of same EmpID with different AddressID and AddressTypeID value as either 3 or 1
3. Address_Type AT
---> There are 2 rows, with AddressTypeID and AddressType columns. AddressTypeID value with 1 will be Home and 3 will be Postal.
Need to fetch records in such a way that first need to look for Postal address, if not available then Home.
I'm using the query:
SELECT (AD.sAddressLine1+' '+AD.sAddressLine2+' '+AD.sSuburb+' '+AD.sPostCode) AS Address
from Address AD
INNER JOIN Address_Employee AE ON AD.AddressID = AE.AddressID
INNER JOIN Address_Type AT ON AE.AddressTypeID= AT.AddressTypeID
But how can I apply the condition: "first need to look for Postal address, if not available then Home."
Please advise.
Thanks, Krishna
You could create a view delivering the preferred address for every EmpID, and use this for furthor joins.
Create View V_Emp_With_Preferred_Address as
Select Distinct EmpID
,Case when Exists(Select * from Address_Employee where Address_Employee.EmpID=e.EmpID and AddressTypeID=3) then
( -- take preferred postal address if available
Select ISNULL(sAddressLine1+' ','')+ISNULL(sAddressLine2+' ','')+ISNULL(sSuburb+' ','')+ISNULL(sPostCode,'')
from Address_Employee
join Address on Address.AddressID=Address_Employee.AddressID
where Address_Employee.EmpID=e.EmpID and AddressTypeID=3
)
else
(
Select ISNULL(sAddressLine1+' ','')+ISNULL(sAddressLine2+' ','')+ISNULL(sSuburb+' ','')+ISNULL(sPostCode,'')
from Address_Employee
join Address on Address.AddressID=Address_Employee.AddressID
where Address_Employee.EmpID=e.EmpID and AddressTypeID=1
)
end as Address
from dbo.Address_Employee e