Count of Employee Nos. starting with T and N - sql

Please help me solve this query. I want to find the count of employees where employee nos. starting with T and N from employee table which is grouped by District. The query is given below
select
m.institution_dist,
count(e.emp_pf_acc_number like 'T%') as tcnt,
count(e.emp_pf_acc_number like 'N%') as ncnt
from
pf_emp e
left join
pfmast_institution m on e.emp_workplace_code = m.institution_code
where
e.retired <> 'Y'
and emp_classification_code = '3'
and m.institution_dist in (
select dist_name
from pfmast_dist
)
group by m.institution_dist
The problem is that the result which I am getting is same for the employee no starting with both T and N.

Just add or null to the count expression
count(e.emp_pf_acc_number like 'T%' or null) as tcnt,
count(e.emp_pf_acc_number like 'N%' or null) as ncnt
count counts not nulls. As your original expression returns either true or false it would always be counted. false or null evaluates to null so it is not counted.
There is an optimization to the where clause
and exists (
select 1
from pfmast_dist
where m.institution_dist = dist_name
)

Try this Query:
Select * from
(select m.institution_dist,
Count(e.emp_pf_acc_number like 'T%') as tcnt
from pf_emp e
left join
pfmast_institution m on e.emp_workplace_code = m.institution_code
where e.retired <> 'Y'
and emp_classification_code = '3'
and m.institution_dist in ( select dist_name from pfmast_dist)
group by m.institution_dist ) x
full outer join
(select b.institution_dist,count(a.emp_pf_acc_number like 'N%') as ncnt
from pf_emp a
left join
pfmast_institution b on a.emp_workplace_code = b.institution_code
where b.retired <> 'Y'
and emp_classification_code = '3'
and b.institution_dist in ( select dist_name from pfmast_dist )
group by b.institution_dist) y
on x.institution_dist = y.institution_dist

Related

How do you properly query the result of a complex join statement in SQL?

New to advanced SQL!
I'm trying to write a query that returns the COUNT(*) and SUM of the resulting columns from this query:
DECLARE #Id INT = 1000;
SELECT
*,
CASE
WHEN Id1 >= 6 THEN 1
ELSE 0
END AS Tier1,
CASE
WHEN Id1 >= 4 THEN 1
ELSE 0
END AS Tier2,
CASE
WHEN Id1 >= 2 THEN 1
ELSE 0
END AS Tier3
FROM (
SELECT
Org.OrgID,
App.AppID,
App.FirstName,
App.LastName,
MAX(AppSubmitU_Level.Id1) AS Id1
FROM Org
INNER JOIN AppEmployment
ON AppEmployment.OrgID = Org.OrgID
INNER JOIN App
ON App.AppID = AppEmployment.AppID
INNER JOIN AppSubmit
ON App.AppID = AppSubmit.AppID
INNER JOIN AppSubmitU_Level
ON AppSubmit.LevelID = AppSubmitU_Level.Id1
INNER JOIN AppEmpU_VerifyStatus
ON AppEmpU_VerifyStatus.VerifyStatusID = AppEmployment.VerifyStatusID
WHERE AppSubmitU_Level.SubmitTypeID = 1 -- Career
AND AppEmpU_VerifyStatus.StatusIsVerified = 1
AND AppSubmit.[ExpireDate] IS NOT NULL
AND AppSubmit.[ExpireDate] > GETDATE()
AND Org.OrgID = #Id
GROUP BY
Org.OrgID,
App.AppID,
App.FirstName,
App.LastName
) employees
I've tried to do so by moving the #Id outside the original query, and adding a SELECT(*), SUM, and SUM to the top, like so:
DECLARE #OrgID INT = 1000;
SELECT COUNT(*), SUM(employees.Tier1), SUM(employees.Tier2), SUM(employees.Tier3)
FROM
(SELECT *,
...
) AS employees
);
When I run the query, however, I'm getting the errors:
The multi-part identifier employees.Tier1 could not be bound
The same errors appear for the other identifiers in my SUM statements.
I'm assuming this has to do with the fact that the Tier1, Tier2, and Tier3 columns are being returned by the inner join query in my FROM(), and aren't values set by the existing tables that I'm querying. But I can't figure out how to rewrite it to initialize properly.
Thanks in advance for the help!
This is a scope problem: employees is defined in the subquery only, it is not available in the outer scope. You basically want to alias the outer query:
DECLARE #OrgID INT = 1000;
SELECT COUNT(*), SUM(employees.Tier1) TotalTier1, SUM(employees.Tier2) TotalTier2, SUM(employees.Tier3) TotalTier3
FROM (
SELECT *,
...
) AS employees
) AS employees;
--^ here
Note that I added column aliases to the outer query, which is a good practice in SQL.
It might be easier to understand what is going on if you use another alias for the outer query:
SELECT COUNT(*), SUM(e.Tier1), SUM(e.Tier2), SUM(e.Tier3)
FROM (
SELECT *,
...
) AS employees
) AS e;
Note that you don't actually need to qualify the column names in the outer query, since column names are unambigous anyway.
And finally: you don't actually need a subquery. You could write the query as:
SELECT
SUM(CASE WHEN Id1 >= 6 THEN 1 ELSE 0 END) AS TotalTier1,
SUM(CASE WHEN Id1 >= 4 THEN 1 ELSE 0 END) AS TotalTier2,
SUM(CASE WHEN Id1 >= 2 THEN 1 ELSE 0 END) AS TotalTier3
FROM (
SELECT
Org.OrgID,
App.AppID,
App.FirstName,
App.LastName,
MAX(AppSubmitU_Level.Id1) AS Id1
FROM Org
INNER JOIN AppEmployment
ON AppEmployment.OrgID = Org.OrgID
INNER JOIN App
ON App.AppID = AppEmployment.AppID
INNER JOIN AppSubmit
ON App.AppID = AppSubmit.AppID
INNER JOIN AppSubmitU_Level
ON AppSubmit.LevelID = AppSubmitU_Level.Id1
INNER JOIN AppEmpU_VerifyStatus
ON AppEmpU_VerifyStatus.VerifyStatusID = AppEmployment.VerifyStatusID
WHERE AppSubmitU_Level.SubmitTypeID = 1 -- Career
AND AppEmpU_VerifyStatus.StatusIsVerified = 1
AND AppSubmit.[ExpireDate] IS NOT NULL
AND AppSubmit.[ExpireDate] > GETDATE()
AND Org.OrgID = #Id
GROUP BY
Org.OrgID,
App.AppID,
App.FirstName,
App.LastName
) employees

