Select Distinct with CASE - PostgreSQL - postgresql

I am trying to select places infos from different tables: I have the place in one table, the geographical coordinates on another, and telephone and email in another. The issue is that we may or not have the email and telephone. I am looking for a way to get the email / phone when they exist, and have a blank value if they are not known. So I use the "CASE" instruction on the SELECT.
If we have no email and no phone, everything goes ok : we have only one line returned.
But if we have the email or the phone, it returns 2 lines; and the worst case is when we have both email and phones: in this case it returns three lines for each place :
place name , blank phone , blank mail
place name , blank phone , populated mail
place name , populated phone , blank mail
I am looking for a way to get only one line :
0) place name + populated (or not) phone + populated (or not) mail
I tried the 'distinct' but it doesn't do the trick :(
Here is my query :
select distinct
places.name as place,
CASE place_properties.pkey WHEN 'gen.telephone'
THEN place_properties.pvalue
END
as telephone,
CASE place_properties.pkey WHEN 'gen.email'
THEN place_properties.pvalue
END
as email
from
places
inner join
place_properties on place_properties.place_id = places.id
And here is a simplified answer example :
"ALLIAT" ;"0561059858" ;""
"ALLIAT" ;"" ;"contact#leslonguespistes.com"
"ALLIAT" ;"" ;""
"TARASCON SUR ARIEGE" ;"0561023281" ;""
"TARASCON SUR ARIEGE" ;"" ;"hotel#manoiragnes.com"
"TARASCON SUR ARIEGE" ;"" ;""
"Les Cabanes" ;"" ;""
We see that 'ALLIAT' and 'Tarascon' are returned three time because it has both a phone number and an email while "Les cabanes" is returned only once because it doesn't have any of it.
How to change the SQL to have only one line when we habe the email and/or the phone on the database?

try this:
SELECT
places.name AS place
, pp.telephone
, pp.email
FROM places
LEFT JOIN (
SELECT
place_id
, MAX(CASE place_properties.pkey WHEN 'gen.telephone' THEN place_properties.pvalue END) AS telephone
, MAX(CASE place_properties.pkey WHEN 'gen.email' THEN place_properties.pvalue END) AS email
FROM place_properties
WHERE place_properties.pkey IN ('gen.telephone', 'gen.email')
GROUP BY
place_id
) AS pp
ON places.id = pp.place_id
It might be possible to use alternatives to this approach but for that I would suggest we need to a good set of sample data.
It isn't clear if you only want places that have a phone of email, so I have used a LEFT JOIN which allows all places to be listed. If you do only want those places with (phone or email) then use an INNER JOIN instead

I think you can try this for get all email and telephone number
SELECT places.name AS place,
pp.telephone
pp.email
FROM places
LEFT JOIN (
SELECT
place_id,
ARRAY_TO_STRING(ARRAY_AGG(DISTINCT NULLIF(CASE place_properties.pkey WHEN 'gen.telephone' THEN place_properties.pvalue END,'')),',') AS telephone,
ARRAY_TO_STRING(ARRAY_AGG(DISTINCT NULLIF(CASE place_properties.pkey WHEN 'gen.email' THEN place_properties.pvalue END,'')),',') AS email
FROM place_properties
WHERE place_properties.pkey IN ('gen.telephone', 'gen.email')
GROUP BY 1
) AS pp
ON places.id = pp.place_id

You can use sub-selects here, like: select
name as place,
(select pvalue from place_properties where pkey = 'gen.telephone' and place_properties.place_id = places.id) as telephone,
(select pvalue from place_properties where pkey = 'gen.email' and place_properties.place_id = places.id) as email
from
places

Related

find the first date after a given date in SQL

