Sql query using count(*) , groupby and fetching a column value simultaneously - sql

I am been developing application in java and not much insight into sql.
Meanwhile , I have got to fetch count and username in the same query. My query goes like this :
SELECT count(*),c_user_name
FROM tra_shipment_status
WHERE i_tra_shipment_status_id IN
(SELECT MAX(i_tra_shipment_status_id)
FROM tra_shipment_status
WHERE i_status_code = '4072'
AND c_reference IN ('FILEIO0023','MIASTOFIL003')
Group By C_Reference
);
This throws me an exception "not a single-group group function"
I want the count and C_user_name of the top most row (latest one).
Can somebody please help.

If you use count(*) in the main query you also need a group by there:
SELECT count(*),c_user_name
FROM tra_shipment_status
WHERE i_tra_shipment_status_id IN
(SELECT MAX(i_tra_shipment_status_id)
FROM tra_shipment_status
WHERE i_status_code = '4072'
AND c_reference IN ('FILEIO0023','MIASTOFIL003')
Group By C_Reference
)
Group by c_user_name

When you try to include an aggregate function like count, sum, max,.. you should use group by with other unaggregated columns like c_user_name in your case. There is another thing; you say that you need the top most row but your sub query will return the top most row for each C_Reference, and to get the top most row regardless of C_Reference you should do something like the following:
SELECT count(*),c_user_name
FROM tra_shipment_status
WHERE i_tra_shipment_status_id IN
(SELECT MAX(i_tra_shipment_status_id)
FROM tra_shipment_status
WHERE i_status_code = '4072'
AND c_reference IN ('FILEIO0023','MIASTOFIL003')
);
Group By c_user_name

Related

How to get top n rows from a dense_rank()

I'm trying to get collect the top customers from each region however I seem to run into the issue of not being able to specify that I want only the top customers.
I keep getting this error: ORA-00904: "RANK_UNITPRICE": invalid identifier and I know its coming from the where statement but I don't know how else to filter to get my expected output.
Here's what my code looks like:
select reg_name, cus_lname, sum(sale_units * sale_price) as total_sold,
rank() over (partition by reg_name order by sum(sale_units * sale_price) desc) as rank_unitprice
from dwregion
inner join dwcustomer on dwregion.reg_id = dwcustomer.reg_id
inner join dwsalesfact on dwcustomer.cus_code = dwsalesfact.cus_code
where rank_unitprice = 1
group by reg_name, cus_lname
It works if I take the where statement out the code works fine as expected (correct ranks and correct values) but its unfiltered (obviously).
How would I go about fixing this issue?
I'm using Oracle live and you can find the script here if it helps.
At first I didn't understand what something that you should do ("take the where clause out of code") doesn't work. Then I realized that you probably did it wrong - "out" here means that current query should be a subquery (or a CTE). Something like this:
WITH
temp
AS
( SELECT reg_name,
cus_lname,
SUM (sale_units * sale_price) AS total_sold,
RANK ()
OVER (PARTITION BY reg_name
ORDER BY SUM (sale_units * sale_price) DESC) AS rank_unitprice
FROM dwregion
INNER JOIN dwcustomer ON dwregion.reg_id = dwcustomer.reg_id
INNER JOIN dwsalesfact
ON dwcustomer.cus_code = dwsalesfact.cus_code
GROUP BY reg_name, cus_lname)
SELECT t.*
FROM temp
WHERE t.rank_unitprice = 1

Count query giving wrong column name error

