Problem with count in SQL query - sql

I want to create a query that show the name of the employee and the sum of all service that aren't not closed. The result of the query is not giving me the correct value.
Here is my query:
SELECT DISTINCT
a.AssignedEmp,
COUNT( a.TipoStatus ) AS 'Service Request Count'
FROM Service a,
employee b
WHERE b.Classification_ClassificationID = 2
AND a.TipoStatus = 'Open'
OR a.TipoStatus = 'Pending'
OR a.TipoStatus = 'Hold'
OR a.TipoStatus = 'Warranty'
AND a.AssignedEmp = b.UserName
GROUP BY a.AssignedEmp
LIMIT 0, 30
There result that this query gives me is:
dpadil 16
epadil 8
The correct values are supposed to be 2 and 1.

The first thing you should do is get rid of the count and the group by.
This will give you the actual rows that are being used and will probably show you the problem straight up.
The fact that the ratios are correct (16:8 = 2:1) usually indicates that AND a.AssignedEmp = b.UserName is not as limiting as you think.
In other words, the cross-table joins are more prolific. Getting the rows themselves out should help you understand why.

You need brackets around your OR'd conditions (and should use JOIN notation):
SELECT DISTINCT
a.AssignedEmp,
COUNT( a.TipoStatus ) AS 'Service Request Count'
FROM Service AS a
JOIN employee AS b ON a.AssignedEmp = b.UserName
WHERE b.Classification_ClassificationID = 2
AND (a.TipoStatus = 'Open'
OR a.TipoStatus = 'Pending'
OR a.TipoStatus = 'Hold'
OR a.TipoStatus = 'Warranty')
GROUP BY a.AssignedEmp
LIMIT 0, 30
Or you can write the condition directly in terms of 'not closed':
SELECT DISTINCT
a.AssignedEmp,
COUNT( a.TipoStatus ) AS 'Service Request Count'
FROM Service AS a
JOIN employee AS b ON a.AssignedEmp = b.UserName
WHERE b.Classification_ClassificationID = 2
AND a.TipoStatus != 'Closed'
GROUP BY a.AssignedEmp
LIMIT 0, 30
As originally written, the WHERE clause was equivalent to:
WHERE (b.Classification_ClassificationID = 2 AND a.TipoStatus = 'Open')
OR a.TipoStatus = 'Pending'
OR a.TipoStatus = 'Hold'
OR (a.TipoStatus = 'Warranty' AND a.AssignedEmp = b.UserName)
This counts all the Open service items that have a classification ID of 2 for the user; it also counts all the Pending service items regardless of classification ID, and all the Hold service items regardless of classification ID, and all the Warranty service items assigned to the user, regardless of classification ID. The GROUP BY filters things so that only the items with the right assigned employee are counted, but there is a partial cross-product for some of the terms, leading to the inflated counts.

You probably want to structure your condition a bit more explicitly.
You should also specify the JOIN condition using a JOIN statement, rather than an additional condition.
SELECT DISTINCT
a.AssignedEmp,
COUNT( a.TipoStatus ) AS 'Service Request Count'
FROM Service a
INNER JOIN employee b
ON a.AssignedEmp = b.UserName
WHERE b.Classification_ClassificationID = 2
AND (
a.TipoStatus = 'Open'
OR a.TipoStatus = 'Pending'
OR a.TipoStatus = 'Hold'
OR a.TipoStatus = 'Warranty'
)
GROUP BY a.AssignedEmp
LIMIT 0, 30

Related

How to update data with multi condition

