SELECT statement with operators not working - sql

I have these two tables and I need a query, that outputs every member that has the lvnr 050056 AND NOT 050054.
I have these two tables
I have tried it with the following query but it does not work right:
SELECT s.matrnr, s.vorname, s.nachname
FROM student s
INNER JOIN teilgenommen t ON s.matrnr = t.matrnr
WHERE (t.lvnr = 050056) AND (t.lvnr != 050054)
Only Martin Huber with the ID 0111111 should be shown, but I get both..
I would be very thankful for any advie

Use exists and not exists:
select s.*
from student s
where exists (select 1
from teilgenommen t
where t.matrnr = s.matrnr and t.lvnr = '050056'
) and
not exists (select 1
from teilgenommen t
where t.matrnr = s.matrnr and t.lvnr = '050054'
);
The leading zeros suggest that lvnr is really stored as a string. If so, then single quotes should be used for the comparison value.

You can do:
SELECT
s.matrnr,
s.vorname,
s.nachname
FROM
student s
INNER JOIN
(
select
matrnr,
max(case when lvnr='050056' then 1 else 0 end) as a,
max(case when lvnr='050054' then 1 else 0 end) as b
from
teilgenommen
group by
matrnr
having a=1 and b=0
) t
ON s.matrnr = t.matrnr

This can also be solved with aggregation and a having clause for filtering:
select s.matrnr, s.vorname, s.nachname
from student s
inner join teilgenommen t on s.matrnr = t.matrnr
group by s.matrnr, s.vorname, s.nachname
having
max(case when t.lvnr = 050056 then 1 else 0 end) = 1
and max(case when t.lvnr = 050054 then 1 else 0 end) = 0

Related

In a nested query, check all values for a condition

How can I get rid of nested queries (agree, dis_agreed)? How to rewrite to join - I can not think of it. Maybe there are other optimal solutions?
select *
from (
select
(select count(id) from Agreement a where a.ConclusionCardFile = f.id and a.Status = 1) agreed,
(select count(id) from Agreement a where a.ConclusionCardFile = f.id and (a.Status <> 1 or a.Status is null)) dis_agreed
from ConclusionCard_Files f
) t
where t.agreed > 0 and dis_agreed = 0
You can write the conditions as a where clause:
select *
from conclusionCard_Files
where exists (
select *
from agreement
where agreement.conclusionCardFile = conclusionCard_Files.id
having sum(case when status = 1 then 1 else 0 end) > 0
and sum(case when status = 1 then 0 else 1 end) = 0
)
Maybe you just using sub-queries only to filter? What about to move them to WHERE clause?
SELECT
*
FROM ConclusionCard_Files f
WHERE
EXISTS(select * from Agreement a where (a.ConclusionCardFile = f.id) and a.Status =1)
AND NOT EXISTS(select * from Agreement a where (a.ConclusionCardFile = f.id) and (a.Status != 1 or a.Status is null))
It's performance friendly because SqlServer do not count all Counts
If I understand correctly, you can try to use JOIN with HAVING condition aggregate function.
SELECT COUNT(CASE WHEN a.Status = 1 THEN ID END) agreed,
COUNT(CASE WHEN a.Status <> 1 or a.Status is null THEN ID END) dis_agreed
FROM Agreement a
INNER JOIN ConclusionCard_Files f
ON a.ConclusionCardFile = f.id
HAVING
COUNT(CASE WHEN a.Status = 1 THEN ID END) > 0
AND
COUNT(CASE WHEN a.Status <> 1 or a.Status is null THEN ID END) = 0
EDIT
if you want to get data from ConclusionCard_Files based on your condition. you can try to let condition aggregate function in subquery each ConclusionCardFile from table Agreement then do JOIN
SELECT f.*
FROM (
SELECT COUNT(CASE WHEN a.Status = 1 THEN ID END) agreed,
COUNT(CASE WHEN a.Status <> 1 or a.Status is null THEN ID END) dis_agreed,
a.ConclusionCardFile
FROM Agreement a
GROUP BY a.ConclusionCardFile
) a
INNER JOIN ConclusionCard_Files f
ON a.ConclusionCardFile = f.id
WHERE a.agreed > 0 AND a.dis_agreed = 0

SQL Join Rows to column