select COUNT(analysed) from Results where analysed="True"
I want to display count of rows in which analysed value is true.
However, my query gives the error: "The multi-part identifier "Results.runId" could not be bound.".
This is the actual query:
select ((SELECT COUNT(*) AS 'Count'
FROM Results
WHERE Analysed = 'True')/failCount) as PercentAnalysed
from Runs
where Runs.runId=Analysed.runId
My table schema is:
The value I want for a particular runId is: (the number of entries where analysed=true)/failCount
EDIT : How to merge these two queries?
i) select runId,Runs.prodId,prodDate,prodName,buildNumber,totalCount as TotalTestCases,(passCount*100)/(passCount+failCount) as PassPercent,
passCount,failCount,runOwner from Runs,Product where Runs.prodId=Product.prodId
ii) select (cast(counts.Count as decimal(10,4)) / cast(failCount as decimal(10,4))) as PercentAnalysed
from Runs
inner join
(
SELECT COUNT(*) AS 'Count', runId
FROM Results
WHERE Analysed = 'True'
GROUP BY runId
) counts
on counts.runId = Runs.runId
I tried this :
select runId,Runs.prodId,prodDate,prodName,buildNumber,totalCount as TotalTestCases,(passCount*100)/(passCount+failCount) as PassPercent,
passCount,failCount,runOwner,counts.runId,(cast(counts.Count as decimal(10,4)) / cast(failCount as decimal(10,4))) as PercentAnalysed
from Runs,Product
inner join
(
SELECT COUNT(*) AS 'Count', runId
FROM Results
WHERE Analysed = 'True'
GROUP BY runId
) counts
on counts.runId = Runs.runId
where Runs.prodId=Product.prodId
but it gives error.
Your problems are arising from improper joining of tables. You need information from both Runs and Results, but they aren't combined properly in your query. You have the right idea with a nested subquery, but it's in the wrong spot. You're also referencing the Analysed table in the outer where clause, but it hasn't been included in the from clause.
Try this instead:
select (cast(counts.Count as decimal(10,4)) / cast(failCount as decimal(10,4))) as PercentAnalysed
from Runs
inner join
(
SELECT COUNT(*) AS 'Count', runId
FROM Results
WHERE Analysed = 'True'
GROUP BY runId
) counts
on counts.runId = Runs.runId
I've set this up as an inner join to eliminate any runs which don't have analysed results; you can change it to a left join if you want those rows, but will need to add code to handle the null case. I've also added casts to the two numbers, because otherwise the query will perform integer division and truncate any fractional amounts.
I'd try the following query:
SELECT COUNT(*) AS 'Count'
FROM Results
WHERE Analysed = 'True'
This will count all of your rows where Analysed is 'True'. This should work if the datatype of your Analysed column is either BIT (Boolean) or STRING(VARCHAR, NVARCHAR).
Use CASE in Count
SELECT COUNT(CASE WHEN analysed='True' THEN analysed END) [COUNT]
FROM Results
Click here to view result
select COUNT(*) from Results where analysed="True"

How can I COUNT rows from another table using a SELECT statement when joining?

this is the first time I've tried including a row count within a select statement. I've tried the following but including COUNT(other row) is apparently not allowed in the way I'm trying to do it.
How can I include a row count from another table in a select statement, mainly consisting of objects from the first table?
-Thanks
...
SELECT
Reports.ReportID,
EmployeeADcontext,
ReportName,
CreatedDate,
COUNT(Expenses.ExpID) AS ExpCount,
ReportTotal,
Status
FROM
[dbo].[Reports]
INNER JOIN
[dbo].[Expenses]
ON
[dbo].[Expenses].ReportID = [dbo].[Reports].ReportID
WHERE EmployeeADcontext = #rptEmployeeADcontext
You are missing your GROUP BY. Whenever you aggregate (SUM, COUNT, MAX, etc..) you always need to include a GROUP BY statement that includes all visible fields except your aggregated fields. So your code should read:
SELECT
Reports.ReportID,
EmployeeADcontext,
ReportName,
CreatedDate,
COUNT(Expenses.ExpID) AS ExpCount,
ReportTotal,
Status
FROM
[dbo].[Reports]
INNER JOIN
[dbo].[Expenses]
ON
[dbo].[Expenses].ReportID = [dbo].[Reports].ReportID
WHERE EmployeeADcontext = #rptEmployeeADcontext
GROUP BY Reports.ReportID, EmployeeADcontext, ReportName, CreatedDate,
ReportTotal, Status
Here is some additional documentation on T-SQL GROUP BY.
You need a group by clause.
Add:
GROUP BY
Reports.ReportID,
EmployeeADcontext,
ReportName,
CreatedDate,
ReportTotal,
Status
You could use a sub-query to return the count. That way you don't need any joins. For example:
SELECT
r.ReportID,
r.EmployeeADcontext,
r.ReportName,
r.CreatedDate,
(select COUNT(e1.ExpID) FROM Expenses e1 where e1.ReportID = r.ReportId) AS ExpCount,
r.ReportTotal,
r.Status
FROM Reports r
WHERE r.EmployeeADcontext = #rptEmployeeADcontext

