SQL to select parent that contains child specific value - sql

I am actually creating a crystal reports v12 (2008) report but can't find the method, using Crystal, to extract the following. I thought if someone might answer in SQL language, I could piece it together.
2 Tables: hbmast, ddmast
SELECT hbmast.custno, hbmast.id, ddmast.name, ddmast.status
WHERE hbmast.custno = ddmast.custno
GROUP BY hbmast.id
pseudo code::show all hbmast values that have ddmast.status = '2'
Sample output:
J0001, 111222, PAUL JONES, 1
111222, PAUL JONES, 2
111222, PAUL JONES, 1
K0001, 555333, PETER KING, 3
555333, PETER KING, 1
I would like to have Paul show on the report with all child records but Peter should not be returned on the report since he has no child records with '2' for ddmast.status field.
Thanks for the help

I think you're looking for this:
select hb.custno, hb.id, dd.name, dd.status from hbmast hb
join ddmast dd on hb.custno = dd.custno
where hb.custno in (
select custno from ddmast
where status = '2'
)
Let me know if this returns your expected result.

The way to achieve this in Crystal would be to have your hb and dd tables then a second alias of the dd table.
So you would filter your dd alias table where status = 2 then join to your hb table and back to your dd table (not the alias). The SQL would end up looking like:
select hb.custno, hb.id, dd.name, dd.status from hbmast hb
inner join ddmast dd on hb.custno = dd.custno
inner join ddmast dd2 on hb.custno = dd2.custno
where dd2.status = '2'
Andomar makes a valid point about duplicate records appearing if there is more than 1 record per group with a status of 2. If that is the case you can either group by primary key and show row information at group footer level OR use a sql expression with a subquery in your selection formula instead of the double join method.
SQL Expression: (select count(*) from ddmast where custno = "hbmast.custno" and status = '2')
Then record selection expert: {%sqlexpression} > 0

And a different way to get the same...
SELECT hb.custno, hb.id, dd.name, dd.status
FROM hbmast hb
INNER join ddmast dd
on hb.custno = dd.custno
INNER JOIN DDMAST2 DD2
on DD2.custNo = HB.custNo
AND DD2.Status='2'

Related

Filter for combination of column values in SQL

I want to filter for all People who have the same AttributValue for certain Attributs as another Person
I have the following Query:
SELECT
p1.keyValue,
p1.Displayname,
p2.keyValue,
p2.Displayname,
p1.ImportantAttrName,
p1.ImportantAttrValue
FROM Person p1 WITH (NOLOCK)
JOIN Person p2 WITH (NOLOCK)
ON p1.ImportantAttr = p2.ImportantAttr
WHERE p1.keyValue != p2.keyValue
AND p1.ImportantAttrValue = p2.ImportantAttrValue
with this query I will get all entries twice, because every Person will be in p1 and p2.
So the result will look like this:
I123 Freddy Krüger A123 The Horsemen Moviecategorie Horror
A123 The Horsemen I123 Freddy Krüger Moviecategorie Horror
But for analysis purposes it would be be nice if I could get a combination of p1.keyvalue and p2.keyvalue only once, without respect to in which of both colums the values are.
So far I did this by exporting to excel and do the cleanup there, but is there a way to fix the query to not get this "duplicates"?
Use where p1.keyValue < p2.keyValue:
SELECT
p1.keyValue,
p1.Displayname,
p2.keyValue,
p2.Displayname,
p1.ImportantAttrName,
p1.ImportantAttrValue
FROM Person p1 WITH (NOLOCK)
INNER JOIN Person p2 WITH (NOLOCK)
ON p1.ImportantAttr = p2.ImportantAttr
WHERE
p1.keyValue < p2.keyValue AND -- change is here
p1.ImportantAttrValue = p2.ImportantAttrValue;
This will ensure that you do not see duplicate pairs. To understand numerically why this works, consider two key values, 1 and 2. Using the condition !=, both 1-2 and 2-1 meet that criteria. But using < results in only 1-2.
You can turn:
on p1.ImportantAttr = p2.ImportantAttr
to:
on p1.ImportantAttr = p2.ImportantAttr and p1.keyValue < p2.keyValue
The whole query could look like this:
SELECT
p1.keyValue,
p1.Displayname,
p2.keyValue,
p2.Displayname,
p1.ImportantAttrName,
p1.ImportantAttrValue
FROM Person p1 WITH (NOLOCK)
JOIN Person p2 WITH (NOLOCK)
ON p1.ImportantAttr = p2.ImportantAttr
AND p1.keyValue < p2.keyValue
WHERE p1.ImportantAttrValue = p2.ImportantAttrValue
this may be different way of approach but can be get the expected.
Using Partition Count(*) :
select count(*) over(partition by Attr) as RepeatCount, * from (
select keyValue,DisplayName,ImportantAttr + ' ' +ImportantAttrValue as Attr
from tblTest) tblTemp
as per the above Query you will get the result like below
> RepeatCount keyValue DisplayName Attr
>
> 1 P321 The Ironman Generalcategorie Test
> 2 I123 Freddy Krüger Moviecategorie Horror
> 2 A123 The Horsemen Moviecategorie Horror
from this result you can filter records by Repeatcount > 1