I have left join between two tables where it returns two records I need that record should be converted from rows to column.
SQL join as follows:
select (case when l.PatientVisitCluster=1 then t.Result end) as t1,
(case when l.PatientVisitCluster=2 then t.Result end) as t2
from tPatientLabTestData t left join
tPatientLabTest l
on t.tPatientLabTestId = l.Id
where l.PatientId = #PatientId and
l.Volgnar = #Volgnar and
l.TestTypeListId = #LabTestId
It returns result like this.
I need desired result like below:
t1 t2
22 120
Use aggregation:
select max(case when l.PatientVisitCluster = 1 then t.Result end) as t1,
max(case when l.PatientVisitCluster = 2 then t.Result end) as t2
from tPatientLabTestData t left join
tPatientLabTest l
on t.tPatientLabTestId=l.Id
where l.PatientId = #PatientId and
l.Volgnar = #Volgnar and
l.TestTypeListId = #LabTestId;
If you wanted this for all combinations of the three columns, include group by:
select l.PatientId, l.Volgnar, l.TestTypeListId,
max(case when l.PatientVisitCluster = 1 then t.Result end) as t1,
max(case when l.PatientVisitCluster = 2 then t.Result end) as t2
from tPatientLabTestData t left join
tPatientLabTest l
on t.tPatientLabTestId=l.Id
group by l.PatientId, l.Volgnar, l.TestTypeListId;
You can also use PIVOT
select PatientId, Volgnar, TestTypeListId, [1] t1, [2] t2
from tPatientLabTestData t
left join tPatientLabTest l on t.tPatientLabTestId=l.Id
pivot (max(result) for PatientVisitCluster in ([1], [2])) p

Using sub query in in group by sql server

Hello everyone I would like to use sub query as code bellow but it get error I would like to to ask you that I how can i do this. Thanks!
SELECT HR_EMPMAST.DEPT,HR_DEPARTMENT.DESCRIPTION AS DEPARTMENT,HR_JOBFUNCTION.CODE,HR_JOBFUNCTION.DESCRIPTION AS POSITION,
COUNT(HR_EMPMAST.EMPCODE) ACTUAL,
SUM(CASE WHEN HR_EMPMAST.SEX = 'M' THEN 1 ELSE 0 END) AS M,
SUM(CASE WHEN HR_EMPMAST.SEX = 'F' THEN 1 ELSE 0 END) AS F,SUM(CASE WHEN HR_EMPMAST.EMPTYPE='LOCAL' THEN 1 ELSE 0 END) AS LOCALEMP,
SUM(CASE WHEN HR_EMPMAST.EMPTYPE='EXPAT' THEN 1 ELSE 0 END) AS EXPATEMP--,
(SELECT EMPNO FROM HR_HEADCOUNT WHERE POSITION=HR_EMPMAST.JOBCODE AND INMONTH=1 AND INYEAR=2017) AS EMPNO
FROM HR_EMPMAST
LEFT JOIN HR_DEPARTMENT
ON HR_EMPMAST.DEPT = HR_DEPARTMENT.CODE
LEFT JOIN HR_JOBFUNCTION
ON HR_EMPMAST.JOBCODE=HR_JOBFUNCTION.CODE
WHERE HR_EMPMAST.CAREERDESC <> 'TERMIMATE'
GROUP BY HR_EMPMAST.DEPT,HR_DEPARTMENT.DESCRIPTION,HR_JOBFUNCTION.CODE,HR_JOBFUNCTION.DESCRIPTION
At first, please, use table aliases.
Second: you need to add ALL columns from SELECT to GROUP BY (but not ones in SUM and COUNT functions).
Third: you got strange EMPNO select. Maybe better way is to use JOIN?
Try to use this one:
SELECT e.DEPT,
d.DESCRIPTION AS DEPARTMENT,
jf.CODE,
jf.DESCRIPTION AS POSITION,
COUNT(e.EMPCODE) ACTUAL,
SUM(CASE WHEN e.SEX = 'M' THEN 1 ELSE 0 END) AS M,
SUM(CASE WHEN e.SEX = 'F' THEN 1 ELSE 0 END) AS F,
SUM(CASE WHEN e.EMPTYPE='LOCAL' THEN 1 ELSE 0 END) AS LOCALEMP,
SUM(CASE WHEN e.EMPTYPE='EXPAT' THEN 1 ELSE 0 END) AS EXPATEMP,
hc.EMPNO
FROM HR_EMPMAST e
LEFT JOIN HR_DEPARTMENT d
ON e.DEPT = d.CODE
LEFT JOIN HR_JOBFUNCTION jf
ON e.JOBCODE = jf.CODE
LEFT JOIN HR_HEADCOUNT hc
ON hc.POSITION = e.JOBCODE AND hc.INMONTH=1 AND hc.INYEAR=2017
WHERE e.CAREERDESC <> 'TERMIMATE'
GROUP BY e.DEPT,
d.DESCRIPTION,
jf.CODE,
jf.DESCRIPTION,
hc.EMPNO
Try this below query or include HR_EMPMAST.JOBCODE in group by clause...
SELECT HR_EMPMAST.DEPT,HR_DEPARTMENT.DESCRIPTION AS DEPARTMENT,HR_JOBFUNCTION.CODE,HR_JOBFUNCTION.DESCRIPTION AS POSITION,
COUNT(HR_EMPMAST.EMPCODE) ACTUAL,
SUM(CASE WHEN HR_EMPMAST.SEX = 'M' THEN 1 ELSE 0 END) AS M,
SUM(CASE WHEN HR_EMPMAST.SEX = 'F' THEN 1 ELSE 0 END) AS F,SUM(CASE WHEN HR_EMPMAST.EMPTYPE='LOCAL' THEN 1 ELSE 0 END) AS LOCALEMP,
SUM(CASE WHEN HR_EMPMAST.EMPTYPE='EXPAT' THEN 1 ELSE 0 END) AS EXPATEMP,
em.EMPNO AS EMPNO
FROM HR_EMPMAST
LEFT JOIN (SELECT POSITION, EMPNO FROM HR_HEADCOUNT WHERE INMONTH=1 AND INYEAR=2017) em on em.POSITION=HR_EMPMAST.JOBCODE
LEFT JOIN HR_DEPARTMENT
ON HR_EMPMAST.DEPT = HR_DEPARTMENT.CODE
LEFT JOIN HR_JOBFUNCTION
ON HR_EMPMAST.JOBCODE=HR_JOBFUNCTION.CODE
WHERE HR_EMPMAST.CAREERDESC <> 'TERMIMATE'
GROUP BY HR_EMPMAST.DEPT,HR_DEPARTMENT.DESCRIPTION,HR_JOBFUNCTION.CODE,HR_JOBFUNCTION.DESCRIPTION