Rewrite a query with GROUP BY ALL

Microsoft has deprecated GROUP BY ALL and while the query might work now, I'd like to future-proof this query for future SQL upgrades.
Currently, my query is:
SELECT qt.QueueName AS [Queue] ,
COUNT ( qt.QueueName ) AS [#ofUnprocessedEnvelopes] ,
COUNT ( CASE WHEN dq.AssignedToUserID = 0 THEN 1
ELSE NULL
END
) AS [#ofUnassignedEnvelopes] ,
MIN ( dq.DocumentDate ) AS [OldestEnvelope]
FROM dbo.VehicleReg_Documents_QueueTypes AS [qt]
LEFT OUTER JOIN dbo.VehicleReg_Documents_Queue AS [dq] ON dq.QueueID = qt.QueueTypeID
WHERE dq.IsProcessed = 0
AND dq.PageNumber = 1
GROUP BY ALL qt.QueueName
ORDER BY qt.QueueName ASC;
And the resulting data set:
<table><tbody><tr><td>Queue</td><td>#ofUnprocessedEnvelopes</td><td>#ofUnassignedEnvelopes</td><td>OldestEnvelope</td></tr><tr><td>Cancellations</td><td>0</td><td>0</td><td>NULL</td></tr><tr><td>Dealer</td><td>26</td><td>17</td><td>2018-04-06</td></tr><tr><td>Matched to Registration</td><td>93</td><td>82</td><td>2018-04-04</td></tr><tr><td>New Registration</td><td>166</td><td>140</td><td>2018-03-21</td></tr><tr><td>Remaining Documents</td><td>2</td><td>2</td><td>2018-04-04</td></tr><tr><td>Renewals</td><td>217</td><td>0</td><td>2018-04-03</td></tr><tr><td>Transfers</td><td>296</td><td>245</td><td>2018-03-30</td></tr><tr><td>Writebacks</td><td>53</td><td>46</td><td>2018-04-09</td></tr></tbody></table>
I've tried various versions using CTE's and UNION's but I cannot get result set to generate correctly - the records that have no counts will not display or I will have duplicate records displayed.
Any suggestions on how to make this work without the GROUP BY ALL?
Below is one attempt where I tried a CTE with a UNION:
;WITH QueueTypes ( QueueTypeID, QueueName )
AS ( SELECT QueueTypeID ,
QueueName
FROM dbo.VehicleReg_Documents_QueueTypes )
SELECT qt.QueueName AS [Queue] ,
COUNT ( qt.QueueName ) AS [#ofUnprocessedEnvelopes] ,
COUNT ( CASE WHEN dq.AssignedToUserID = 0 THEN 1
ELSE NULL
END
) AS [#ofUnassignedEnvelopes] ,
CONVERT ( VARCHAR (8), MIN ( dq.DocumentDate ), 1 ) AS [OldestEnvelope]
FROM QueueTypes AS qt
LEFT OUTER JOIN dbo.VehicleReg_Documents_Queue AS dq ON dq.QueueID = qt.QueueTypeID
WHERE dq.IsProcessed = 0
AND dq.PageNumber = 1
GROUP BY qt.QueueName
UNION ALL
SELECT qt.QueueName AS [Queue] ,
COUNT ( qt.QueueName ) AS [#ofUnprocessedEnvelopes] ,
COUNT ( CASE WHEN dq.AssignedToUserID = 0 THEN 1
ELSE NULL
END
) AS [#ofUnassignedEnvelopes] ,
CONVERT ( VARCHAR (8), MIN ( dq.DocumentDate ), 1 ) AS [OldestEnvelope]
FROM QueueTypes AS qt
LEFT OUTER JOIN dbo.VehicleReg_Documents_Queue AS dq ON dq.QueueID = qt.QueueTypeID
GROUP BY qt.QueueName
But the results are not close to being correct:
Your current query doesn't work as it seems to work, because while you outer join table VehicleReg_Documents_Queue you dismiss all outer joined rows in the WHERE clause, so you are where you would have been with a mere inner join. You may want to consider either moving your criteria to the ON clause or make this an inner join right away.
It is also weird that you join queue type and queue not on the queue ID or the queue type ID, but on dq.QueueID = qt.QueueTypeID. That's like joining employees and addresses on employee number matching the house number. At least that's what it looks like.
(Then why does your queue type table have a queue name? Shouldn't the queue table contain the queue name instead? But this is not about your query, but about your data model.)
GROUP BY ALL means: "Please give us all QueueNames, even when the WHERE clause dismisses them. I see two possibilities for your query:
You do want an outer join actually. Then there is no WHERE clause and you can simply make this GROUP BY qt.QueueName.
You don't want an outer join. Then we want a row per QueueName in the table, which we might not get with simply changing GROUP BY ALL qt.QueueName to GROUP BY qt.QueueName.
In that second case we want all QueueNames first and outer join your query:
select
qn.QueueName AS [Queue],
q.[#ofUnassignedEnvelopes],
q.[OldestEnvelope]
FROM (select distinct QueueName from VehicleReg_Documents_QueueTypes) qn
LEFT JOIN
(
SELECT qt.QueueName,
COUNT ( qt.QueueName ) AS [#ofUnprocessedEnvelopes] ,
COUNT ( CASE WHEN dq.AssignedToUserID = 0 THEN 1
ELSE NULL
END
) AS [#ofUnassignedEnvelopes] ,
MIN ( dq.DocumentDate ) AS [OldestEnvelope]
FROM dbo.VehicleReg_Documents_QueueTypes AS [qt]
JOIN dbo.VehicleReg_Documents_Queue AS [dq] ON dq.QueueID = qt.QueueTypeID
WHERE dq.IsProcessed = 0
AND dq.PageNumber = 1
) q ON q.QueueName = qn.QueueName
GROUP BY ALL qn.QueueName
ORDER BY qn.QueueName ASC;
I think the best corollary here for a 'GROUP BY ALL' into something more ANSI compliant would be a CASE statement. Without knowing your data, it's hard to say for sure if this is 1:1, but I'm betting it's in the ballpark.
SELECT qt.QueueName AS [Queue]
,COUNT(CASE
WHEN dq.IsProcessed = 0
AND dq.PageNumber = 1
THEN qt.QueueName
END) AS [#ofUnprocessedEnvelopes]
,COUNT(CASE
WHEN dq.AssignedToUserID = 0
AND dq.IsProcessed = 0
AND dq.PageNumber = 1
THEN 1
ELSE NULL
END) AS [#ofUnassignedEnvelopes]
,MIN(CASE
WHEN dq.IsProcessed = 0
AND dq.PageNumber = 1
THEN dq.DocumentDate
END) AS [OldestEnvelope]
FROM dbo.VehicleReg_Documents_QueueTypes AS [qt]
LEFT OUTER JOIN dbo.VehicleReg_Documents_Queue AS [dq] ON dq.QueueID = qt.QueueTypeID
GROUP BY qt.QueueName
ORDER BY qt.QueueName ASC;
That's a bit uglier because every aggregate has to have the WHERE conditions inside a case statement, but at least you are future proof.

SQL sub query not working with group by clause

I am using SQL Server 2012. Can anyone tell me where i am going wrong ?
SELECT
avg ( tbl.FirstBillComplete )
FROM
( select l.MONTH, a.OverallScore, (a.FirstBillComplete), ( a.EmailComplete)
from tbl_T1 a join calls.dbo.c1_LP l on a.QID = l.QID
union
select l.MONTH, a.OverallScore, (a.FirstBillComplete), ( a.EmailComplete)
from tbl_2 a join calls.dbo.C3_LP l on a.QID = l.QID
union ALL
select l.MONTH, a.OverallScore, (a.FirstBillComplete), ( a.EmailComplete)
from tbl_3 a join c2 l on a.QID = l.QID
) As tbl
GROUP BY tbl.MONTH
The error I get is :
No column was specified for column 7 of 'tbl'.
No column was specified for column 8 of 'tbl'
You need to specify a column name for column 7 and 8 of tbl: use
'' AS MyColumn7,
For example.

How to find rows that have one equal value and one different value from the table

I have the following table:
ID Number Revision
x y 0
x y 1
z w 0
a w 0
a w 1
b m 0
b m 0
I need to return rows that for the same Number thare are more then one ID with the same Revision.Number can be "Null" and I don't need those values.
The output should be:
z w 0
a w 0
I have tried the following query:
SELECT a.id,a.number,a.revision,
FROM table a INNER JOIN
(SELECT id, number, revision FROM table where number > '0'
GROUP BY number HAVING COUNT(*) > 1
) b ON a.revision = b.revision AND a.id != b.id
A little addition- I have rows in my table with the same Number, ID and Revision- I don't need those rows in my query to be displayed!
It is not working! Please help me to figure out how to fix it.
Thanks.
Select t.Id,s.number,t.revision
from (Select number,count(*) 'c'
from table t1
where revision=0
group by number
having count(*) > 1
) s join table t on t.number= s.number
where revision = 0
Another simple approach:
SELECT DISTINCT b.id, b.Number, b.Revision
FROM tbl a
INNER JOIN tbl b
ON a.ID != b.ID AND a.Number = b.Number AND a.Revision = b.Revision;
This is tested in MySql 5, syntax might differ slightly.
You are not that far away with your query:
SELECT a.id,a.number,a.revision
FROM table a
JOIN (
-- multiple id for the same number and revision
SELECT number, revision
FROM table
GROUP BY number, revision
HAVING COUNT(*) > 1
) b
ON a.revision = b.revision
AND a.number = b.number
Untested, but you should get the idea. If your sql-server is a resent version you can solve this with OLAP functions as well.
To filter out rows where the whole row is duplicated we can select only unique rows via group by and having:
SELECT a.id,a.number,a.revision
FROM table a
JOIN (
-- multiple id for the same number and revision
SELECT number, revision
FROM table
GROUP BY number, revision
HAVING COUNT(*) > 1
) b
ON a.revision = b.revision
AND a.number = b.number
GROUP BY a.id,a.number,a.revision
HAVING COUNT(1) = 1

Replace no result

I have a query like this:
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
order by V.IDTipoVersamento,month(DataAllibramento)
This query must always return something. If no result is produced a
0 0 0 0
row must be returned. How can I do this. Use a isnull for every selected field isn't usefull.
Use a derived table with one row and do a outer apply to your other table / query.
Here is a sample with a table variable #T in place of your real table.
declare #T table
(
ID int,
Grp int
)
select isnull(Q.MaxID, 0) as MaxID,
isnull(Q.C, 0) as C
from (select 1) as T(X)
outer apply (
-- Your query goes here
select max(ID) as MaxID,
count(*) as C
from #T
group by Grp
) as Q
order by Q.C -- order by goes to the outer query
That will make sure you have always at least one row in the output.
Something like this using your query.
select isnull(Q.TipoVers, '0') as TipoVers,
isnull(Q.ImpTot, 0) as ImpTot,
isnull(Q.N, 0) as N,
isnull(Q.Mese, 0) as Mese
from (select 1) as T(X)
outer apply (
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese,
V.IDTipoVersamento
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
) as Q
order by Q.IDTipoVersamento, Q.Mese
Use COALESCE. It returns the first non-null value. E.g.
SELECT COALESCE(TV.Desc, 0)...
Will return 0 if TV.DESC is NULL.
You can try:
with dat as (select TV.[Desc] as TipyDesc, sum(Import) as ToImp, count(*) as N, month(Date) as Mounth
from /*DATA SOURCE HERE*/ as TV
group by [Desc], month(Date))
select [TipyDesc], ToImp, N, Mounth from dat
union all
select '0', 0, 0, 0 where (select count (*) from dat)=0
That should do what you want...
If it's ok to include the "0 0 0 0" row in a result set that has data, you can use a union:
SELECT TV.Desc as TipyDesc,
sum(Import) as TotImp,
count(*) as N,
month(Date) as Mounth
...
UNION
SELECT
0,0,0,0
Depending on the database, you may need a FROM for the second SELECT. In Oracle, this would be "FROM DUAL". For MySQL, no FROM is necessary