SQL AVG statement another table

I'm having trouble with a SQL query. The goal is to see only the certain entries on a specific date (I got this already) which have an average score below 1 in their last 5 home games.
You can see the tables here:
http://dbup2date.uni-bayreuth.de/downloads/bundesliga/Klassendiagramm_Bundesliga.pdf
I have this code so far:
SELECT
A.Spieltag, A.Datum, A.Uhrzeit, B.Name AS Heim
FROM
Spiel AS A
JOIN
Verein AS B ON A.Heim = B.V_ID AND B.Liga = 1
WHERE
Spieltag = 5
HAVING
AVG(SELECT Tore_Heim
FROM Spiel AS A
JOIN Verein AS B
WHEN A.Heim = B.V_ID) < 1
Sorry for my bad English
Thank you
Make sure you group by any =fields that you are not aggregating on when you use HAVING. And you can simplify that HAVING clause since you are already referencing those exact tables in your FROM:
SELECT
A.Spieltag, A.Datum, A.Uhrzeit, B.Name AS Heim
FROM
Spiel AS A
JOIN
Verein AS B ON A.Heim = B.V_ID AND B.Liga = 1
WHERE
Spieltag = 5
GROUP BY A.Spieltag, A.Datum, A.Uhrzeit, B.Name
HAVING
AVG(Tore_Heim) < 1

SQL Display Distinct records and include a field if certain data is found

What I'm trying to accomplish is display distinct data but also display a field on each row if a particular row has a certain data.
The thing thats confusing me is the fact that I still need it to be distinct and if I attempt to do another join i get more rows..
I just wish to keep the same results i'm retrieving but with an additional column that tells me - This equipment (row) needs repairs because at least one of its properties stated so... Hope this makes sense, not sure if I'm explaining myself clearly here.
The Main Table (Inspection Table)
In the above table, Notice FK_Sequence Each entry has around 17 which the user is required to answer OK or REPAIR (this is the FK_Status)
My current Query is the following and results just an Employee and the equipment they worked on.
SELECT DISTINCT
a.EnteredDate,
bb.EmployeeId,
bb.EmployeeName,
dd.EquipmentId,
dd.EquipmentName
FROM dbo.PIT_Inspection a
INNER JOIN dbo.EmployeeName bb
ON a.FK_EmployeeName = bb.PK_EmployeeName
INNER JOIN dbo.EquipmentName dd
ON a.FK_EquipmentName = dd.PK_EquipmentName
Results for above query:
But then here is where my question comes in. Notice the Main Table - FK_Status of 2 on Line 19.. I would like to detect this and for this particular Employee Display Repair in an additional column in the table above named StatusName.
You can do this by using GROUP BY instead of DISTINCT, then you can use a conditional count to see how many rows have a status of 2, if it is more than none then display REPAIR
SELECT a.EnteredDate,
bb.EmployeeId,
bb.EmployeeName,
dd.EquipmentId,
dd.EquipmentName,
StatusName = CASE WHEN COUNT(CASE WHEN a.FK_Status = 2 THEN 1 END) > 0
THEN 'REPAIR'
ELSE ''
END
FROM dbo.PIT_Inspection a
INNER JOIN dbo.EmployeeName bb
ON a.FK_EmployeeName = bb.PK_EmployeeName
INNER JOIN dbo.EquipmentName dd
ON a.FK_EquipmentName = dd.PK_EquipmentName
GROUP BY a.EnteredDate, bb.EmployeeId, bb.EmployeeName, dd.EquipmentId, dd.EquipmentName;
Try this:
SELECT
a.EnteredDate,
bb.EmployeeId,
bb.EmployeeName,
dd.EquipmentId,
dd.EquipmentName,
CASE WHEN SUM(CASE FK_Status WHEN 2 THEN 1 ELSE 0 END) > 0
THEN 'Repair' ELSE 'OK' END AS StatusName
FROM dbo.PIT_Inspection a
INNER JOIN dbo.EmployeeName bb
ON a.FK_EmployeeName = bb.PK_EmployeeName
INNER JOIN dbo.EquipmentName dd
ON a.FK_EquipmentName = dd.PK_EquipmentName
GROUP BY
a.EnteredDate,
bb.EmployeeId,
bb.EmployeeName,
dd.EquipmentId,
dd.EquipmentName
Have the query with the distinct as a sub query, then join to get the "additional information". Something along the lines of:
SELECT (your orig fields, but take from inspection where you took from a)
FROM
(SELECT DISTINCT a.FK_EmployeeName, a.EnteredDate, FK_EquipmentName
FROM dbo.PIT_Inspection a) inspection
INNER JOIN dbo.EmployeeName bb
ON inspection.FK_EmployeeName = bb.PK_EmployeeName
INNER JOIN dbo.EquipmentName dd
ON inspection.FK_EquipmentName = dd.PK_EquipmentName