How do I compare SUM and COUNT()s in SQL?

I am building a small query to find all CustomerNumbers where all of their policies are in a certain status (terminated).
Here is the query I am working on
select
a.cn
,p.pn
, tp = COUNT(p.pn)
, tp2 = SUM(case when p.status = 4 then 1 else 0 end)
from
(
select cn, cn2
from bc
union
select cn, cn2= fn
from ic
) as a
left join p as p
on a.cn = p.cn
group by
a.cn,
pn
My issue is when I add the clause:
WHERE cn = tp
It says the columns are invalid. Am I missing something incredibly obvious?
You can't use aliases at the same level of a query. The reason is that the where clause is logically evaluated before the select, so the aliases defined in the select are not available in the where.
A typical solution is to repeat the expression (other answers) or use a subquery or cte:
with cte as (
<your query here>
)
select cte.*
from cte
where TotalPolicies = TermedPolicies;
However, in your case, you have an easier solution, because you have an aggregation query. So just use:
having TotalPolicies = TermedPolicies
You cannot use the aliased aggregate column names in the where clause. You have to use the expression itself instead. Also you cannot use it as where cluase, but use it in the having clause
HAVING COUNT(p.PolicyNumber) = SUM(case when p.status = 4 then 1 else 0 end)
You can also make the whole query as a subquery then add your where statement:
select CustomerNumber
,PolicyNumber
,TotalPolicies
,TermedPolicies
from (
select
a.CustomerNumber
,p.PolicyNumber
, TotalPolicies = COUNT(p.PolicyNumber)
, TermedPolicies = SUM(case when p.status = 4 then 1 else 0 end)
from
(
select CustomerNumber, CompanyName
from BusinessClients
union
select CustomerNumber, CompanyName = FullName
from IndividualClients
) as a
left join Policies as p
on a.CustomerNumber = p.CustomerNumber
group by
a.CustomerNumber,
PolicyNumber
) tb
where TotalPolicies = TermedPolicies
select
a.CustomerNumber
,p.PolicyNumber
, COUNT(p.PolicyNumber) as TotalPolicies
, SUM(case when p.status = 4 then 1 else 0 end) as TermedPolicies
from
(
select CustomerNumber, CompanyName
from BusinessClients
union
select CustomerNumber, CompanyName = FullName
from IndividualClients
) as a
left join Policies as p
on a.CustomerNumber = p.CustomerNumber
WHERE COUNT(p.PolicyNumber)= SUM(case when p.status = 4 then 1 else 0 end)
group by
a.CustomerNumber,
PolicyNumber
This should work. But it is not tested.
In order to filter by an aggregate function, you must include it in the HAVING clause, rather than the WHERE clause.
select
a.CustomerNumber
,p.PolicyNumber
, TotalPolicies = COUNT(p.PolicyNumber)
, TermedPolicies = SUM(case when p.status = 4 then 1 else 0 end)
from
(
select CustomerNumber, CompanyName
from BusinessClients
union
select CustomerNumber, CompanyName = FullName
from IndividualClients
) as a
left join Policies as p
on a.CustomerNumber = p.CustomerNumber
having COUNT(p.PolicyNumber) = SUM(case when p.status = 4 then 1 else 0 end)
group by
a.CustomerNumber,
PolicyNumber
The reason for this has to do with the way SQL engines evaluate queries. The contents of the WHERE clause are used to filter out rows before the aggregate functions are applied. If you could reference aggregate functions there, the engine would have to have some way to determine which predicates to apply before aggregation and which to apply after. The HAVING clause allows the engine to have a clear demarcation between the two: WHERE applies before aggregation and HAVING applies after aggregation.
When dealing with aggregations in a query that has grouping, you will need to use HAVING. This should work:
select
a.CustomerNumber
,p.PolicyNumber
, TotalPolicies = COUNT(p.PolicyNumber)
, TermedPolicies = SUM(case when p.status = 4 then 1 else 0 end)
from
(
select CustomerNumber, CompanyName
from BusinessClients
union
select CustomerNumber, CompanyName = FullName
from IndividualClients
) as a
left join Policies as p
on a.CustomerNumber = p.CustomerNumber
group by
a.CustomerNumber,
PolicyNumber
HAVING TermedPolicies = SUM(case when p.status = 4 then 1 else 0 end)

