SQL join - join first no zero element if possible - sql

I have a data structure something like this:
Company:
ID Name
1 A
2 B
3 C
4 D
5 E
Contact
ID CompanyID Phone
1 1 (12)111
2 1 (12)222
3 2 NULL
4 2 (12)333
5 3 NULL
6 5 (12)444
I need to match company with its phone - one record for company. In 70% of cases in my data a company has one contact with one phone and this is easy. But sometimes there are more contacts and some phones are null.
So my logic would be:
- if company has more than 1 contact take 1st not null phone (by ID)
- if it is not possible then phone=null
So results of my query would be
A (12)111
B (12)333
C NULL
D NULL
E (12)444
It's probably easy but I got stuck with it ;) Thanks for any help

select a.[Name], min(b.[Phone])
from Company a
JOIN Contact b
on a.ID = b.CompanyID
then, optionals, e.g.
WHERE b.[Phone] is not null
then group
GROUP BY a.[Name]

Try the following:
SELECT com.name
,MIN(case when isnull(con.phone, 'null') != 'null' then con.phone end) a
FROM Company com
LEFT JOIN Contact con on com.id=con.companyid
GROUP BY com.name

Related

Filling the NULL parts of the tabels in the left join

I have two tables that have similar structure but I need to obtain all information relative to zip code.
tableA
zip_code location
1 A
2 C
2 D
3 E
4 F
5 G
tableB
zip_code location n
2 A 1
2 C 2
2 D 3
3 A 4
3 E 5
4 F 6
4 H 7
6 Y 8
As you can see, one locatıon can have multiple zip_code. So, I have to use zip_code and location for the join condition. When I applied left join, I couldn't manage to fill NULL parts. My strategy to fill it is like this:
If zip_code in the tableA is not in the tableB, I search for the location names and choose the n based on the minimum difference between zip_codes.
If zip_code and location in the tableA are not in the tableB, I want to search for n based on the minimum difference between zip_codes, and if there are multiple possibilities based on this I want to choose the minimum n.
NOTE: When looking at the difference between zip_codes, if there is a situation where they are equal I want to get the smaller number. For example, 5 could get 4 and 6 in terms of minimum difference but I want to go with 4 and look other conditions then.
The resulting table should be something like this:
zip_code location n
1 A 1
2 C 2
2 D 3
3 E 5
4 F 6
5 G 6
I know it is a bit complicated but I can explain the fuzzy parts with more details
This sounds like an OUTER APPLY:
select a.*, b.n
from tableA a outer apply
(select top (1) b.*
from tableB b
order by (case when b.zip_code = a.zip_code and b.location = a.location
then -1
when b.location = a.location
then abs(b.zip_code - a.zip_code)
else abs(b.zip_code - a.zip_code)
end),
b.n
) b;
Here is a db<>fiddle.

How to get a subset of a table using the count operator

SQL Server 2012.
Each enterprise has one or more teams. Each team can have sponsors or cannot have any sponsors.
Enterprise
Id Name
1 A
2 B
3 C
and the team table:
Team
Id Name EnterpiseId
1 For 1
2 Xor 2
3 Nor 2
4 Xur 1
5 Fir 3
6 Fte 2
and now the table sponsor
Sponsor
id Name TeamId
1 XX1 1
2 FC7 1
3 89U 3
Now I need to know how to present this table that shows only the enterprises that have at least one sponsor.
FINAL TABLE
Id Name
1 A
3 C
The enterprise B has 3 teams, but there are no sponsors for those 3 teams, so I want to show the enterprises that have sponsors which are "A" and "C".
Select A.id, A.name
FROM Enterprise A
LEFT JOIN Team B on A.Id=b.EnterpriseId
INNER JOIN Sponsor C on B.Id=C.TeamId
Where (SELECT COUNT(*) FROM Sponsor S INNER JOIN Team T on T.id=S.TeamId group by T.id)>0
This is not working. I am not used to use subsets which is likely the way to achieve the desired table. Thanks.
You can do this with JOINs. The GROUP BY is just to eliminate duplicates:
SELECT e.id, e.name
FROM Enterprise e JOIN
Team t
ON e.Id = t.EnterpriseId JOIN
Sponsor s
ON t.Id = s.TeamId
GROUP BY e.id, e.name;
The JOIN only matches teams that have sponsors.
If you were looking for more than one, then something like HAVING COUNT(*) > 1 would be called for.

Compare the same column values in a table and then the take their relationship value from other table