Got a error message when I try to find out which patient account have duplicated record.

When I run the script below, I got a error message "Cannot perform an aggregate function on an expression containing an aggregate or a subquery" Please provide some advice. Thanks
SELECT
CONVERT(DECIMAL(18,5),SUM(CASE WHEN PATIENT_ACCOUNT_NO IN (
SELECT PATIENT_ACCOUNT_NO
FROM STND_ENCOUNTER
GROUP BY PATIENT_ACCOUNT_NO
HAVING ( COUNT(PATIENT_ACCOUNT_NO) > 1)) THEN 0 ELSE 1 END)) dupPatNo
FROM [DBO].[STND_ENCOUNTER]
I think the error message is pretty clear. You have a sum() function with a subquery in it (albeit within a case, but that doesn't matter).
It seems that you want to choose patients that have more than one encounter, then add 0 if the patients is in the list and 1 if the patient is not. Hmmm. . . sounds like you want to count the number of patients with only one encounter.
Try using this logic instead:
select count(*)
from (select se.*, count(*) over (partition by PATIENT_ACCOUNT_NO) as NumEncounters
from dbo.stnd_encounter se
) se
where NumEncounters = 1;
As a note, the variable you are assigning is called DupPatientNo. This sounds like the number of patients that have duplicates. In that case, the query is:
select count(distinct PATIENT_ACCOUNT_NO)
from (select se.*, count(*) over (partition by PATIENT_ACCOUNT_NO) as NumEncounters
from dbo.stnd_encounter se
) se
where NumEncounters > 1;
(Or use count(*) if you want the number of encounters on duplicate patients.)
If you want to find number of PATIENT_ACCOUNT_NO that does not have any duplicates then use the following
SELECT COUNT(DISTINCT dupPatNo.PATIENT_ACCOUNT_NO)
FROM (
SELECT PATIENT_ACCOUNT_NO
FROM STND_ENCOUNTER
GROUP BY PATIENT_ACCOUNT_NO
HAVING COUNT(PATIENT_ACCOUNT_NO) = 1
) dupPatNo
If you want to find number of PATIENT_ACCOUNT_NO that have atleast one duplicate then use the following
SELECT COUNT(DISTINCT dupPatNo.PATIENT_ACCOUNT_NO)
FROM (
SELECT PATIENT_ACCOUNT_NO
FROM STND_ENCOUNTER
GROUP BY PATIENT_ACCOUNT_NO
HAVING COUNT(PATIENT_ACCOUNT_NO) > 1
) dupPatNo
Use of DISTINCT will make the query not count same item again and again
Though your query looks for first result, its not clear what you want. Hence giving query for both

Switch case in aggregate query

I want to have a switch case in my SQL query such that when the group by does not group any element i dont want to aggregate otherwise I want to. Is that possible.
my query is something like this:
select count(1),AVG(student_mark) ,case when Count(1)=1 then student_subjectid else null end from Students
group by student_id
i get this error Column 'student_subjectid' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Thanks in advance..
SELECT
student_id,
COUNT(*) AS MarkCount,
AVG(student_mark) AS student_mark,
CASE COUNT(*) WHEN 1 THEN MIN(student_subjectid) END AS student_subjectid
FROM Students
GROUP BY student_id
Why in the world would you complicate it?
select count(1), AVG(Student_mark) Student_mark
from Students
group by student_id
If there is only one student_mark, it is also the SUM, AVG, MIN and MAX - so just continue to use the aggregate!
EDIT
The dataset that would eventuate with your requirement will not normally make sense. The way to achieve that would be to merge (union) two different results
select
numRecords,
Student_mark,
case when numRecords = 1 then student_subjectid end # else is implicitly NULL
from
(
select
count(1) AS numRecords,
AVG(Student_mark) Student_mark,
min(student_subjectid) as student_subjectid
from Students
group by student_id
) x