SQL SELECT string Greater than using count()

I am trying to list groups that have more graduate than undergraduate student members. I feel I have the concept behind my idea, but making the query is a little more difficult then a simple translation. Below is my code, I currently am getting a missing right parenthesis error where COUNT(student.career = 'GRD'). Thanks.
SELECT studentgroup.name
COUNT(student.career = 'GRD') - COUNT(student.career = 'UGRD')
AS Gradnum FROM studentgroup
INNER JOIN memberof ON studentgroup.GID = memberof.GroupID
INNER JOIN student ON memberof.StudentID = student.SID
WHERE Gradnum > 1;
SELECT studentgroup.GID, max(studentgroup.name)
FROM studentgroup
INNER JOIN memberof ON studentgroup.GID = memberof.GroupID
INNER JOIN student ON memberof.StudentID = student.SID
GROUP BY studentgroup.GID
HAVING SUM(CASE WHEN student.career = 'GRD' THEN 1
WHEN student.career = 'UGRD'THEN -1
ELSE 0
END) >0
SELECT studentgroup.name
SUM(CASE WHEN student.career = 'GRD' THEN 1 ELSE 0 END) - SUM(CASE WHEN student.career = 'UGRD' THEN 1 ELSE 0 END)
AS Gradnum FROM studentgroup
INNER JOIN memberof ON studentgroup.GID = memberof.GroupID
INNER JOIN student ON memberof.StudentID = student.SID
WHERE Gradnum > 1
GROUP BY studentgroup.name;
I have used WITH As clause which is supported by most of DBMS like SQL Server, PostGresSQL except MySQL
With grpTbl As
(
SELECT studentgroup.name As StudentGroupName,
SUM( CASE WHEN student.career = 'GRD' THEN 1 ELSE 0 END ) AS 'TotalGraduate',
SUM( CASE WHEN student.career = 'UGRD' THEN 1 ELSE 0 END ) AS 'TotalUnderGraduate'
FROM studentgroup
INNER JOIN memberof ON studentgroup.GID = memberof.GroupID
INNER JOIN student ON memberof.StudentID = student.SID
)
SELECT StudentGroupName
FROM grpTbl
WHERE TotalGraduate > TotalUnderGraduate
For MySQL you can use Temporary table to store resultset from First query and filter out the GroupNames which have more Graduate than UnderGraduate in WHERE clause .
This method will work for other DBMS also difference being syntax of creating temporary table.
CREATE TEMPORARY TABLE grpTbl (
StudentGroupName varchar(255),
TotalGraduate INT,
TotalUnderGraduate INT
);
INSERT INTO grpTbl
SELECT studentgroup.name As StudentGroupName,
SUM( CASE WHEN student.career = 'GRD' THEN 1 ELSE 0 END ) ,
SUM( CASE WHEN student.career = 'UGRD' THEN 1 ELSE 0 END )
FROM studentgroup
INNER JOIN memberof ON studentgroup.GID = memberof.GroupID
INNER JOIN student ON memberof.StudentID = student.SID
SELECT StudentGroupName
FROM grpTbl
WHERE TotalGraduate > TotalUnderGraduate
DROP TABLE grpTbl
One more option
SELECT studentgroup.name
FROM studentgroup INNER JOIN memberof ON studentgroup.GID = memberof.GroupID
INNER JOIN student ON memberof.StudentID = student.SID
GROUP BY studentgroup.name
HAVING COUNT(CASE WHEN student.career = 'GRD' THEN student.career END)
> COUNT(CASE WHEN student.career = 'UGRD' THEN student.career END)