SQL SELECT clause with WHERE statement for specific order - sql

I have a through table, doctor_specialties that has a column ordinal that I would like to use in order to create a column named primary_specialty and also secondary_specialty. The logic for primary_specialty is WHERE ordinal == 1.
How can I add the primary_specialty and secondary_specialty columns? One approach would be to use a WHERE statement with the INNER JOIN but I think that would be less efficient?
SELECT pd.name AS "doctor_name",
s.name AS "primary_specialty" WHERE ds.ordinal == 1
FROM doctor_profiles AS dp
INNER JOIN doctor_specialties AS ds on dp.id = ds.doctor_profile_id
INNER JOIN specialties AS s on ds.specialty_id = s.id
Desired output is
name primary_specialty secondary_specialty
Josh Dermatology, Cosmetic Dermatology
Linda Primary Care null

You need to achive this by using case statement. example is shared below
SELECT pd.name AS "doctor_name",
case when ds.ordinal = 1 then s.name end as "primary_specialty",
case when ds.ordinal <> 1 then s.name end as "secondary_specialty"
FROM doctor_profiles AS dp
INNER JOIN doctor_specialties AS ds on dp.id = ds.doctor_profile_id
INNER JOIN specialties AS s on ds.specialty_id = s.id

You can use conditional aggregation:
SELECT dp.name AS "doctor_name",
MAX(CASE WHEN ds.ordinal = 1 THEN s.name END) AS "primary_specialty",
MAX(CASE WHEN ds.ordinal != 2 THEN s.name END) AS "secondary_specialty"
FROM doctor_profiles AS dp
INNER JOIN doctor_specialties AS ds on dp.id = ds.doctor_profile_id
INNER JOIN specialties AS s on ds.specialty_id = s.id
GROUP BY pd.name
You can alter the existing, or use additional MAX aggregates containing CASE expressions, in order to implement the logic for secondary specialties.

Related

Count with several conditions - oracle sql