I have a table with 5 columns like this table below:
I wanna fill the approval column based on conditions such as:
APPROVAL = "Y" IN CASE
(1) CUSTOMER_NEW <> CUSTOMER (as in the case CUSTOMER = 12467)
(2) CUSTOMER_NEW = CUSTOMER AND SALE_SHORT_ID COLUMN HAS DIFFERENT VALUE (as in the case CUSTOMER = 13579)
(3) CUSTOMER_NEW = CUSTOMER AND SALE_SHORT_ID HAS THE SAME VALUE THEN MAX(LEN(SALE_ID) (as in the case CUSTOMER = 65465)
ELSE "N"
My result expected to like this table:
Thanks for your support.
I am not sure about all your your conditions but this one could do it:
UPDATE a
SET approval =
CASE WHEN
a.customer <> a.customer_new OR
a.customer = a.customer_new AND b.sale_short_eq = 0 OR
a.customer = a.customer_new AND a.sale_id = b.max_len_sale_id
THEN 'Y'
ELSE 'N'
END
FROM
thetable a
INNER JOIN (
SELECT
customer,
CASE WHEN MIN(sale_short_id) = MAX(sale_short_id) THEN 1 ELSE 0 END AS sale_short_eq,
( SELECT TOP 1 sale_id
FROM thetable
WHERE customer = c.customer
ORDER BY LEN(sale_id) DESC
) AS max_len_sale_id
FROM thetable c
GROUP BY customer
) b
ON a.customer = b.customer
Note that the columns b.sale_short_eq and b.max_len_sale_id are returned by a sub-query joined to the main query.
Especially SALE_SHORT_ID HAS THE SAME VALUE THEN MAX(LEN(SALE_ID) is not clear. Did you mean that SALE_ID must be the same as the maximum length SALE_ID? Because SALE_SHORT_ID cannot be the same than any SALE_ID. And what happens if two different SALE_IDs have the same max length?
See: https://dbfiddle.uk/7Ct87-Mk

use distinct within case statement

I have a query that uses multiple left joins and trying to get a SUM of values from one of the joined columns.
SELECT
SUM( case when session.usersessionrun =1 then 1 else 0 end) new_unique_session_user_count
FROM session
LEFT JOIN appuser ON appuser.appid = '6279df3bd2d3352aed591583'
AND appuser.userid = session.userid
LEFT JOIN userdevice ON userdevice.appid = '6279df3bd2d3352aed591583'
AND userdevice.userid = appuser.userid
WHERE session.appid = '6279df3bd2d3352aed591583'
AND (session.uploadedon BETWEEN '2022-04-18 08:31:26' AND '2022-05-18 08:31:26')
But this obviously gives a redundant session.usersessionrun=1 counts since it's a joined resultset.
Here the logic was to mark the user as new if the sessionrun for that record is 1.
I grouped by userid and usersessionrun and it shows that the records are repeated.
userid. sessionrun. count
628212 1 2
627a01 1 4
So what I was trying to do was something like
SUM(CASE distinct(session.userid) AND WHEN session.usersessionrun = 1 THEN 1 ELSE 0 END) new_unique_session_user_count
i.e. for every unique user count, session.usersessionrun = 1 should only be done once.
As you have discovered, JOIN operations can generate combinatorial explosions of data.
You need a subquery to count your sessions by userid. Then you can treat the subquery as a virtual table and JOIN it to the other tables to get the information you need in your result set.
The subquery (nothing in my answer is debugged):
SELECT COUNT(*) new_unique_session_user_count,
session.userid
FROM session
WHERE session.appid = '6279df3bd2d3352aed591583'
AND session.uploadedon BETWEEN '2022-04-18 08:31:26'
AND '2022-05-18 08:31:26'
AND session.usersessionrun = 1
AND session.appid = '6279df3bd2d3352aed591583'
GROUP BY userid
This subquery summarizes your session table and has one row per userid. The trick to avoiding JOIN-created combinatorial explosions is using subqueries that generate results with only one row per data item mentioned in a JOIN's ON-clause.
Then, you join it with the other tables like this
SELECT summary.new_unique_session_user_count
FROM (
SELECT COUNT(*) new_unique_session_user_count,
session.userid
FROM session
WHERE session.appid = '6279df3bd2d3352aed591583'
AND session.uploadedon BETWEEN '2022-04-18 08:31:26'
AND '2022-05-18 08:31:26'
AND session.usersessionrun = 1
AND session.appid = '6279df3bd2d3352aed591583'
GROUP BY userid
) summary
JOIN appuser ON appuser.appid = '6279df3bd2d3352aed591583'
AND appuser.userid = summary.userid
JOIN userdevice ON userdevice.appid = '6279df3bd2d3352aed591583'
AND userdevice.userid = appuser.userid
There may be better ways to structure this query, but it's hard to guess at them without more information about your table definitions and business rules.

SQL query with GROUP BY and HAVING COUNT(condition) in ORACLE

I have three tables: temp, product and feed.
I'll show on example:
select ri.id from temp ri
inner join product i on ri.id = to_char(i.val)
inner join feed f on f.product_id = i.product_id
where i.status = 'Finished'
and f.type = 'Type'
group by f.feed_id, ri.id
having COUNT(f.status = 'SUCCESS') < 1;
so I tried to get all ids from temp that have f.type = 'Type'. Problem is that for one feed.feed_id can be many rows because I could retrigger it 5 times and let's say 4 times it crashed but at 5th attempt it was SUCCESS, so for one feed.feed_id I would have 5 rows and only one would be with f.status = SUCCESS.
Error which I receive for this query is ORA-00907: missing right parenthesis which makes me totally confused.
feed table:
feed_id, status, type
I am interested in all feed_id which don't have even one status='SUCCESS' for type='TYPE'
You can't COUNT a boolean expression in Oracle, you can use a CASE expression instead e.g.
HAVING COUNT(CASE WHEN f.status = 'SUCCESS' THEN 1 END) < 1
This expression returns NULL when the condition is false, so it will only count the rows for which the condition is true (since COUNT of an expression ignores NULL values).
Note also (as #GordonLinoff points out in the comments) that since COUNT cannot return a negative number, it is cleaner (and would be more efficient) to simply compare the result for equality with 0, rather than being less than 1:
HAVING COUNT(CASE WHEN f.status = 'SUCCESS' THEN 1 END) = 0

TSQL Select element with only one value

I have a project in which I need to save request for file. I have to save the state of the request with the date of each step.
I have two tables in my database :
FileRequest :
FileRequestStatus :
I would like to create a request to get each FileName from the table FileRequest having only the Status == 'NEW'. The desired output would be in this case C12345, LIVE.
If you have a better idea in order to build my database, I'll take it
I tried something like that :
SELECT [FileName] FROM [FileRequest]
INNER JOIN [FileRequestStatus] ON [FileRequestStatus].[RequestId] = [FileRequest].[RequestId]
GROUP BY [FileRequestStatus].[RequestId]
HAVING COUNT([FileRequestStatus].[RequestStatus]) < 2
SELECT FR.FileName
FROM [FileRequest] FR
INNER JOIN [FileRequestStatus] FRS ON FRS.[RequestId] = FR.[RequestId]
GROUP BY FR.FileName
HAVING COUNT(CASE WHEN FRS.RequestStatus <> 'New' THEN 1 END) = 0 --No statuses other than `NEW`
AND COUNT(CASE WHEN FRS.RequestStatus = 'New' THEN 1 END) >= 1 --Atleast one status `NEW`
I think the simplest method is aggregation:
select fr.FileName
from nep.FileRequest fr join
nep.FileRequestStatus] frs
on frs.RequestId = fr.RequestId
group by fr.FileName
having min(frs.RequestStatus) = max(frs.RequestStatus) and
min(frs.RequestStatus) = 'New';
Note: This assumes that the request status is never NULL (although that is easy to take into account).
What this does is aggregate by file name and then check that the statuses for a given file are all equal (the first condition) and equal to 'New' (the second condition).
You need to include [FileName] to your GROUP BY, then you can select it in result
SELECT [nep].[FileRequest].[FileName] FROM [nep].[FileRequest]
INNER JOIN [nep].[FileRequestStatus] ON [nep].[FileRequestStatus].[RequestId] = [nep].[FileRequest].[RequestId]
GROUP BY [nep].[FileRequestStatus].[RequestId], [nep].[FileRequest].[FileName]
HAVING COUNT([nep].[FileRequestStatus].[RequestStatus]) < 2

I want SQLplus count(*) to return zero-values

I'm using Oracle 10.2 and have the following query:
select h.company, count(*)
from history h
where h.status = '2'
and h.substatus = '0'
and h.timestamp > '2012-01-01'
and h.timestamp < '2012-02-01'
group by h.company
having (h.company = 'AAA')
or (h.company = 'BBB')
or (h.company = 'CCC')
order by h.company
This will count the amount of times any customer from the companies AAA, BBB or CCC has reached a specific point (status = 2).
Presume no (zero) customers from BBB did so, the result will come back with 2 rows of count(AAA and CCC).
What I want: I wish for the query to return me rows for all 3 companies, even tho the count is zero.
Sorry for the odd layout of the query. It's made to work with MS Excel as well.
Edit: Sorry.. Too little caffeine. changed "Customer" in the later half of the query to "Company".
Clarification: A customer is made unique by combining "h.company" and "h.customer" (or by using the same method in customer-table (customer c), like "c.company" and "c.customer"
Edit 2: Updated code.
select c.company, count(*)
from companyregister c
left join history h on h.company = c.company
where h.status = '2'
and h.substatus = '0'
and h.timestamp > '2012-01-01'
and h.timestamp < '2012-02-01'
group by c.company
having (c.company = 'AAA')
or (c.company = 'BBB')
or (c.company = 'CCC')
order by c.company
Both sets of code from above will yield two rows as follows:
AAA 630
CCC 3020
I would like to have BBB represented, but since they have zero rows in history, they don't show.
Make a left join on the customer table. I don't know what you have named it, but like this:
select c.company, count(h.customer)
from customer as c
left join history as h on h.customer = c.customer
...
Another alternative is to use a condition when counting. I'm not sure if there is any other condition that you need along with the status, but something liket this:
select company, sum(case status when '2' then 1 else 0 end)
from history
where substatus = '0'
and timestamp > '2012-01-01'
and timestamp < '2012-02-01'
and customer in ('AAA', 'BBB', 'CCC')
group by customer
order by customer