Joining 2 sql selects - sql

I have two very similar sql statements
select instrumentuniqueid, count(levelid) as errors
from dbo.testevent
join dbo.test
on dbo.test.id = dbo.testevent.testid where dbo.test.runid = 20962 and dbo.testevent.levelid = 1
group by instrumentuniqueid
select instrumentuniqueid, count(levelid) as warnings
from dbo.testevent
join dbo.test
on dbo.test.id = dbo.testevent.testid where runid = 20962 and levelid = 2
group by instrumentuniqueid
The first one produces columns of instrumentuniqueid (aggregated) and the count
The second one produces columns of aggregated instrumentuniqueid with a different count.
How can I join them together so that the final table looks like:
Instrumentuniqueid | Errors | Warnings

Use conditional aggregation:
select instrumentuniqueid,
sum(case when te.levelid = 1 then 1 else 0 end) as errors,
sum(case when te.levelid = 2 then 1 else 0 end) as warnings
from dbo.testevent te join
dbo.test t
on t.id = t2.testid
where t.runid = 20962
group by instrumentuniqueid;
Table aliases also make a query easier to write and to read.

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.

Incorrect count value in subquery on table join column

The query I'm executing seems to be ignoring the where clause in the subquery
(select count(amazon) from orders where b.amazon = 2 and manifest = a.dbid)
column amazon is type INT
SQL SERVER 2014
If I run the query on its own and enter the value for manifest I get the correct result which I am expecting and is 1
select count(amazon) from orders where amazon = 2 and manifest = '211104'
Result Returns 1
When I run the query below I get a result of 5 which is the count of all orders where manifest = 211104 but the value of amazon is 1 in 4 results and 2 in 1 result.
Select distinct
top 30 DBID, today ,sum([amazon-orders])
From
(
SELECT [dbid], [today],
(select count(amazon) from orders
where b.amazon = 2 and manifest = a.dbid) as [amazon-orders]
FROM [manifest] a
join orders b on a.[dbid] = b.[manifest]
) t1
Group By
DBID, today
order by dbid desc
Can someone please help me.
Thanks
You have an extra join so you are counting multiple times... do this:
Select distinct
top 30 DBID, today ,sum([amazon-orders])
From
(
SELECT [dbid], [today],
(select count(amazon) from orders b
where b.amazon = 2 and manifest = a.dbid) as [amazon-orders]
FROM [manifest] a
) t1
Group By
DBID, today
order by dbid desc
or like this
SELECT [dbid], [today], count(o.amazon)
FROM [manifest] a
join orders o on a.dbid = o.manifest and o.amazon = 2
group by dbid, today
or this if you have columns you don't want to join (there is more going on than just this one join in your query and you need to use a left join):
SELECT [dbid], [today], sum(case when o.amazon is not null then 1 else 0 end)
FROM [manifest] a
left join orders o on a.dbid = o.manifest and o.amazon = 2
group by dbid, today
How's this?
SELECT top 30 [dbid], [today], sum(case when b.amazon = 2 then 1 else 0 end) as [amazon-orders]
FROM [manifest] a
join orders b on a.[dbid] = b.[manifest]
group by DBID, today
order by dbid desc
Pretty sure that because your query is in effect joining orders twice it's increasing the count.
Use this :
select a.DBID, a.today, count(b.amazon) from [manifest] a
join orders b on a.[dbid] = b.[manifest] and b.amazon = 2
Group By a.DBID, a.today

Query to Not selecting columns if multiple

I've been scratching my head about this.
I have a table with multiple columns for the same project.
However, each project can have multiple rows of a different type.
I would like to find only projects type O and only if they don't have other types associated with them.
Ex:
Project_Num | Type
1 | O
1 | P
2 | O
3 | P
In the case above, only project 2 should be returned.
Is there a query or a method to filter this information? Any suggestions are welcome.
If I understand correctly, you want to check that the project has only record for its project number and it has type 'O'. You can use below query to implement this:
;with cte_proj as
(
select Project_Num from YourTable
group by Project_Num
having count(Project_Num) = 1)
select Project_Num from cte_proj c
inner join YourTable t on c.Project_Num = t.Project_Num
where t.Type = 'O'
You can do this using not exists:
select p.*
from projects p
where type = 'O' and
not exists (select 1
from projects p2
where p2.project_num = p.project_num and p2.type <> 'O'
);
You can also do this using aggregation:
select p.project_num
from projects p
group by p.project_num
having sum(case when p.type = 'O' then 1 else 0 end) > 0 and
sum(case when p.type <> 'O' then 1 else 0 end) = 0;
Another option (pretty fast)
SELECT p0.*
FROM project p0
LEFT JOIN project p1 ON (p0.Type<>p1.Type AND p0.Project_Num=p1.Project_Num)
WHERE p0.Type='O' AND p1.Type IS NULL;

In Oracle how to search with sub conditions on same column

In Oracle how do I find Cars which must have Feature1 and have at lest, one out of Feature2 or Feature3. Sample table and expected result should look like below screenshot. Thanks Kiran
This should work:
select t1.car, t1.feature
from yourtable t1
inner join
( -- inner select returns the cars with the Feature1 and Feature2 or Feature3
select car, feature
from yourtable
where feature = 'Feature1'
and exists (select car
from yourtable
where feature in ('Feature2', 'Feature3'))
) t2
on t1.car = t2.car
where t1.feature in ('Feature1', 'Feature2', 'Feature3') -- this excludes any other features
see SQL Fiddle with Demo
I like to do this with GROUP BY and HAVING:
select car
from t
group by car
having max(case when feature = 'Feature1' then 1 else 0 end) = 1 and
max(case when feature in ('Feature1', 'Feature2') then 1 else 0 end) = 1
This query returns the cars. To get the featuers as well, you have to join tis back in:
select t.*
from (select car
from t
group by car
having max(case when feature = 'Feature1' then 1 else 0 end) = 1 and
max(case when feature in ('Feature1', 'Feature2') then 1 else 0 end) = 1
) c join
t
on t.car = c.car
I like this method, because the same idea can be used for handling many different similar queries -- AND conditions, OR conditions, different subgroups, and different counts.