SQL results in one row - sql

I have following statement:
SELECT
Person.PersonID, Person.PersonName, Person.Surname,
Address.Street, Address.PostCode,
(SELECT Phones.Num
WHERE (Phones.CommunicationTypeId = '78')) AS email,
(SELECT Phones.Num
WHERE (Phones.CommunicationTypeId = '83')) AS mobile,
(SELECT Phones.Num
WHERE (Phones.CommunicationTypeId = '88')) AS phone
FROM
Address
RIGHT OUTER JOIN
Person ON Address.ObjectId = Person.PersonID
RIGHT OUTER JOIN
Phones ON Person.PersonID = Phones.ObjectID
Now results shows separate rows, while I want to have it in one row:
How to consolidate it into just one row?

Looks like your person has more than one corresponding phone records (I guess 2 records with different types.
SELECT
ObjectID,
MAX(CASE WHEN CommunicationTypeId = '78' THEN Num ELSE null END) as email,
MAX(CASE WHEN CommunicationTypeId = '83' THEN Num ELSE null END) as mobile,
MAX(CASE WHEN CommunicationTypeId = '88' THEN Num ELSE null END) as phone
FROM Phones
GROUP BY ObjectID
The select above returns 3 columns for each person and you can use it in the main query
SELECT
Person.PersonID, Person.PersonName, Person.Surname,
Address.Street, Address.PostCode,
Phones.email, Phones mobile, Phones.phone
FROM
Address
RIGHT OUTER JOIN
Person ON Address.ObjectId = Person.PersonID
RIGHT OUTER JOIN
(sub query above) Phones ON Person.PersonID = Phones.ObjectID

Related

Row to column in SQL Server with dynamic number of rows

My source data looks something like this
ID Phone Type Phone Number
-------------------------------------
308820 P 8136542273
308820 F 8136541384
308820 P 8139108555
308820 P 8136842229
308820 F 8139108655
211111 P 6874598695
456788 F 5687659867
In the above data, Phone type = P is phone and F is fax.
I need to sort the data and then pick only one F type and one P type phone number and populate the data as below
ID Fax Number Phone Number
-------------------------------------
308820 8136541384 8136542273
211111 6874598695
456788 5687659867
Can someone help me out how to achieve this. There can be n number of Phone Numbers and Fax numbers against one ID but I need to sort it and pick the first.
You can use conditional aggregation:
select id, max(case when phone_type = 'F' then phone_number end) as fax,
max(case when phone_type = 'P' then phone_number end) as phone
from t
group by id;
Guys thanks for the response. The query which I created is below. The help I got above was used in my query below. However the problem is that in the Binary_CheckSum it randomly picks up a Phone number and as such again selects the same record.
What I am trying to find is that for a particular ID and AddressID combination has the iSActive flag or Phone Number or Fax number has changed so that I need to select that record and insert it in PrescriberLocation table.
select TESTID, ADDRESSID, max(PhoneNumber) as PhoneNumber,max(FaxNumber) as FaxNumber,'1' as isactive from (select distinct a.TESTID
, b.AddressId , CASE WHEN c.PhoneType = 'P'THEN C.PhoneNumber END PhoneNumber
, CASE WHEN c.PhoneType = 'F'THEN C.PhoneNumber END FaxNumber
,'1' as isactive from stg_Address a inner join stg_AddressPhone c on a.TESTID = c.TESTID and a.AddressID = c.AddressID INNER join pbmsys_new.dbo.sc_Address b on
upper(a.Address1) = upper(b.Address1) and upper(isnull(a.Address2,'')) = upper(b.Address2) join pbmsys_new.[dbo].[dr_PrescriberLocation] d
on a.TESTID = d.TESTID and b.AddressId = d.AddressId
where BINARY_CHECKSUM(1,c.PhoneNumber, FaxNumber) != BINARY_CHECKSUM(d.IsActive,d.PhoneNumber,d.FaxNumber) and d.PrescriberLocationId = SELECT max(Z.PrescriberLocationId) as PrescriberLocationId FROM pbmsys_new.dbo.dr_PrescriberLocation Z where d.TESTID = z.TESTID and d.AddressId =z.AddressId))f group by TESTID, AddressId
you can use aggregate function and sub-query
select id,max(fax_Number) as fax_Number, max(phone_Number) as phone_Number
from
(select id, case when phone_type = 'F' then phone_number end as fax_Number,
case when phone_type = 'P' then phone_number end as phone_Number
from yourtable
) as t group by id
If the requirement is to pick first value and not the max value then you can use it.
; with CTE1
As
(
select row_number() over (partition by id,phonetype order by ID) as Num,ID,
case when phonetype = 'F' then PhoneNumber end as FaxNumber,
case when phonetype = 'P' then PhoneNumber end as PhoneNumber
from ContactDetails
)
select ID,max(FaxNumber),max(PhoneNumber)
from (select ID,FaxNumber,PhoneNumber from CTE1 where num = 1)as T2
group by ID