I have two tables related to vital signs like below:
enter image description here
and have the second table
enter image description here
I want to show the table below:
enter image description here
My problem is when I used with statement in SQL to join the tables, the date sometimes didn't show the first record after the date of admission, but it showed the second or third one like this:
enter image description here
I want to show the first and last record after the admission date.
Can anyone help me with this?
With admissionCTE AS (SELECT DISTINCT on (id) id, dateofadmission:: date as Date_of_admission
WHERE
dateofadmission::date is not NULL
and dateofadmission::date < now()),
first_BMI_CTE AS (select id,bmi, date::date as first_assess
from vital_signs
where vital_signs.bmi is not null
order by
id),
last_BMI_CTE AS(SELECT id, bmi,date
from vital_signs
where bmi is not null AND (id,date::DATE) IN (select id,max(date::DATE) as last_date_assessment
from vital_signs
Group by id))
SELECT DISTINCT on (first_BMI_CTE.id) first_BMI_CTE.id as "ID",
admissionCTE.Date_of_admission::date,
first_BMI_CTE.first_assess as "First_assessment_date",
first_BMI_CTE.bmi as "First BMI",
case
when last_BMI_CTE.obs_datetime::date = first_BMI_CTE.first_assess::date then null
else last_BMI_CTE.obs_datetime
end as "Last_assessment_date",
case
when last_BMI_CTE.obs_datetime::date = first_BMI_CTE.first_assess::date then null
else last_BMI_CTE.bmi end as "Last BMI"
from first_BMI_CTE
left outer join last_BMI_CTE on
last_BMI_CTE.id = first_BMI_CTE.id
left outer join admissionCTE on
admissionCTE.id = first_BMI_CTE.id
where admissionCTE.id is not null
and first_BMI_CTE.first_assess >= admissionCTE.Date_of_admission

T-SQL Question for Getting One Customer Type When There Can be More Than One Value

