I'm Using Advantage Server 12.
I need a result set that finds all the records when the supplied parameter occurs in either of a master or a detail table when the supplied value is not the linking field.
Customer Table
|AccountCode |Tel1|Tel2|Tel3|Accout Name/Address etc....
| ACODE | | | |
N < 2
Contact Table
|AccountCode |cTel1|cTel2|cTel3|ContactName/Address etc....
| ACODE | | | |
N >=0
Require the Row from Customer and All rows with matching ACODE from Contacts.
When Tel1 or Tel2 or Tel3 or cTel1 or cTel2 or cTel3 is supplied
This SQL gives the required result when the supplied value ('thenumber') is found in any of the Customer fields
When the supplied number is found in the contact table it returns the Customer fields and only data from the matching row in Contacts
declare #Tel string;
set #Tel = 'thenumber';
select
#TEL as calling,c.Tel1,c.Tel2,c.contact as "Primary",
c.acnt_nmbr,c.Acnt_name, a.contact,a.cTel1 as telephone1,a.cTel2 as telephone2
from
customer c
full outer join contact a on (c.acnt_nmbr=a.acnt_nmbr)
where
replace(c.Tel1,' ','') = #Tel
or
replace(c.Tel2,' ','') = #Tel
or
replace(c.Tel3,' ','') = #Tel
or
replace(a.cTel1,' ','') = #Tel
or
replace(a.cTel2,' ','') = #Tel
or
replace(a.cTel3,' ','') = #Tel
Result when 'thenumber' is found in Customer - This is what we want
calling Tel1 Tel2 Primary acnt_nmbr Acnt_name contact telephone1 telephone2
thenumber 11111111 thenumber KIERAN 687 theCo Pat 12234560 333444555
thenumber 11111111 thenumber KIERAN 687 theco Mary 45678900 444555666
thenumber 11111111 thenumber KIERAN 687 theco Jon 22233344
thenumber 11111111 thenumber KIERAN 687 theco Paul 22244455 124578111
thenumber 11111111 thenumber KIERAN 687 theco Jane 33225544
Result when 'thenumber' is found in Contacts - We want the same result set as above
calling Tel1 Tel2 Primary acnt_nmbr Acnt_name contact telephone1 telephone2
thenumber 11111111 2222222 KIERAN 687 theco Jane thenumber
I'm thinking either some self join or EXISTS statement is the answer but not sure how to proceed.
It's a bit more tricky than you seem to expect and as far as I just see your latest comment, I hope the solution I will propose you will work (I do not know SAP technical environment at all).
On a SQL point of you, my approach is the following.
For both customer and contact, find matching values. It's quite easy for customer, but for contacts you need to identify single matching values, then keep contacts where all values are matching
Then, build the assembly of customer and contacts.
Finally, union results from both first filters, joined with the basis data.
Here is the final request I propose:
with cust_check as (
SELECT account_code, tel1, tel2, tel3, address,
CASE WHEN tel1 = #Tel THEN 1
ELSE CASE WHEN tel2 = #Tel THEN 1
ELSE CASE WHEN tel3 = #Tel THEN 1
ELSE 0
END
END
END as cust_match
from customer
),
cust_filter as (
SELECT account_code, tel1, tel2, tel3, address AS detail
FROM cust_check
WHERE cust_match = 1
),
contact_check as (
SELECT account_code, ctel1, ctel2, ctel3, cname,
CASE WHEN ctel1 = #Tel THEN 1
ELSE CASE WHEN ctel2 = #Tel THEN 1
ELSE CASE WHEN ctel3 = #Tel THEN 1
ELSE 0
END
END
END as contact_match
FROM contact
),
contact_filter as (
SELECT account_code, count(*) as nb_rows, sum(contact_match) as nb_matched
FROM contact_check
GROUP BY account_code
HAVING count(*) = sum(contact_match)
),
all_contacts as (
SELECT t.account_code, ctel1, ctel2, ctel3, cname, address
FROM contact as t
JOIN customer as c ON c.account_code = t.account_code
),
union_match as
(
SELECT c.account_code, c.ctel1, c.ctel2, c.ctel3, c.cname, c.address
FROM all_contacts c
JOIN cust_filter f ON f.account_code = c.account_code
UNION
SELECT c.account_code, c.ctel1, c.ctel2, c.ctel3, c.cname, c.address
FROM all_contacts c
JOIN contact_filter f ON f.account_code = c.account_code
)
SELECT account_code, ctel1, ctel2, ctel3, cname, address
FROM union_match
This request is based on MS SQL Server and you can easily 'play' with it thanks to DB-Fiddle - I just hope it will be compliant with your constraints!
This should achieve what you want:
select
#TEL as calling,c.Tel1,c.Tel2,c.contact as "Primary",
c.acnt_nmbr,c.Acnt_name, a.contact,a.cTel1 as telephone1,a.cTel2 as telephone2
from
customer c
left join contact a on (c.acnt_nmbr=a.acnt_nmbr)
WHERE c.acnt_nmbr IN
(
SELECT DISTINCT acnt_nmbr FROM
(SELECT acnt_nmbr, Tel1, Tel2
FROM Customer
UNION
SELECT acnt_nmbr, cTel1, cTel2
FROM Contact) x
WHERE
replace(x.Tel1,' ','') = #Tel
or
replace(x.Tel2,' ','') = #Tel
)
(I'll leave you to work out how to add the Tel3 :)
Note that I have changed your full outer join to a simple left join because the code as presented will not work with customerless contacts.
If you really need a full outer join, the code becomes a bit more convoluted:
SELECT z.* FROM
( select #TEL as calling,c.Tel1,c.Tel2,c.contact as "Primary",
COALESCE(c.acnt_nmbr,a.acnt_nmbr) AS AccountNo,
c.Acnt_name, a.contact,a.cTel1 as telephone1,a.cTel2 as telephone2
from
Customer c
full outer join Contact a on (c.acnt_nmbr=a.acnt_nmbr)
) z
WHERE COALESCE(z.AccountNo,0) IN
(...)
I have modified Christophe's DB-fiddle to show this working.
Note that the solution for the full outer join is slightly different in the fiddle, to cope with the different behaviour of Microsoft SQL server compared to the Advantage database server. (With ADS DBF tables, NULL values in an integer field become zeros in the UNION for example).
Related
Hey, I have a question regarding an "xor" in SQL
I need to know if a value if one column matches one of the two given values but not both, for example, need to return the names of those who match Dollar or Euro, but not both, they can match other currencies.
Name | Currency
---------------
John | Dollar
John | Euro
John | Dollar
Peter| Euro
Peter| Pound
Bob | Dollar
In this example I need to return both Peter and Bob.
I have tried symmetric difference, the union of those who have dollars and euros minus the intersection, but it is a big chunk of code and not working properly. Thanks!
here is one way , using group by :
SELECT *
FROM curenncyname c1
WHERE NOT EXISTS (
SELECT name , Currency, COUNT(*)
FROM urenncyname c2
WHERE c2.name = c1.name
GROUP BY name , Currency
HAVING COUNT(*) > 1
)
You can use aggregation:
SELECT name
FROM t
WHERE currency IN ('Dollar', 'Euro')
GROUP BY name
HAVING MIN(currency) = MAX(currency);
Of, if you like, you can use set operators:
select name
from t
where currency in ('Dollar', 'Euro')
minus
(select name
from t
where currency in ('Dollar')
intersect
select name
from t
where currency in ('Euro')
);
Another way to use it is to use a FULL JOIN with an appropriate filter in the WHERE clause:
SELECT COALESCE(d.NAME, e.NAME)
FROM (SELECT * FROM SOME_TABLE WHERE CURRENCY = 'Dollar') d
FULL OUTER JOIN (SELECT * FROM SOME_TABLE WHERE CURRENCY = 'Euro') e
ON e.Name = d.Name
WHERE (e.Name IS NULL AND d.Name IS NOT NULL) OR
(e.Name IS NOT NULL AND d.Name IS NULL)
This returns
Peter
Bob
db<>fiddle here
TABLE A: Pre-joined table - Holds a list of providers who belong to a group and the group the provider belongs to. Columns are something like this:
ProviderID (PK, FK) | ProviderName | GroupID | GroupName
1234 | LocalDoctor | 987 | LocalDoctorsUnited
5678 | Physican82 | 987 | LocalDoctorsUnited
9012 | Dentist13 | 153 | DentistryToday
0506 | EyeSpecial | 759 | OphtaSpecialist
TABLE B: Another pre-joined table, holds a list of providers and their demographic information. Columns as such:
ProviderID (PK,FK) | ProviderName | G_or_I | OtherColumnsThatArentInUse
1234 | LocalDoctor | G | Etc.
5678 | Physican82 | G | Etc.
9012 | Dentist13 | I | Etc.
0506 | EyeSpecial | I | Etc.
The expected result is something like this:
ProviderID | ProviderName | ProviderStatus | GroupCount
1234 | LocalDoctor | Group | 2
5678 | Physican82 | Group | 2
9012 | Dentist13 | Individual | N/A
0506 | EyeSpecial | Individual | N/A
The goal is to determine whether or not a provider belongs to a group or operates individually, by the G_or_I column. If the provider belongs to a group, I need to include an additional column that provides the count of total providers in that group.
The Group/Individual portion is relatively easy - I've done something like this:
SELECT DISTINCT
A.ProviderID,
A.ProviderName,
CASE
WHEN B.G_or_I = 'G'
THEN 'Group'
WHEN B.G_or_I = 'I'
THEN 'Individual' END AS ProviderStatus
FROM
TableA A
LEFT OUTER JOIN TableB B
ON A.ProviderID = B.ProviderID;
So far so good, this returns the expected results based on the G_or_I flag.
However, I can't seem to wrap my head around how to complete the COUNT portion. I feel like I may be overthinking it, and stuck in a loop of errors. Some things I've tried:
Add a second CASE STATEMENT:
CASE
WHEN B.G_or_I = 'G'
THEN (
SELECT CountedGroups
FROM (
SELECT ProviderID, count(GroupID) AS CountedGroups
FROM TableA
WHERE A.ProviderID = B.ProviderID
GROUP BY ProviderID --originally had this as ORDER BY, but that was a mis-type on my part
)
)
ELSE 'N/A' END
This returns an error stating that a single row sub-query is returning more than one row. If I limit the number of rows returned to 1, the CountedGroups column returns 1 for every row. This makes me think that its not performing the count function as I expect it to.
I've also tried including a direct count of TableA as a factored sub-query:
WITH CountedGroups AS
( SELECT Provider ID, count(GroupID) As GroupSum
FROM TableA
GROUP BY ProviderID --originally had this as ORDER BY, but that was a mis-type on my part
) --This as a standalone query works just fine
SELECT DISTINCT
A.ProviderID,
A.ProviderName,
CASE
WHEN B.G_or_I = 'G'
THEN 'Group'
WHEN B.G_or_I = 'I'
THEN 'Individual' END AS ProviderStatus,
CASE
WHEN B.G_or_I = 'G'
THEN GroupSum
ELSE 'N/A' END
FROM
CountedGroups CG
JOIN TableA A
ON CG.ProviderID = A.ProviderID
LEFT OUTER JOIN TableB
ON A.ProviderID = B.ProviderID
This returns either null or completely incorrect column values
Other attempts have been a number of variations of this, with a mix of bad results or Oracle errors. As I mentioned above, I'm probably way overthinking it and the solution could be rather simple. Apologies if the information is confusing or I've not provided enough detail. The real tables have a lot of private medical information, and I tried to translate the essence of the issue as best I could.
Thank you.
You can use the CASE..WHEN and analytical function COUNT as follows:
SELECT
A.PROVIDERID,
A.PROVIDERNAME,
CASE
WHEN B.G_OR_I = 'G' THEN 'Group'
ELSE 'Individual'
END AS PROVIDERSTATUS,
CASE
WHEN B.G_OR_I = 'G' THEN TO_CHAR(COUNT(1) OVER(
PARTITION BY A.GROUPID
))
ELSE 'N/A'
END AS GROUPCOUNT
FROM
TABLE_A A
JOIN TABLE_B B ON A.PROVIDERID = B.PROVIDERID;
TO_CHAR is needed on COUNT as output expression must be of the same data type in CASE..WHEN
Your problem seems to be that you are missing a column. You need to add group name, otherwise you won't be able to differentiate rows for the same practitioner who works under multiple business entities (groups). This is probably why you have a DISTINCT on your query. Things looked like duplicates which weren't. Once you've done that, just use an analytic function to figure out the rest:
SELECT ta.providerid,
ta.providername,
DECODE(tb.g_or_i, 'G', 'Group', 'I', 'Individual') AS ProviderStatus,
ta.group_name,
CASE
WHEN tb.g_or_i = 'G' THEN COUNT(DISTINCT ta.provider_id) OVER (PARTITION BY ta.group_id)
ELSE 'N/A'
END AS GROUP_COUNT
FROM table_a ta
INNER JOIN table_b tb ON ta.providerid = tb.providerid
Is it possible that your LEFT JOIN was going the wrong direction? It makes more sense that your base demographic table would have all practitioners in it and then the Group table might be missing some records. For instance if the solo prac was operating under their own SSN and Type I NPI without applying for a separate Type II NPI or TIN.
EXPLAINATION
Imagine that here are 3 companies. We are joining tables by Name, that because not every employee have provided his PersonalNo. StringId have only specialists, so It can't be used for joining too. The same employee can work in multiple companies.
PROBLEM
Problem is that there can be different employees with the same name (with equal first and last names, in example provided only first name).
WHAT I NEED?
Return 1 when there are any problems with data and 0 If It is correct.
RULES TO DETECT PROBLEM
When have multiple equal names (2 or more) and all have equal PersonalNo and not all have StringId (as Peter) should return 1 (It is wrong)
When have multiple equal names (2 or more) and there are NULL (see John), but all of them have the same StringId It should return 0 (It's correct, that means one of companies not provided PersonalNo)
When have multiple equal names (2 or more) and all PersonalNo are equal and all StringId are equall (see at Lisa) It should return 0 (correct)
When have multiple equal names (2 or more) and there are multiple different PersonalNo and all StringId provided It should be like: We see that here are 2 different people Jennifer with 4805250141 PersonalNo and Jennifer with 4920225088 PersonalNo, Jennifer with NULL PersonalNo have the same StringId as Jennifer with 4920225088 PersonalNo so It should return 0 (correct) and Jennifer with 4805250141 PersonalNo shouldn't be selected, because have StringID and have only 1 row with the same PersonalNo.
If there is only 1 row and there is no provided StringId It shouldn't appear in select at all.
SAMPLE DATA
Company Name PersonalNo StringId
Comp1 Peter 3850342515 85426 -------------------------------------------------------------------
Comp2 Peter 3850342515 '' -- If have the same PersonalNo and there is no StringId - 1 (wrong)
Comp1 John NULL 12345 ------------------------------------------------------------------
Comp2 John 3952525252 12345 -- If have the same StringId and 1 PersonalNo is NULL - 0 (correct)
Comp1 Lisa 4951212581 52124 ----------------------------------------------------------------
Comp3 Lisa 4951212581 52124 -- If PersonalNo are equal and StringId are equal - 0 (correct)
Comp1 Jennifer 4805250141 '' -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Comp1 Jennifer 4920225088 55443 -- If have 2 different PersonalNo and NULL PersonalNo, but where PersonalNo is NULL
Comp3 Jennifer NULL 55443 -- Have the same StringId with other row where is provided PersonalNo it should be 0 (correct), with different PersonalNo where is no StringId shouldn't appear at all.
Comp1 Ralph 3961212256 '' -- Shouldn't appear in select list, because only 1 row with this PersonalNo and there is no StringID
DESIRED OUTPUT
Peter 1
John 0
Lisa 0
Jennifer 0
QUERY
LEFT JOIN (SELECT Name,
(
SELECT CASE WHEN MIN(PersonalNo) <> MAX(d.PersonalNo)
and MIN(CASE WHEN StringId IS NULL THEN '0' ELSE StringId END) <> MAX(CASE WHEN d.StringId IS NULL THEN '0' ELSE d.StringId END) -- this is wrong
and MIN(PersonalNo) <> ''
and MIN(PersonalNo) IS NOT NULL
and MAX(rn) > 1 THEN 1
ELSE 0
END AS CheckPersonalNo
FROM (
SELECT Name, PersonalNo, [StringId], ROW_NUMBER() OVER (PARTITION BY Name, PersonalNo ORDER BY Name) rn
FROM TableEmp e1
WHERE Condition = 1 and e1.Name = d.Name
) sub2
GROUP BY Name
) CheckPersonalNo
FROM [TableEmp] d
WHERE Condition = 1
GROUP BY Name
) f ON f.Name = x.Name
Problem with query is that I can group by Name only, can't add PersonalNo to GROUP BY clause, so I need to use aggregates in select list. But for now It comparing only MIN and MAX values, If there is more than 2 rows with the same name It not working as expected.
I need to do something like, compare values by PARTITION BY Fullname, PersonalNo. It for now comparing values with the same Name (not depending to PersonalNo).
Any ideas? If you have any questions - ask me, I will try to explain.
UPDATE 1
If there is 2 entries with different PersonalNo, but their StringId are equal, It should be 1 (wrong).
Company Name PersonalNo StringId
Comp1 Anna 4805250141 88552 -- different PersonalNo and the same StringId for both should go as 1 (wrong)
Comp1 Anna 4920225088 88552
For now It returning like:
Anna 0
Anna 0
It should be:
Anna 1
UPDATE 2
After you UNION update in Identifier column returns StringId: 55443 (for data below), but in this case when 1 entry have PersonalNo, other is blank, but both of them have the same (equal) StringId It is correct (should be 0)
Comp1 Jennifer 4920225088 55443
Comp3 Jennifer '' 55443
i hope i have understood your requirements..
There maybe other ways of doing this but personally i would probably use temp tables for interim working if it was me doing it..
--select data into a temp table that can be modified
select
*
into #cleaned
from
table
--apply personal numbers based on other records with matching string id
--you could take note of the records you are doing this to for data clean up
update c
set c.personalNo = s.personalNo
from #cleaned as c
inner join table as s
on c.name = s.name
and c.stringID = s.stringID
and c.personalNo is null
and s.personalNo is not null
--find all records with non matching string ids
select
name
,PersonalNo
,count(*) as numIDs
into #issues
from(
select
name
,PersonalNo
,stringID
from
#cleaned
group by
name
,PersonalNo
,stringID
) as i
group by
name
,PersonalNo
having
count(*) > 1
--select data for viewing.
select
distinct
s.name
,case
when i.name is not null then 1
else 0
end as issue
from
#cleaned as s
left outer join #issues as i
on s.name = i.name
and s.personalNo = i.personalNo
order by issue desc
SQLFiddle: http://sqlfiddle.com/#!3/f4aab/7
sorry if there are bugs in here but i am sure you will get the idea, its not rocket science, just another approach
EDIT: just noticed you are interested in rows with no string ID.. just if its the only row then it is not an issue. i modified first select (into #cleaned) to take all rows.
EDIT: NO Temp Tables now you know what its doing, here is the same thing without any temp tables - but WARNING this updates the source tables to assign missing personalNo's
update c
set c.personalNo = s.personalNo
from table1 as c
inner join table1 as s
on c.name = s.name
and c.stringID = s.stringID
and c.personalNo is null
and s.personalNo is not null
select
distinct
s.name
,case
when i.name is not null then 1
else 0
end as issue
from
table1 as s
left outer join (
select
name
,PersonalNo
,count(*) as numIDs
from(
select
name
,PersonalNo
,stringID
from
table1
group by
name
,PersonalNo
,stringID
) as i
group by
name
,PersonalNo
having
count(*) > 1
)
as i
on s.name = i.name
and s.personalNo = i.personalNo
order by issue desc
SQLFiddle: http://sqlfiddle.com/#!3/f4aab/8
PARITIONING i dont see how i would use partitioning here as what you want to do is only know if there is more than one row, i use partitioning from more complex tabulation or if i was going to rank the results of make judgement calls on updating data based on more complex rules.. but in any case here is a crow barred in partition by :D
Select
name
,personalNo
,case
when numstrings > 1 then 1
else 0 end as issue
from
(select
name
,personalNo
,row_number() over (partition by
name
,personalNo
order by
name
,personalNo
,stringID
) as numstrings
from
#cleaned
group by
name
,personalNo
,stringid) as d
order by
issue desc
NOTE: this uses the #cleaned table as above, as i believe that the crux of what makes this hard is the sometimes missing personalNo's.
No temp tables, no updates
Working with above its obviously possible to do without any temp tables or updating anything, its just a question of readability / maintainability and also whether or not it is actually even any faster. this could be made more stable to handle string ids with multiple personalNo's assigned:
select
distinct
s.name
,case
when i.name is not null then 1
else 0
end as issue
from
table1 as s
left outer join (
select
name
,PersonalNo
,count(*) as numIDs
from(
select
a.name
,coalesce(a.PersonalNo,b.PersonalNo) as PersonalNo
,a.stringID
from
table1 as a
left outer join table1 as b
on a.name = b.name
and a.stringid=b.stringid
and a.personalNo != b.personalNo
and b.personalNo Is Not Null
group by
a.name
,a.PersonalNo
,a.stringID
,b.PersonalNo
) as i
group by
name
,PersonalNo
having
count(*) > 1
)
as i
on s.name = i.name
and s.personalNo = i.personalNo
order by issue desc
SQLFiddle: http://sqlfiddle.com/#!3/f4aab/9
EDIT: Looking for inconsistent personal numbers too - this uses one temp table, but you could swap it out as done in last example.. NOTE there is a slight deviation from the original structure you asked for as this is more how i would do this task, but there is more than enough code here for you to re-jig any way you want.
--select data into a temp table that can be modified
select
*
into #cleaned
from
table1
--apply personal numbers based on other records with matching string id
--you could take note of the records you are doing this to for data clean up
update c
set c.personalNo = s.personalNo
from #cleaned as c
inner join table1 as s
on c.name = s.name
and c.stringID = s.stringID
and c.personalNo is null
and s.personalNo is not null
Select
IssueType
,Name
,Identifier
from
(
--find all records with non matching PersonalNos
select
name
,cast('StringID: ' + stringID as nvarchar(400)) as Identifier
,cast('Inconsistent PersonalNo' as nvarchar(400)) as issueType
from(
select
name
,PersonalNo
,stringID
from
#cleaned
group by
name
,PersonalNo
,stringID
) as i
group by
name
,StringId
having
count(*) > 1
UNION
--find all records with non matching string ids
select
name
,'PersonalNo: ' + PersonalNo
,cast('Inconsistent String ID' as nvarchar(400)) as issueType
from(
select
name
,PersonalNo
,stringID
from
#cleaned
group by
name
,PersonalNo
,stringID
) as i
group by
name
,PersonalNo
having
count(*) > 1
) as a
SQLFiddle: http://sqlfiddle.com/#!3/e9da2/18
UPDATE: also wanting to accept empty string personalNo's
This is another new requirement.. to accept empty strings in teh same way as NULLs in personalNo
--select data into a temp table that can be modified
select
*
into #cleaned
from
table1
--apply personal numbers based on other records with matching string id
--you could take note of the records you are doing this to for data clean up
update c
set c.personalNo = s.personalNo
from #cleaned as c
inner join table1 as s
on c.name = s.name
and c.stringID = s.stringID
and (c.personalNo IS NULL OR c.personalNo ='')
and s.personalNo is not null
and s.personalNo != ''
Select
IssueType
,Name
,Identifier
from
(
--find all records with non matching PersonalNos
select
name
,cast('StringID: ' + stringID as nvarchar(400)) as Identifier
,cast('Inconsistent PersonalNo' as nvarchar(400)) as issueType
from(
select
name
,PersonalNo
,stringID
from
#cleaned
group by
name
,PersonalNo
,stringID
) as i
group by
name
,StringId
having
count(*) > 1
UNION
--find all records with non matching string ids
select
name
,'PersonalNo: ' + PersonalNo
,cast('Inconsistent String ID' as nvarchar(400)) as issueType
from(
select
name
,PersonalNo
,stringID
from
#cleaned
group by
name
,PersonalNo
,stringID
) as i
group by
name
,PersonalNo
having
count(*) > 1
) as a
SQLFiddle: http://sqlfiddle.com/#!3/412127/8
I'm trying to get all phones, emails, and organizations for a person and show it in a flat file format. There should be n number of rows, where n is the max count of organizations, emails, or phones. NULL values will be shown once all values have been shown in the rows, with NULL being the last values. The emails and phones can only have 1 PreferredInd per person. I want these to be on the same row (1 of them can be NULL). I've tried to do this on a more complex query, but couldn't get it to work, so I've started over using this simpler example.
Example tables and values:
#ContactPerson
Id Name
1 John Doe
#ContactEmail
Id PersonId Email PreferredInd
1 1 johndoe#us.gov 0
2 1 jdoe#us.gov 1
3 1 johndoe#gmail.com 0
#ContactPhone
Id PersonId Phone PreferredInd
1 1 888-867-5309 0
2 1 305-476-5234 1
#ContactOrganization
Id PersonId Organization
1 1 US Government
2 1 US Army
I want a resulting set to look like:
Name Organization PreferredInd Email Phone
John Doe US Government 1 jdoe#us.gov 888-867-5309
John Doe US Army 0 johndoe#us.gov 305-467-5234
John Doe NULL 0 johndoe#gmail.com NULL
The complete sql code that I have for this example is here on pastebin. It also includes code to create the sample tables. It works when the count of emails exceeds the count of organizations or phones, but that won't always be true. I can't seem to figure out how to get the result that I'm looking for. The actual tables I'm working with can have 0 or infinity emails, phones, or organizations per person. There will also be many more values, but I can fix that myself.
Can you help me fix my query or show me a simpler way to do it? If you have any questions, just let me know and I can try to answer them.
something like this?
with cte_e as (
select
*,
row_number() over(order by PreferredInd desc, Id) as rn
from ContactEmail
), cte_p as (
select
*,
row_number() over(order by PreferredInd desc, Id) as rn
from ContactPhone
), cte_o as (
select
*,
row_number() over(order by Organization) as rn
from ContactOrganization
), cte_d as (
select distinct rn, PersonId from cte_e union
select distinct rn, PersonId from cte_p union
select distinct rn, PersonId from cte_o
)
select
pr.Name, o.Organization, e.Email, p.Phone
from cte_d as d
left outer join ContactPerson as pr on pr.Id = d.PersonId
left outer join cte_e as e on e.PersonId = d.PersonId and e.rn = d.rn
left outer join cte_p as p on p.PersonId = d.PersonId and p.rn = d.rn
left outer join cte_o as o on o.PersonId = d.PersonId and o.rn = d.rn
sql fiddle demo
it's a bit clumsy, I can think of couple of other possible ways to do this, but I think this one is most readable one
Step 1
Write a query that does the full join of all the tables, which will end up with lots of duplicate rows for each person (for each email or phone number)
Step 2
Write a second query that uses GroupBy to group the rows, and that uses the Case or Decode keywords (like a c# switch statement) to find the preferred row value and select it as the value to display
I have a table with Name and Area.
I want to run a query that returns that table with the addition of a 'Special_COUNT'
The count returns 1 on the firstmost Concat(Distinct(Name,Area); should return 0 otherwise
For example:
Name | Area | Special_COUNT |
ABCD | US | 1 |
ABCD | US | 0 |*Same Name/Region no value
ABCD | Asia | 1 |*New Region
ABCDX | Asia | 1 |*New Name
How can I get the above 'Special_COUNT' column into the results of my query?
MySQL doesn't have any ranking functionality - you'll want to read this article for setting up the variables for LEAD functionality in order to number the output correctly.
Use:
SELECT x.name,
x.area,
x.special_count
FROM (SELECT t.name,
t.area,
CASE WHEN #name = t.name AND #area = t.area THEN 0 ELSE 1 END AS special_count,
#name := t.name,
#area := t.area
FROM TABLE t,
(SELECT #area := NULL, #name := NULL) r) x
Tested on 4.1 - results match, thanks for supplying test data.
Thanks!
Your code looks so simple, alas I am not able to work this into a larger query (apologies for not providing an example query so that you could show me how this would work, below I have done that for you in the hopes that you can explain by example)
So given this original query below how would I revise it to incorporate your data with the goal of obtaining a 'special_count' on allanimalnames.AnimalName per location.Area
I see the light at the end of the tunnel...
SELECT
CASE allanimalnames.animalID
WHEN allanimalnames.animalID = 1 THEN "Dangerous!"
WHEN allanimalnames.animalID <> 1 THEN "Cuddly"
END AS 'Animal Danger Levell,
allanimalsizes.SizeInFeet AS 'Length of the Animal',
allanimaltypes.AnimalTypeName AS 'Carnivor or Herbavore?',
location.Area AS 'Region this Animal is found in',
allanimalnames.AnimalName AS 'Name of this animal,
MAX(report.reportdate) AS 'Last Reported'
FROM
allanimalnames
INNER JOIN allanimaltypes ON allanimaltypes.atypeid = allanimalnames.atypeid
INNER JOIN allanimalsizes ON allanimalsizes.sizeid = allanimalnames.sizeid
INNER JOIN location ON location.locid = allanimalnames.locid
INNER JOIN report ON report.reportid = allanimalnames.reportid
WHERE
report.reportdate
BETWEEN '2008-01-01' AND '2009-01-01'
GROUP BY
allanimalsizes.SizeInFeet
allanimaltypes.AnimalTypeName
location.Area
allanimalnames.AnimalName
report.reportdate