SQL Where in for Many to Many Join Table

I have the following (moderately epic query) which I have been writing
Select *
from
(Select
Salutation,
FirstName, LastName, FullName,
PhotoUrl, CountryCode, Email, Birthday,
Gender, HomePhone, M.MemberId, IDType, JoinDate,
HomeLocation, HomeLocationId,
Region.Name as RegionName,
M.MembershipId,
coalesce(case
when Package.PackageIsReoccuring = 1 then 'Recurring'
when Package.PackageIsSession = 1 then 'Paid In Full'
when membership.TotalPrice = 0 then 'Free'
when Package.PackagePayInFull = 1 then 'Paid In Full'
end, 'N/A') as PackageTerm,
coalesce(PackageType.Name, 'N/A') as PackageType,
coalesce(membershipstate.name, 'N/A') as MembershipState,
MembershipStartDate =
case
when membership.StartDate IS NULL
then ''
else CONVERT(varchar(50),membership.StartDate)
end,
MembershipEndDate =
case
when membership.EndDate IS NULL
then ''
else CONVERT(varchar(50),membership.EndDate)
end,
Region.Id as RegionId
from
(select
AspNetUsers.Salutation,
AspNetUsers.FirstName, AspNetUsers.LastName,
CONCAT (AspNetUsers.FirstName, ' ', AspNetUsers.LastName) as FullName,
AspNetUsers.PhotoUrl, AspNetUsers.CountryCode, AspNetUsers.Email,
AspNetUsers.Birthday, AspNetUsers.Gender,
AspNetUsers.HomePhone as HomePhone,
Member.Id as MemberId, Member.IDType, Member.JoinDate,
HomeLocation.Name as HomeLocation,
HomeLocation.Id as HomeLocationId,
(case when (select top 1 id from membership where membership.memberid = Member.id and (membership.membershipstateid = 1 or membership.membershipstateid = 6)) IS NULL Then (select top 1 id from membership where membership.memberid = Member.id order by membership.enddate desc) ELSE (select top 1 id from membership where membership.memberid = Member.id and (membership.membershipstateid = 1 or membership.membershipstateid = 6)) END) as MembershipId
from
AspNetUsers
join
Member on AspNetUsers.id = Member.aspnetuserid
join
Location as HomeLocation on Member.HomeLocationId = HomeLocation.id) as M
left join
Membership on M.MembershipId = Membership.Id
left join
Package on Membership.packageid = Package.Id
left join
PackageType on Package.packagetypeid = PackageType.Id
left join
MembershipState on Membership.membershipstateid = MembershipState.Id
left join
Region on Membership.RegionId = Region.Id) as Result
order by
Result.LastName desc
I have a final join table which I want to use which is a many-to-many relationship on Region. Region has a Join Table (RegionLocations) which is a join between Region and Locations.
With my query below I would like to get all results where the HomeLocationId = 2 OR he has a LocationId (from RegionLocations) which also contains 2. The RegionId is a nullable value and isn't always populated.
How can I get this? Do I need to return values into a CSV? This final hurdle is a battle..
Thanks
You could extend this:
left join
Region on Membership.RegionId = Region.Id) as Result
to this:
left join
Region on Membership.RegionId = Region.Id
where M.HomeLocationId = 2
or Region.Id in (select RegionId from RegionLocation where LocationId = 2)
) as Result
Some other remarks about your query:
The fields MembershipStartDate and MembershipEndDate can be evaluated more concisely as:
COALESCE(CONVERT(varchar(50),membership.StartDate), '') as MembershipStartDate,
COALESCE(CONVERT(varchar(50),membership.EndDate), '') as MembershipEndDate,
The inner field MembershipId is defined with three sub-queries in a case when, which can be shortened to just one query. It uses in instead of an or condition, and puts it in the order by clause in a way that gets the priority right:
(select top 1 id
from membership
where membership.memberid = Member.id
order by case when membership.membershipstateid in (1,6) then 0 else 1 end,
membership.enddate desc
) as MembershipId
Finally, if you just have an outer query that performs a select * from (...) order by, then why not skip that outer query and perform that order by on the inner query direcly?