I have a table like below:
ID Role CompanyID
1 T 123
2 S 1234
3 B 12345
T's company id has relationship with S's and B's company ids.The value of their relationship is in another table like below:
Rel CompanyId1 CompanyId
Private 1234 123
Public 12345 123
Of course first I have to join the two tables and then I have to get the relationship of T(123) with S(1234) and B(12345) from the table 2 and on the basis of that I have to create another column which will be if the relationship of T with S and B is private then 1 and if public then 0
It should be like this
Id Role Companyid BoolCol
1 T 123 for T there is no need to fill this col can be null
2 S 1234 1 becoz rel b/w T and S is private
3 B 12345 0 becoz rel b/w T and B is public
The relationship is only of T with another companies not the other way around
Any help will be appreciated.......
Thanks
You can join the table using Left join on CompanyId in first table and CompanyId1 in second table and use case when statement to get the BoolCol column.
select a.*,case when b.rel = 'Private' then 1
when b.rel = 'Public' then 0
else NULL
end as BoolCol
from table1 a
left join
table2 b
on a.companyid = b.companyid1;
Let me know in case you of any queries.
you can try this code:
select id, role,a.companyid,rel,case when rel='Private' then 1 when rel='Public' then 0 else null end as boolcol
from first_table a left join relation_table b
on a.companid=b.companyid1

How to select record in 1 to many relation

I have a contact table and contactphone tables.
Each record in contacts can have many records in contactphone but only 1 can be primary at most (primary is Boolean but can be also NULL)
Contacts:
cid name address
1 Jack A
2 Bill B
3 Mor C
4 Jeff D
ContactsPhones:
cpid cid phone primary
1 1 140 True
2 1 150
3 2 170
I need to write a query that returns all contacts with their details, if they have more than 1 phone then present only primary.
Output:
cid name address phone
1 Jack A 140
2 Bill B 170
3 Mor C
4 Jeff D
What is most easiest way to do it?
Note that primary can have (True,False, Null)
This is a bit tricky. I think the easiest method is distinct on:
SELECT DISTINCT ON (cid) cid, name, address, cp.phone
FROM Contacts c LEFT JOIN
ContactsPhones cp
ON c.cid = cp.cpid
ORDER BY cid, (case when primary then 1 else 2 end);
You can use a LEFT JOIN to a derived table that uses ROW_NUMBER to specify more suitable record:
SELECT cid, name, address, phone
FROM Contacts AS c
LEFT JOIN (
SELECT phone, cpid,
ROW_NUMBER() OVER(PARTITION BY cpid
ORDER BY CASE
WHEN primary THEN 0
ELSE 1
END) AS rn
FROM ContactsPhones) AS cp
ON c.cid = cp.cpid AND cp.rn = 1

Group by with two columns

I am trying to write a query using group by in sub query ,I referred lot of blogs but could not get all the values.
I have three tables and below is the structure of those tables.
Pet_Seller_Master
ps_id ps_name city_id
2 abc 1
3 xyz 2
4 fer 4
5 bbb 1
City_Master
city_id city_name
1 Bangalore
2 COIMBATORE
4 MYSORE
Api_Entry
api_id ps_id otp
1 2 yes
2 3
3 2 yes
4 3 yes
5 4
6 5 yes
7 5 yes
8 5 yes
Query is to get number of sellers, no of pet sellers with zero otp, no of pet sellers with 1 otp, no of pet sellers with 2 otp,no of pet sellers with otp>2 for the particular city and within date range.
Through Below query I am able to get city , psp , and zero otp
select cm.city_name,
count(ps.ps_id) as PSP,
((select count(ps1.ps_id)
FROM ps_master ps1
WHERE ps1.city = cm.city_id)-
(SELECT count(distinct ps1.ps_id)
from ps_master ps1
INNER JOIN api_entry ae ON ps1.ps_id = ae.ps_id and otp!=''
WHERE ps1.city = cm.city_id and date(timestamp) >= curdate() - INTERVAL DAYOFWEEK(curdate())+6 DAY AND date(timestamp) < curdate())) as zero_psp
from ps_master ps INNER JOIN city_master cm ON ps.city = cm.city_id and cm.city_type = 'IN HOUSE PNS'
group by city_id
Please tell me the solution to solve this query.
Thanks in advance
It's not hard to do and you were on a right track. Here is what I would use:
select c.city_name, a.otp, p.ps_name, COUNT(*) nbr
from Api_Entry a
inner join Pet_Seller_Master p on p.ps_id=a.ps_id
inner join City_Master c on p.city_id=c.city_id
group by c.city_name, a.otp, p.ps_name
Now, if you want to get the number of sellers with zero otp, you just apply where clause:
where otp <> 'yes'
If you want to get the number of pet sellers with otp>2, then you just use subquery:
select *
from (
select c.city_name, a.otp, p.ps_name, COUNT(*) nbr
from #tempA a
inner join #tempP p on p.ps_id=a.ps_id
inner join #tempC c on p.city_id=c.city_id
group by c.city_name, a.otp, p.ps_name
) g
where nbr > 2