We have an organization that can have more than one customer type basically. However, what a user wants to see is either the partner or direct type (customer type is either Direct, Partner1, Partner2, or Partner3 but can be direct plus a partner value but only can be one of the partner values). So if a customer is both (ex: Direct and Partner1) they just want the type that is a partner (ex: Partner1). So I tried splitting out partners only into one temp table from a few tables joining together different org data. I have the same query without any limit pulling into a different temp table. Then I calculate count and put that into a temp table. Then I tried gathering data from all the temp tables. That is where I run into trouble and lose some of the customers where the type is direct (I have a image link below for a directcustomer and a customer who is both). I have been out of SQL for a bit so this one is throwing me...I figure the issue is the fact that I have a case statement referencing a table that a direct customer will not exist in (#WLPO). However I am not sure how to achieve pulling in these customers while also only selecting which partner type it is for a customer that has a partner and is also direct. FYI using MSSMS for querying.
If OBJECT_ID('tempdb..#WLPO') IS NOT NULL
DROP TABLE #WLPO
IF OBJECT_ID('tempdb..#org') IS NOT NULL
DROP TABLE #org
IF OBJECT_ID('tempdb..#OrgCount') IS NOT NULL
DROP TABLE #OrgCount
IF OBJECT_ID('tempdb..#cc') IS NOT NULL
DROP TABLE #cc
Select
o.OrganizationID,
o.OrganizationName,
os.WhiteLabelPartnerID,
s.StateName
INTO #WLPO
from [Org].[Organizations] o
join [Org].[OrganizationStates] os on o.OrganizationID=os.OrganizationID --and os.WhiteLabelPartnerID = 1
join [Lookup].[States] s on os.StateID = s.StateID
join [Org].[PaymentOnFile] pof on pof.OrganizationID=o.OrganizationID
where os.WhiteLabelPartnerID in (2,3,4)
and os.StateID in (1, 2, 3)
and o.OrganizationID = 7613
select * from #WLPO
Select
o.OrganizationID,
o.OrganizationName,
os.WhiteLabelPartnerID,
s.StateName
INTO #org
from [Org].[Organizations] o
join [Org].[OrganizationStates] os on o.OrganizationID=os.OrganizationID --and os.WhiteLabelPartnerID = 1
join [Lookup].[States] s on os.StateID = s.StateID
join [Org].[PaymentOnFile] pof on pof.OrganizationID=o.OrganizationID
where 1=1--os.WhiteLabelPartnerID = 1
and os.StateID in (1, 2, 3)
and o.OrganizationID = 7613
select * from #org
Select
OrganizationID,
count(OrganizationID) AS CountOrgTypes
INTO #OrgCount
from #org
where OrganizationID = 7613
group by OrganizationID
select * from #OrgCount
Select distinct
ct.OrganizationID,
ok.OrganizationName,
ct.CountOrgTypes,
case when ct.CountOrgTypes = 2 then wlp.WhiteLabelPartnerID
when ct.CountOrgTypes = 1 then ok.WhiteLabelPartnerID
END AS CustomerTypeCode,
case when ct.CountOrgTypes = 2 then wlp.StateName
when ct.CountOrgTypes = 1 then ok.StateName END As OrgState
INTO #cc
from #org ok
left join #WLPO wlp on wlp.OrganizationID=ok.OrganizationID
join #OrgCount ct on wlp.OrganizationID=ct.OrganizationID
select * from #cc
Select
OrganizationID,
OrganizationName,
CountOrgTypes,
case when CustomerTypeCode = 1 then 'Direct'
when CustomerTypeCode = 2 then 'Partner1'
when CustomerTypeCode = 3 then 'Partner2'
when CustomerTypeCode = 4 then 'Partner3' ELSE Null END AS CustomerType,
OrgState
from #cc
order by OrganizationName asc
DirectCustomer
CustomerwithBoth

Converting rows to columns using crosstab in PostgreSQL not working (relation “table” does not exist)

I need to use the compiled data using CTE and then convert the columns to rows using crosstab(open to other ideas) in the next select statement. Below is the query.
with checked_adgroup AS (
SELECT
ua.new_adgroup,
ua.account,
ua.campaign,
ua.ad_group,
ua."position",
cp.category,
pt.full_value,
FROM unnest_adgroup ua
LEFT JOIN taxonomy_category cp ON ua."position" = cp."position"
LEFT JOIN taxonomy pt ON ua.short_val = pt.short_value AND cp.category = pt.category AND (pt.lob IS NULL OR pt.lob = ua.lob)
)
SELECT *
from crosstab(
'select
cad.account,
cad.campaign,
cad.ad_group,
cad.category,
cad.full_value
FROM checked_adgroup cad
WHERE cad.all_correct AND cad.category IS NOT NULL
ORDER BY 1,2,3')
AS final_result(
account text, campaign text, ad_group text,
division text, lob text, match_type text );
Error message:
ERROR: relation "checked_adgroup" does not exist LINE 7: FROM checked_adgroup cad
Output of checked_adgroup cte looks like below:
enter image description here
Desired output of the final statement is:
enter image description here
Welcome to the community. First off please do not post images, they are useless to work with, and in some instances they are prohibited and cannot be viewed. Instead use formatted text.
I haven't used crosstab functionality all that much, but it does offer a second version which contains 2 queries, the second feeding into the first. There are however a couple errors in your posted query that would need correcting either way. So that first. Look for --<< tag.
with checked_adgroup AS (
SELECT
ua.new_adgroup,
ua.account,
ua.campaign,
ua.ad_group,
ua."position",
cp.category,
pt.full_value,
--<< missing column or ending , above should not be there, assumption missing column see below.
FROM unnest_adgroup ua
LEFT JOIN taxonomy_category cp ON ua."position" = cp."position"
LEFT JOIN taxonomy pt ON ua.short_val = pt.short_value AND cp.category = pt.category AND (pt.lob IS NULL OR pt.lob = ua.lob)
)
SELECT *
from crosstab(
'select
cad.account,
cad.campaign,
cad.ad_group,
cad.category,
cad.full_value
FROM checked_adgroup cad
WHERE cad.all_correct AND cad.category IS NOT NULL
--<< above line has 2 errors:
--<< Incorrectly formatted needs to cad.all_correct is not null AND cad.category IS NOT NULL
--<< column cad.all_correct does not exist (see missing column above
ORDER BY 1,2,3')
AS final_result(
account text, campaign text, ad_group text,
division text, lob text, match_type text);
Now we need to transform the CTE to a second query that crosstab might be ale to use. I have identified each with Postgres $Quoting$, not so much from necessity as standard string quote (') would be sufficant, but more from visibility standing.
select *
from crosstab(
$ct1$select
account,
campaign,
ad_group,
category,
full_value
--<< from checked_adgroup cad
--<< where cad.all_correct and cad.category is not null
--<< moved above lines to second query to avoid reference and removed qualification
order by 1,2,3
$ct1$
, $ct2$select *
from (
select
ua.new_adgroup,
ua.account,
ua.campaign,
ua.ad_group,
ua."position",
cp.category,
pt.full_value,
'mssing from orig posted query' all_correct
from unnest_adgroup ua
left join taxonomy_category cp on ua."position" = cp."position"
left join taxonomy pt on ua.short_val = pt.short_value
and cp.category = pt.category
and (pt.lob is null or pt.lob = ua.lob)
) s
where all_correct is not null and cad.category is not null
--<< move from query1
$ct2$ )
as final_result(
account text, campaign text, ad_group text,
division text, lob text, match_type text );
But at this point I get an error relationship unnest_adgroup does not exist. Which is true as you did not post the definition, not other referenced tables. But that seems to imply the syntax is correct.
Admittedly, this may be way off base if so, so be it, I can always delete later. But, I stuck at home with no other projects at the moment and this seems like an interesting question. Looking forward to the results. Good Luck.

Highlighting duplicate values

I have the following script:
SELECT
Reference.ReferenceNumber AS [Reference.ReferenceNumber)
Reference.LastName
Reference.FirstName
Address.ReferenceNumber AS [Address.ReferenceNumber]
Address.Address1
Address.Address2
Address.Address3
Address.Address4
Address.ZipCode
Telephone.ReferenceNumber AS [Telephone.ReferenceNumber]
Telephone.TelephoneNumber
Email.ReferenceNumber AS [Email.ReferenceNumber]
Email.EmailAddress
FROM
Reference
INNER JOIN Address
ON Reference.ReferenceNumber = Reference.ContactNumber
LEFT OUTER JOIN Telephone
ON Reference.ReferenceNumber = Telephone.ReferenceNumber
LEFT OUTER Join Email
ON Reference.ReferenceNumber = Email.ReferenceNumber
This pulls through all customers, plus their addresses, email and phone numbers.
However some have duplicate surnames and addresses.
Please can you advise an amendment to the script that would show which records were duplicates? Ideally I'd like the output to show where the surname, address 1 and the zipcode were identical and restrict the output to the duplicate rows only.
Many thanks.
something like
SELECT
Reference.ReferenceNumber AS [Reference.ReferenceNumber),
case when vrln.c is null then Reference.LastName else 'dup: ' + Reference.LastName end as LastName,
Reference.FirstName,
Address.ReferenceNumber AS [Address.ReferenceNumber],
Address.Address1,
Address.Address2,
Address.Address3,
Address.Address4,
Address.ZipCode,
Telephone.ReferenceNumber AS [Telephone.ReferenceNumber],
Telephone.TelephoneNumber,
Email.ReferenceNumber AS [Email.ReferenceNumber],
Email.EmailAddress
FROM
Reference
INNER JOIN Address
ON Reference.ReferenceNumber = Reference.ContactNumber
LEFT OUTER JOIN Telephone
ON Reference.ReferenceNumber = Telephone.ReferenceNumber
LEFT OUTER Join Email
ON Reference.ReferenceNumber = Email.ReferenceNumber
left join (
select rln.LastName, add.Address1, add.ZipCode, count(rln.LastName) as c
from
Reference rln
INNER JOIN Address add
ON rln.ReferenceNumber = add.ReferenceNumber
group by rln.LastName, add.Address1, add.ZipCode
having count(rln.LastName) > 1
) vrln on Reference.LastName = vrln.LastName and Address.Address1 = vrln.Address1 and Address.ZipCode = vrln.ZipCode
where vrln.c is not null
but, to find duplicates :
select rln.LastName, add.Address1, add.ZipCode, count(rln.LastName) as c
from
Reference rln
INNER JOIN Address add
ON rln.ReferenceNumber = add.ReferenceNumber
group by rln.LastName, add.Address1, add.ZipCode
having count(rln.LastName) > 1
is enough

Nested SELECT statement in a CASE expression

Greetings,
Here is my problem.
I need to get data from multiple rows and return them as a single result in a larger query.
I already posted a similar question here.
Return multiple values in one column within a main query but I suspect my lack of SQL knowledge made the question too vague because the answers did not work.
I am using Microsoft SQL 2005.
Here is what I have.
Multiple tables with CaseID as the PK, CaseID is unique.
One table (tblKIN) with CaseID and ItemNum(AutoInc) as the combined PK.
Because each person in the database will likely have more than one relative.
If I run the following, in a SQL query window, it works.
DECLARE #KINList varchar(1000)
SELECT #KINList = coalesce(#KINList + ', ','') + KINRel from tblKIN
WHERE CaseID = 'xxx' and Address = 'yyy'
ORDER BY KINRel
SELECT #KINList
This will return the relation of all people who live at the same address. the results look like this...
Father, Niece, Sister, Son
Now, the problem for me is how do I add that to my main query?
Shortened to relevant information, the main query looks like this.
SELECT DISTINCT
c.CaseID,
c.Name,
c.Address,
Relatives=CASE WHEN exists(select k.CaseID from tblKIN k where c.CaseID = k.CaseID)
THEN DECLARE #KINList varchar(1000)
SELECT #KINList = coalesce(#KINList + ', ','') + KINRel from tblKIN
WHERE CaseID = 'xxx' and Address = 'yyy'
ORDER BY KINRel
SELECT #KINList
ELSE ''
END
FROM tblCase c
ORDER BY c.CaseID
The errors I receive are.
Server: Msg 156, Level 15, State 1, Line 13
Incorrect syntax near the keyword 'DECLARE'.
Server: Msg 156, Level 15, State 1, Line 18
Incorrect syntax near the keyword 'ELSE'.
I tried nesting inside parenthesis from the DECLARE to the end of the SELECT #KINList.
I tried adding a BEGIN and END to the THEN section of the CASE statement.
Neither worked.
The source table data looks something like this. (periods added for readability)
tblCase
CaseID Name Address
10-001 Jim......100 Main St.
10-002 Tom....150 Elm St.
10-003 Abe.....200 1st St.
tblKIN
CaseID ItemNum Name Relation Address
10-001 00001 Steve...Son........100 Main St.
10-002 00002 James..Father....150 Elm St.
10-002 00003 Betty....Niece......150 Elm St.
10-002 00004 Greta...Sister.....150 Elm St.
10-002 00005 Davey..Son........150 Elm St.
10-003 00006 Edgar...Brother...200 1st St.
If I run the query for CaseID = 10-002, it needs to return the following.
CaseID Name Address.......Relatives
10-002 Tom...150 Elm St. ..Father, Niece, Sister, Son
I am sure this is probably a simple fix, but I just don't know how to do it.
Thank you for your time, and I apologize for the length of the question, but I wanted to be clear.
Thanks !!!
When I did something similar I had to create a scalar function to do the coalesce that returns the varchar result. Then just call it in the select.
CREATE FUNCTION GetRelatives
(
#CaseID varchar(10)
)
RETURNS varchar(1000)
AS
BEGIN
DECLARE #KINList varchar(1000)
SELECT #KINList = coalesce(#KINList + ', ','') + KINRel from tblKIN
WHERE CaseID = #CaseID
ORDER BY KINRel
RETURN #KINList
END
Then your select
SELECT DISTINCT
c.CaseID,
c.Name,
c.Address,
database.dbo.GetRelatives(c.CaseID) AS Relatives
FROM tblCase c
ORDER BY c.CaseID
You can create a FUNCTION which takes in the caseID as the arguement and returns true or false.
Since you are calling the nested query multiple times, its definitely a performance hit. A better solution is to execute the query and store the results in a temporary table.
Then pass this temporary table and the caseID to the FUNCTION and check for containment.