How could I form a tsql query with 'and' and 'not' for a result

I have the following question:
For each city display the number of clients who only rented cars of type 'Toyota' or 'BMW' and never rented 'Mercedes'
The tables are as follows:
Car [CarId int, type varchar(30)]
Client [ClientId int, CityId int, Name varchar(30)]
City [CityId int, CityName varchar(30)]
Rent [CarId int, ClientId int, days_number int]
I don't know how would I formulate this query I tried hard but nothing worked until now.
select city.cityname, count(*) as cnt
from client
join city
on client.cityId = city.cityId
where exists(select * from rent join car on rent.carid = car.carid
where client.clientid = rent.clientid
and car.[type] in ('Toyota', 'BMW'))
and not exists(select * from rent join car on rent.carid = car.carid
where client.clientid = rent.clientid
and car.[type] = 'Mercedes')
group by city.cityname
Please take a look at this query:
select
City.CityName,
count(1) as NumberOfClients
from
City
inner join Client on City.CityID = Client.CityID
inner join
(
select Rent.ClientID
from
Rent
inner join Car on Rent.CarID = Car.CarID
and Car.type in ('Toyota','BMW','Mercedes')
group by Rent.ClientID
having sum(case when Car.type in ('Toyota','BMW') then 1 else 0 end) > 0
and sum(case when Car.type in ('Mercedes') then 1 else 0 end) = 0
) as Summary
on Client.ClientID = Summary.ClientID
group by
City.CityID,
City.CityName
What it does is:
In subquery (named Summary) all distinct clients are selected, basing on a condition that those clients have rented 'Toyota' or 'BMW' at least once, while never have rented 'Mercedes'.
The outer query takes the results of the subquery and calculates totals for each city.
This can probably be done more efficiently - but this is first that comes to my mind.
Declare #count1 int, #count2 int
Select #count1 = Count(*)
From Client inner join Rent
on Client.ClientId = Rent.ClientId
inner join Car
on Car.CarId = Rent.CarId
Where Car.type In( 'Toyota','BMW')
--except
Select #count2 = Count(*)
From Client inner join Rent
on Client.ClientId = Rent.ClientId
inner join Car
on Car.CarId = Rent.CarId
Where Car.type = 'Merccedes'
Select (#count1 - #count2)

Same values being returned for multiple rows in sql but not all columns

i'm currently running the following query (see below.)
However when i do the same values is returned for multiple rows in totalusers, activeusers and suspendedusers.
However when it comes to total login the values are unique.
Is their a reason why this could be happening and is their a way to solve the problem. If it helps im using the tool sql workben with postgre sql driver.
Cheers
SELECT
company.companyStatus,
company.CompanyId,
company.companyName,
select
count(distinct UserID)
From Users
inner join company
on Users.CompanyID = Company.CompanyId
where Users.Companyid = company.Companyid
as TotalUsers,
select sum(case when userstatusid =2 then 1 else 0 end)
from users
inner join company
on users.companyid = company.companyid
where users.companyid = company.companyid)
as ActiveUsers,
select sum(case when userstatusid = 3 then 1 else 0 end)
from users
inner join company
on users.companyid = company.companyid
where users.companyid = company.companyid)
as SuspendedUsers,
(Select COUNT (distinct usersessionid)
From UserSession
inner join users
on usersession.UserID=users.UserID
where usersession.UserID=users.UserID
and users.companyid= company.CompanyID)
as TotalLogin,
from Company
Its because your TotalUsers, ActiveUsers and SuspendedUsers queries are all using their own (unrestricted) copy of the Company table, whereas your TotalLogin is using the main instance from which you're selecting. This means that the TotalLogin numbers you're seeing are for that particular company, but the other fields are across ALL companies.
Presumably you wanted something more like:
SELECT
company.companyStatus,
company.CompanyId,
company.companyName,
count(distinct u.UserID) TotalUsers,
sum(case when u.userstatusid =2 then 1 else 0 end) ActiveUsers,
sum(case when u.userstatusid = 3 then 1 else 0 end) SuspendedUsers,
count(distinct u.usersessionid) TotalLogin
from Company
inner join Users on Users.CompanyID = Company.CompanyId
The reason is because you have company in the subqueries for those calculations.
I much prefer having table references in the from clause where possible, and you can write this query moving everything to the from clause:
SELECT c.companyStatus, c.CompanyId, c.companyName,
uc.Totalusers, uc.Activeusers, uc.Suspendedusers, ucs.TotalLogin
from Company c left outer join
(select u.companyid,
COUNT(distinct userid) as Totalusers,
SUM(case when userstatusid = 2 then 1 else 0 end) as ActiveUsers,
sum(case when userstatusid = 3 then 1 else 0 end) as Suspendedusers
from users u
group by u.companyid
) uc
uc.companyid = c.companyId left outer join
(select u.companyid, COUNT(distinct usersessionid) as TotalLogin
from UserSession us inner join
users u
on us.UserID = u.UserID
) ucs
on ucs.companyid = c.companyid;
This should also speed up the query because it doesn't have to do the same work multiple times.