Merge multiple rows in data to show only a single row in the result

I have a stored procedure which takes 1 parameter, an ID number (systudentid).
The procedure returns 3 rows: a student’s academic counselor (AC), financial counselor (FC), and admissions counselor (EC) along with relevant contact information; 3 different people.
Certain students have ACs and FCs who are the same person, but the query will still return 3 rows.
AdvisorType|AdvisorLastName|AdvisorFirstName|(other data)|systaffID
AC DOE JOHN ..... 12345
AC DOE JOHN ..... 12345
EC SMITH JANE ..... 45678
Where in my code can I plug in the logic (and how, I'm a newbie with sql) so that when the systudentid passed to the procedure identifies a student having the same person for both AC and FC, it will display the results this way.
The advisor type is changed to "SSA" and only one of the records for the double-duty counselor is returned.
AdvisorType|AdvisorLastName|AdvisorFirstName|(other data)|SystaffID
SSA DOE JOHN ...... 12345
EC SMITH JANE ...... 45678
Here is my select statement:
SELECT
SyStaffGroup.Descrip AS AdvisorType
,SyStaff.LastName AS AdvisorLastName
,SyStaff.FirstName AS AdvisorFirstName
,SyStaff.Phone AS AdvisorPhone
,SyStaff.Ext AS AdvisorExtention
,SyStaff.eMail AS AdvisorEMail
,SyStaff.SyStaffID AS SyStaffID
FROM SyStaff (NOLOCK)
JOIN SyAdvisorByEnroll (NOLOCK)
ON SyAdvisorByEnroll.SyStaffID = SyStaff.SyStaffID
JOIN SyStaffGroup (NOLOCK)
ON SyStaffGroup.SyStaffGroupID = SyAdvisorByEnroll.SyStaffGroupID
JOIN AdEnroll (NOLOCK)
ON AdEnroll.AdEnrollID = SyAdvisorByEnroll.AdEnrollID
JOIN SyStudent (NOLOCK)
ON AdEnroll.SyStudentID = SyStudent.SyStudentId
WHERE
SyStaff.Active = 1
--AND
--syadvisorbyenroll.adenrollid = (
--SELECT adenrollid from dbo.fn_student_enrollment_activeenrollmentlist (#systudentid)
--)
AND adEnroll.adEnrollID IN (
SELECT adEnrollID FROM dbo.fn_Student_Enrollment_ActiveEnrollmentList(#SyStudentID)
)
AND SyAdvisorByEnroll.AdvisorModule IN ('AD','FA')
AND SyStaffGroup.Descrip IN ('AC - Academic Counselor', 'FC - Finance Counselors', 'EC - Adm. Counselor With Reg')
UNION
SELECT DISTINCT
'Admissions Counselor' AS AdvisorType
,SyStaff.LastName AS AdvisorLastName
,SyStaff.FirstName AS AdvisorFirstName
,SyStaff.Phone AS AdvisorPhone
,SyStaff.Ext AS AdvisorExtention
,SyStaff.eMail AS AdvisorEMail
,SyStaff.SyStaffID AS SyStaffID
FROM systudent
INNER JOIN AmRep ON SyStudent.AMREpID = AmREp.AMREpid
INNER JOIN SyStaff ON SyStaff.SyStaffID = AmRep.AmRepID
WHERE Systudent.SYStudentid = #systudentid
Any hints or suggested methods that I can either try or Google (I've tried searching but results are a lot more useful if I knew what to look for) would be greatly appreciated.
You can add a nested subquery to indicate which students have the same advisor filling multiple positions, and adjust the type selection accordingly. Here are the changed portions of your above query:
SELECT
CASE WHEN (mutiples.SyStaffID IS NOT NULL) THEN 'SSA'
ELSE SyStaffGroup.Descrip END AS AdvisorType
-- other columns omitted
FROM SyStaff (NOLOCK)
JOIN SyAdvisorByEnroll (NOLOCK)
ON SyAdvisorByEnroll.SyStaffID = SyStaff.SyStaffID
LEFT JOIN (
SELECT SyStaffID,AdEnrollID
FROM SyAdvisorByEnroll
GROUP BY SyStaffID,AdEnrollID
HAVING COUNT(DISTINCT SyStaffGroupID) > 1
) multiples
ON multiples.SyStaffID = SyAdvisorByEnroll.SyStaffID
AND multiples.AdEnrollID = SyAdvisorByEnroll.AdEnrollID
-- rest of query omitted
This might have a mistake or two, since you didn't include your table schema. The nested subquery, "multiples", contains all advisor / enrollee pairs where the advisor is in multiple groups. You left join against this and adjust the final type selection to "SSA" if there's a matching entry in the nested subquery.
An important note: as written, this will include two SSA rows for an eligible advisor / enrollee pair. However, the final results will not, because you are using a UNION in this query, which filters out duplicates, even if they're only present in one half of the union. If you change this to UNION ALL or eliminate the UNION entirely, you will need to add DISTINCT to the top of the query, like so:
SELECT DISTINCT CASE WHEN (mutiples.SyStaffID IS NOT NULL) ...

Get percentages of larger group

The query below is kind of an ugly one so I hope I've got it spaced well enough to make it readable. The query finds the percentage of people that visit a given hospital if they are from a certain area. For instance, if 100 people live in county X and 20 go to hospital A and 80 go to hospital B the query outputs. How the heck is this sort of thing done? Let me know if I need to document the query or whatever I can do to make it clearer.
hospital A 20
hospital B 80
The query below works exactly like I want it to, but it give me thinking: how could this be done for every county in my table?
select hospitalname, round(cast(counts as float)/cast(fayettestrokepop as float)*100,2)as percentSeen
from
(
SELECT tblHospitals.hospitalname, COUNT(tblHospitals.hospitalname) AS counts, tblStateCounties_1.countyName,
(SELECT COUNT(*) AS Expr1
FROM Patient INNER JOIN
tblStateCounties ON Patient.stateCode = tblStateCounties.stateCode AND Patient.countyCode = tblStateCounties.countyCode
WHERE (tblStateCounties.stateCode = '21') AND (tblStateCounties.countyName = 'fayette')) AS fayetteStrokePop
FROM Patient AS Patient_1 INNER JOIN
tblHospitals ON Patient_1.hospitalnpi = tblHospitals.hospitalnpi INNER JOIN
tblStateCounties AS tblStateCounties_1 ON Patient_1.stateCode = tblStateCounties_1.stateCode AND Patient_1.countyCode = tblStateCounties_1.countyCode
WHERE (tblStateCounties_1.stateCode = '21') AND (tblStateCounties_1.countyName = 'fayette')
GROUP BY tblHospitals.hospitalname, tblStateCounties_1.countyName
) as t
order by percentSeen desc
EDIT: sample data
The sample data below is without the outermost query (the as t order by part).
The countsInTheCounty column is the (select count(*)..) part after 'tblStateCounties_1.countyName'
hospitalName hospitalCounts countyName countsInTheCounty
st. james 23 X 300
st. jude 40 X 300
Now with the outer query we would get
st james 0.076 (23/300)
st. jude 0.1333 (40/300)
Here is my guess. You'll have to test against your data or provide proper DDL + sample data.
;WITH totalCounts AS
(
SELECT StateCode, countyCode, COUNT(*) AS totalcount
FROM dbo.Patient GROUP BY StateCode, countyCode
)
SELECT
h.hospitalName,
hospitalCounts = COUNT(p.hospitalnpi),
c.countyName,
countsInTheCounty = tc.totalCount,
percentseen = CONVERT(DECIMAL(5,2), COUNT(p.hospitalnpi)*100.0/tc.totalCount)
FROM
dbo.Patient AS p
INNER JOIN
dbo.tblHospitals AS h
ON p.hospitalnpi = h.hospitalnpi
INNER JOIN
totalCounts AS tc
ON p.StateCode = tc.StateCode
AND p.countyCode = tc.countyCode
INNER JOIN
dbo.tblStateCounties AS c
ON tc.StateCode = c.stateCode
AND tc.countyCode = c.countyCode
GROUP BY
h.hospitalname,
c.countyName,
tc.totalcount
ORDER BY
c.countyName,
percentseen DESC;