Im trying to do a count of particular transactions that have specific conditions and cant seem to make it work.Also in the query I select other columns from other tables
for example:
Want to select all transactions
where transactions.ID_1 = transactions.ID_2,
where transaction.dir = "outbound" and transactions.status in ("completed", "processing")
and do a count on this. like:
select
m.ID
,m.Number
,t.Status
,(column that counts of all transactions with the conditions mentioned above)
,p.label
from module m
inner join transactions t on t.ID_1 = m.ID
inner join process p on p.ID = m.ID`
Tried with sum and when and if statement but doesn't work
I consider this is a simple count you can use the next script:
SELECT COUNT(transactions.ID_1)
FROM YOUR_TABLE
WHERE transactions.ID_1 = transactions.ID_2
AND transaction.dir = "xxx"
AND transactions.status in ("a", "b")
Seems like you want to GROUP BY. Use a CASEexpression to do conditional aggregation.
select
m.ID
,m.Number
,t.Status
,SUM(case when transaction.dir = 'outbound'
and transactions.status in ('completed', 'processing') then 1
else 0
end)
,p.label
from module m
inner join transactions t on t.ID_1 = m.ID
inner join process p on p.ID = m.ID
group by m.ID
,m.Number
,t.Status
p.label

How to create distinct count from queries with several tables

I am trying to create one single query that will give me a distinct count for both the ActivityID and the CommentID. My query in MS Access looks like this:
SELECT
tbl_Category.Category, Count(tbl_Activity.ActivityID) AS CountOfActivityID,
Count(tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;
I know the answer must somehow include SELECT DISTINCT but am not able to get it to work. Do I need to create multiple subqueries?
This is really painful in MS Access. I think the following does what you want to do:
SELECT ac.Category, ac.num_activities, aco.num_comments
FROM (SELECT ca.category, COUNT(*) as num_activities
FROM (SELECT DISTINCT c.Category, a.ActivityID
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as caa
GROUP BY ca.category
) as ca LEFT JOIN
(SELECT c.Category, COUNT(*) as num_comments
FROM (SELECT DISTINCT c.Category, co.CommentId
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as aco
GROUP BY c.Category
) as aco
ON aco.CommentId = ac.CommentId
Note that your LEFT JOINs are superfluous because the WHERE clause turns them into INNER JOINs. This adjusts the logic for that purpose. The filtering is also very tricky, because it uses both tables, requiring that both subqueries have both JOINs.
You can use DISTINCT:
SELECT
tbl_Category.Category, Count(DISTINCT tbl_Activity.ActivityID) AS CountOfActivityID,
Count(DISTINCT tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;

SQL: Attribute matches two different conditions at the same time

I want to make a query where an attribute (same attribute) matches two different conditions at the same time. I have to check if a driver was found in both cities.
I tried to use intersect but I don't get any matches. But in my table I have one driver that matches this conditions.
SELECT s.NumeSofer
FROM Soferi s
INNER JOIN contraventii c ON s.idSofer=c.idSofer
INNER JOIN localitati l ON c.idLocContr=l.idLoc
WHERE l.DenLoc IN ('Iasi', 'Rosiori') AND l.Jud IN ('IS', 'NT');
INTERSECT
SELECT s.NumeSofer
FROM Soferi s
INNER JOIN contraventii c ON s.idSofer=c.idSofer
INNER JOIN localitati l ON c.idLocContr=l.idLoc
WHERE l.DenLoc='Rosiori' AND l.Jud='NT';
You can use aggregation and a HAVING clause, like:
SELECT s.NumeSofer
FROM Soferi s
INNER JOIN contraventii c ON s.idSofer=c.idSofer
INNER JOIN localitati l
ON c.idLocContr = l.idLoc
AND (l.DenLoc, l.Jud) IN ( ('Iasi', 'IS'), ('Rosiori', 'NT') )
GROUP BY s.NumeSofer
HAVING
MAX(CASE WHEN l.DenLoc = 'Iasi' AND l.Jud = 'IS' THEN 1 END) = 1
AND MAX(CASE WHEN l.DenLoc = 'Rosiori' AND l.Jud = 'NT' THEN 1 END) = 1
This will bring you all NumeSofer for which at least one record exists in localitati with DenLoc='Iasi' AND Jud='IS' and at least one record exists with DenLoc='Rosiori' AND Jud='NT'.
Note: the IN operator can be used with tuple values; this reduce the lenght of the query, while avoiding using OR, which is usually not good for general performance.
Do a GROUP BY instead. Use case expressions to do conditional aggregation:
SELECT s.NumeSofer, count(distinct l.DenLoc) as totcount,
count(case when l.DenLoc='Rosiori' then 1 end) as Rosioricount,
count(case when l.DenLoc='Iasi' then 1 end) as Iasicount
FROM Soferi s
INNER JOIN contraventii c ON s.idSofer=c.idSofer
INNER JOIN localitati l ON c.idLocContr=l.idLoc
WHERE (l.DenLoc='Rosiori' AND l.Jud='NT')
OR (l.DenLoc='Iasi' AND l.Jud='IS')
GROUP BY s.NumeSofer
ORDER BY totcount desc
Any rows with totcount = 2?
To get only drivers with both DenLoc's add a HAVING clause:
SELECT s.NumeSofer, count(distinct l.DenLoc) as totcount,
count(case when l.DenLoc='Rosiori' then 1 end) as Rosioricount,
count(case when l.DenLoc='Iasi' then 1 end) as Iasicount
FROM Soferi s
INNER JOIN contraventii c ON s.idSofer=c.idSofer
INNER JOIN localitati l ON c.idLocContr=l.idLoc
WHERE (l.DenLoc='Rosiori' AND l.Jud='NT')
OR (l.DenLoc='Iasi' AND l.Jud='IS')
GROUP BY s.NumeSofer
HAVING count(distinct l.DenLoc) > 1

Sum a Single column into multiple columns based on criteria SQL Server

Right now I am collecting a sum of times based on grouping by a part, job, machine, and type.
I would like to have the summed times to be split across multiple columns rather than multiple rows based on the type. How can I do this?
Here is my code
SELECT
m.[machineName]
,pr.[jobNumber]
,p.[partNumber]
,sc.[type]
,SUM(pl.[elapsedTime]) AS elapsedTime
FROM wincc.dbo.productionLog pl
INNER JOIN wincc.dbo.machines m ON pl.[machineId] = m.id
INNER JOIN wincc.dbo.productionRuns pr ON pl.[productionRunId] = pr.id
INNER JOIN wincc.dbo.parts p ON pr.[partId] = p.Id
INNER JOIN wincc.dbo.statusCodes sc ON pl.[statusCodeId] = sc.id
GROUP BY
m.[machineName]
,pr.[jobNumber]
,p.[partNumber]
,sc.[type]
Which produces:
But I want:
Thank you All!
This is a form of table pivoting. Here's one way to do that with conditional aggregation:
SELECT
m.[machineName]
,pr.[jobNumber]
,p.[partNumber]
,SUM(CASE WHEN sc.Type = 'planned downtime' then pl.[elapsedTime] END) AS plannedDT
,SUM(CASE WHEN sc.Type = 'unplanned downtime' then pl.[elapsedTime] END) AS unplannedDT
,SUM(CASE WHEN sc.Type = 'production' then pl.[elapsedTime] END) AS production
,SUM(CASE WHEN sc.Type = 'rework' then pl.[elapsedTime] END) AS rework
FROM wincc.dbo.productionLog pl
INNER JOIN wincc.dbo.machines m ON pl.[machineId] = m.id
INNER JOIN wincc.dbo.productionRuns pr ON pl.[productionRunId] = pr.id
INNER JOIN wincc.dbo.parts p ON pr.[partId] = p.Id
INNER JOIN wincc.dbo.statusCodes sc ON pl.[statusCodeId] = sc.id
GROUP BY
m.[machineName]
,pr.[jobNumber]
,p.[partNumber]

Remove duplicate address_id from sql data set

I need to get only distinct address_id in result no duplication. Here is my query.
SELECT DISTINCT address.address_id, address.address1, address.streetcity, state.stateabbrev, rtrim(ltrim(case when address.streetzipcode is not null and address.streetzipcode != 'NULL' then address.streetzipcode else '' end))+case when len(address.streetzipplus4)>0 then '-'+rtrim(ltrim(address.streetzipplus4)) else '' end as streetzipcode, address.homephone,
dbo.f_addressstudent (student.address_id) as Students,
dbo.f_addresspeople (student.address_id) as Adults,
case
when #classif_id IS NULL then 0
else
student.classif_id
end classif,
classifctn
FROM district WITH(NOLOCK)
JOIN dbo.building ON building.district_id = district.district_id
JOIN dbo.studbldg_bridge WITH(NOLOCK) ON studbldg_bridge.bldg_id=building.bldg_id
JOIN dbo.student WITH(NOLOCK) ON student.student_id = studbldg_bridge.student_id
JOIN classif with(nolock) on student.classif_id = classif.classif_id
LEFT JOIN dbo.address WITH(NOLOCK) ON student.address_id = address.address_id
LEFT JOIN dbo.state WITH(NOLOCK) ON address.streetstate_id = state.state_id
LEFT JOIN dbo.state AS mailstate WITH(NOLOCK) ON address.state_id = mailstate.state_id
WHERE district.district_id = (SELECT district_id FROM dbo.building WITH(NOLOCK) WHERE bldg_id = #bldg_id)
ORDER BY classif,Adults, Students
Here is result of query
Query result with error in data
I have tried to group by and use aggregate function with address_id but I also have non-aggregate columns so it didn't worked for me.
After that I also tried using OVER(partition by address.address_id) but it also didn't worked.
Any help will be appreciated in advance.
Thank you
**UPDATE on Business logic/Requirements **
I need to get unique addresses for parents of students. As parent can have two or more children living in same address, it causes duplication. I need to get only one child per parent in other words.
From your image of the results it looks like the classifctn column has more than 1 value so it is repeating your row by that. In order to get 1 distinct address_id and rest of the columns either remove it from your query or you can set a precedence that will only return 1 record per address_id
further please tag only the RDBMs you ware actually using. MySQL for example doesn't have window functions yet you tagged it yet referenced using OVER(partition.... which would not be possible in mysql
;WITH cte (
SELECT DISTINCT address.address_id, address.address1, address.streetcity, state.stateabbrev, rtrim(ltrim(case when address.streetzipcode is not null and address.streetzipcode != 'NULL' then address.streetzipcode else '' end))+case when len(address.streetzipplus4)>0 then '-'+rtrim(ltrim(address.streetzipplus4)) else '' end as streetzipcode, address.homephone,
dbo.f_addressstudent (student.address_id) as Students,
dbo.f_addresspeople (student.address_id) as Adults,
case
when #classif_id IS NULL then 0
else
student.classif_id
end classif,
classifctn,
ROW_NUMBER() OVER (PARTITION BY address.address_id ORDER BY HOW WILL YOU CHOOSE?) AS RowNum
FROM district WITH(NOLOCK)
JOIN dbo.building ON building.district_id = district.district_id
JOIN dbo.studbldg_bridge WITH(NOLOCK) ON studbldg_bridge.bldg_id=building.bldg_id
JOIN dbo.student WITH(NOLOCK) ON student.student_id = studbldg_bridge.student_id
JOIN classif with(nolock) on student.classif_id = classif.classif_id
LEFT JOIN dbo.address WITH(NOLOCK) ON student.address_id = address.address_id
LEFT JOIN dbo.state WITH(NOLOCK) ON address.streetstate_id = state.state_id
LEFT JOIN dbo.state AS mailstate WITH(NOLOCK) ON address.state_id = mailstate.state_id
WHERE district.district_id = (SELECT district_id FROM dbo.building WITH(NOLOCK) WHERE bldg_id = #bldg_id)
)
SELECT *
FROM
cte
WHERE
RowNum = 1
ORDER BY
classif
,Adults
,Students
Alternatively you could nest your select query. note though this solution is somewhat useless as it will only return 1 grade/classifctn when more than 1 exists in a household if you really don't care about the column then you should just remove it from your query.
Actually both your classifctn and classif columns will cause you multiple rows when more than 1 student is at the same address. here is a way to concatenate those values to a single row. You should spend some more time on your business case and defining it for us. But here is one example for you:
SELECT DISTINCT
address.address_id
,address.address1
,address.streetcity
,state.stateabbrev
,LTRIM(RTRIM(ISNULL(NULLIF(address.streetzipcode,'NULL'),'')))
+ CASE WHEN LEN(address.streetzipplus4) > 0 THEN '-' ELSE '' END
+ LTRIM(RTRIM(ISNULL(address.streetzipplus4,''))) AS streetzipcode
,address.homephone
,dbo.f_addressstudent (student.address_id) as Students
,dbo.f_addresspeople (student.address_id) as Adults
, case
when #classif_id IS NULL then 0
else student.classif_id
end classif
,STUFF(
(SELECT ',' + CAST(classif_id AS VARCHAR(100))
FROM
classif c
WHERE c.classif = student.classif
FOR XML PATH(''))
,1,1,'') AS classifs
,STUFF(
(SELECT ',' + CAST(classifctn AS VARCHAR(100))
FROM
classif c
WHERE c.classif = student.classif
FOR XML PATH(''))
,1,1,'') AS classifctns
FROM
district WITH(NOLOCK)
INNER JOIN dbo.building
ON building.district_id = district.district_id
AND building.bldg_id = #bldg_id
INNER JOIN dbo.student WITH(NOLOCK)
ON student.student_id = studbldg_bridge.student_id
INNER JOIN dbo.address WITH(NOLOCK)
ON student.address_id = address.address_id
LEFT JOIN dbo.state WITH(NOLOCK)
ON address.streetstate_id = state.state_id
Note I when ahead and changed the zip code logic to show you some use of ISNULL() and NULLIF() that are helpful in cases like that. I also removed 3 tables because 2 are not used and the third ends up being used in a subselect to concatenate the values. Also address table was changed to an INNER JOIN because if an address doesn't exist all of the other information becomes blank/useless....
INNER JOIN dbo.studbldg_bridge WITH(NOLOCK) ON studbldg_bridge.bldg_id=building.bldg_id
LEFT JOIN dbo.state AS mailstate WITH(NOLOCK) ON address.state_id = mailstate.state_id
INNER JOIN classif with(nolock) on student.classif_id = classif.classif_id