comparing fields in 2 different tables using SQL

I would like to compare if the address fields in contact table are different to that of the delivery table.
SELECT contactID, addressline1
FROM contact
where contactID = '0018319'
Below is the delivery table which contains the old details.
SELECT contactID, addressline1
FROM delivery
where contactID = '0018319'
SELECT contactID, d.addressline1, c.addressline1
FROM delivery d
INNER JOIN contact c on d.contactID = c.contactID
where d.addressline1 != c.addressline1
If you want to return a flag, then you would use case in the select statement:
select contactId,
(case when d.addressline1 = c.addressline1 or d.addressline1 is null and c.addressline1 is null
then 'SAME'
else 'DIFFERENT'
end) as SameOrDifferent
from contact c join
delivery d
on c.contactId = d.contactId
where contactId = '0018319';
This is going to compare each address in the two tables.
If you want to know if all are the same, then the query is more complicated. The idea is to do a full outer join between the two table (for the given contractid) on addressline1. If all the addresslines match, then the full outer join will never produce NULL values. If any are missing (on either side), then there will be NULL values.
select coalesce(c.contactId, d.contactId) as contactId,
(case when sum(case when c.addressline1 is null or d.addressline1 is null
then 1
else 0
end) = 0
then 'SAME'
else 'DIFFERENT'
end) as SameOrDifferent
from (select c.* from contact c where c.contactId = '0018319') c full outer join
(select d.* from delivery d where d.contactId = '0018319') d
on c.addressline1 = d.addressline1 and
c.contactId = d.contactId -- not actually necessary but useful for multiple contacts
group by coalesce(c.contactId, d.contactId)