Count Distinct in MS Access - How to count number of accounts with at least one activity of a certain type? - sql

I have a list of activities where one field is AccountName indicating the name of the person. There is another field called ActivityType.
Basically I want to return the number of accounts that have at least one ActivityType that is equal to RET (indicating a returned item). Since a person may have multiple returns in a given year I don't want to count the number of returns total, just the number of accounts that have at least one RET.
I've tried various combinations of select statements, count statements, having statements, it's just not working right.
Here is what I tried:
Select DISTINCT Count(Activities.AccountName) AS CountOfAccountName
FROM Activities
GROUP BY Activities.ActivityType
HAVING (Activities.ActivityType = "RET")
But this seems to return a much larger number than if I just do the select statement:
SELECT DISTINCT Activities.AccountName
FROM Activities
WHERE (Activities.ActivityType = "RET")

I think you need a simple COUNT and GROUP BY function. Since Access doesn't allow Count Distinct, You need to first select distinct records and then count them -
SELECT Count(T.AccountName) AS CountOfAccountName
FROM (SELECT AccountName
FROM Activities
WHERE Activities.ActivityType = 'RET'
GROUP BY AccountName) T;
This will filter only those records having ActivityType = 'RET' and then will count the distinct AccountName.

Use a subquery:
SELECT Count(*) AS ActiveAccounts
FROM
(SELECT DISTINCT AccountName
FROM Activities
WHERE ActivityType = "RET")

If activity/types cannot be duplicated, then you can simply use:
select count(*) AS CountOfAccountName
from Activities
where Activities.ActivityType = "RET";
If they can be, then you need a subquery as shown in other answers.

Related

Confused with the Group By function in SQL

Q1: After using the Group By function, why does it only output one row of each group at most? Does this mean that having is supposed to filter the group rather than filter the records in each group?
Q2: I want to find the records in each group whose ages are greater than the average age of that group. I tried the following, but it returns nothing. How should I fix this?
SELECT *, avg(age) FROM Mytable Group By country Having age > avg(age)
Thanks!!!!
You can calculate the average age for each country in a subquery and join that to your table for filtering:
SELECT mt.*, MtAvg.AvgAge
FROM Mytable mt
inner join
(
select mtavgs.country
, avg(mtavgs.age) as AvgAge
from Mytable mtavgs
group by mtavgs.country
) MTAvg
on mtavg.country=mt.country
and mt.Age > mtavg.AvgAge
GROUP BY returns always 1 row per unique combination of values in the GROUP BY columns listed (provided that they are not removed by a HAVING clause). The subquery in our example (alias: MTAvg) will calculate a single row per country. We will use its results for filtering the main table rows by applying the condition in the INNER JOIN clause; we will also report that average by including the calculated average age.
GROUP BY is a keyword that is called an aggregate function. Check this out here for further reading SQL Group By tutorial
What it does is it lumps all the results together into one row. In your example it would lump all the results with the same country together.
Not quite sure what exactly your query needs to be to solve your exact problem. I would however look into what are called window functions in SQL. I believe what you first need to do is write a window function to find the average age in each group. Then you can write a query to return the results you need
Depending on your dbms type and version, you may be able to use a "window function" that will calculate the average per country and with this approach it makes the calculation available on every row. Once that data is present as a "derived table" you can simply use a where clause to filter for the ages that are greater then the calculated average per country.
SELECT mt.*
FROM (
SELECT *
, avg(age) OVER(PARTITION BY country) AS AvgAge
FROM Mytable
) mt
WHERE mt.Age > mt.AvgAge

How DISTINCT statement operates in this query?

In a query,
SELECT modal_text,
COUNT(DISTINCT CASE
WHEN ab_group = ‘control’ THEN user_id
END) AS ‘control_clicks’
FROM onboarding_modals
GROUP BY 1
ORDER BY 1;
I can’t understand why, in line 2, DISTINCT is used. I thought that query will operate well without DISTINCT and it does. How this statement operates in this query?
I think it could serve a purpose, but it could work without given that your data is of a certain form.
Let's go over it step by step:
CASE WHEN ab_group = ‘control’ THEN user_id END)
This returns a list of user_id's from records where ab_group equals 'control'
DISTINCT This filters the list on duplicates If there are no user_id's in multiple records. Or there are, but there is no case in which both of them have ab_group 'control', this is superfluous.
COUNT This counts the number of items in the list passed to it Preceding it by distinct will have it output the number of unique items in your list.
Hence: If on boarding_modals contains only unique user_id's or user_id's can be listed more than once but never in combination with ab_group = 'control'. DISTINCT can safely be removed while maintaining the same outcome.

Beginning SQL group by and AVG

I am trying to pull information from two columns titled clientstate and clientrevenue in my table. I want clientstate to show up as the state, and have only distinct names in it, and under client revenue I want the average revenue per state, and that will only show up if there are at least two clients from that state. I am very new at this, so what I have is pretty iffy:
SELECT clientstate, clientrevenue
FROM client
GROUP BY clientrevenue
HAVING COUNT (*) >=2;
Where am I going wrong here?
SELECT clientstate AS [State]
, AVG(clientrevenue) AS [Average Revenue]
FROM client
GROUP BY clientstate
Grouping by ClientRevenue will try to group similar values and that doesn't have a logical sense.
First, in order to get distinct states, clientstate column needs to be used in the GROUP BY statement.
Thus, the code would be :
SELECT clientstate, AVG(clientrevenue)
FROM Source_Table
GROUP BY clientstate --this would get you distinct states
Now, considering the 2 clients per state, it's rather a condition than a HAVING statement. HAVING statement limits your query results according to the aggregate function you are using. For instance, in the code aforementioned, the aggregate function is AVG(clientrevenue). So, we can only use it in HAVING. we can not add count(*) unless it was used in SELECT.
So, you need to add it as a condition like
SELECT clientstate, AVG(clientrevenue)
FROM Source_Table A
WHERE (SELECT count(DISTINCT client_ID) FROM Source_Table B
WHERE A.clientstate = B.clientstate) >= 2 --Condition
GROUP BY clientstate --this would get you distinct states

Is GROUP BY needed in the following correlated subquery?

Given scenario:
table fd
(cust_id, fd_id) primary-key and amount
table loan
(cust_id, l_id) primary-key and amount
I want to list all customers who have a fixed deposit with an amount less than the sum of all their loans.
Query:
SELECT cust_id
FROM fd
WHERE amount
<
(SELECT sum(amount)
FROM loan
WHERE fd.cust_id = loan.cust_id);
OR should we use
SELECT cust_id
FROM fd
WHERE amount
<
(SELECT sum(amount)
FROM loan
WHERE fd.cust_id = loan.cust_id group by cust_id);
A customer can have multiple loans but one FD is considered at a time.
GROUP BY can be omitted in this case, because there is only (one) aggregate function(s) in the SELECT list and all rows are guaranteed to belong to the same group of cust_id ( by the WHERE clause).
The aggregation will be over all rows with matching cust_id in both cases. So both queries are correct.
This would be a cleaner another way to implement the same thing:
SELECT fd.cust_id
FROM fd
JOIN loan USING (cust_id)
GROUP BY fd.cust_id, fd.amount
HAVING fd.amount < sum(loan.amount)
There is one difference: rows with identical (cust_id, amount) in fd only appear once in the result of my query, while they would appear multiple times in the original.
Either way, if there is no matching row with a non-null amount in table loan, you get no rows at all. I assume you are aware of that.
There are no need for GROUP BY since you filtered data by cust_id. In any case inner query will return the same result.
No, it isn't, because you calculate sum(amount) for customer with id = fd.cust_id, so for a single customer.
However, if somehow your subquery calculate sum for more than one customer, the group by would cause the subquery to generate more than one row and this will cause the condition(<) to fail, and thus, the query to fail.
A query with an aggregate like sum but without a group by will output one group. The aggregates will be computed over all matching rows.
A subquery in a condition clause is only allowed to return one row. If the subquery returned multiple rows, what would the following expression mean?
where 1 > (... subquery ...)
So the group by must be omitted; you would even get an error for your second query.
N.B. When you specify all, any, or some a subquery can return multiple rows:
where 1 > ALL (... subquery ...)
But it's easy to see why that doesn't make sense in your case; you'd compare one customer's data to that of another.

SQL Query to find if different values exist for a column

I have a temporary table with three columns
pay_id,
id_client_grp,
id_user
Basically i want to ensure that this table should have all the rows having same client group and same id_user if not i want to know which pay_id is the culprit and throw error to user.
Can somebody help me with a query.
Thanks,
Rishi
When you say 'culprit,' I assume you mean the pay_id(s) that are not like the others, assuming there is a majority.
The problem is all of the pay_id's could potentially become culprits once your SELECT COUNT(DISTINCT id_client_grp, id_user) returns > 1 record, if there is a relatively even distribution. It is difficult to program for this scenario, since you will need to determine what exactly a majority is.
Your best bet will be to return all distinct combinations of those 3 fields, then decide where to go from there based on your business logic.
So could this question be asked like this:
If I wanted to add a unique index on my table across the three columns: client group, id user, pay id, identify those that break the unique condition where we have non unique pay id for a client group and id user??
select a.id_client_grp, a.id_user, a.pay_id , a.count from (
/* this should return 1 row per client group and user, */
/* if the pay id is the same for all */
select id_client_grp, id_user, pay_id, count(1) as count
from table t
group by id_client_grp, id_user ) a
group by a.id_client_grp, a.id_user
/* if we have more than one row per client group and user, then we have a dupe, so report them all */
having count (1) > 1
If you want all the rows to have the same values for some set of columns (your question is not entirely clear to me as t9o what you want to be the same)
Do you know going in WHICH pay_id, id_client_grp all the rows should be? Or do you not care, as long as they are all the same?
If you know the values you are looking for, simply test for rows that are not set to those desired values
Select distinct id_user
From tempTable
Where pay_id <> #PayIdValue
Or id_client_grp <> #ClientGroupIDValue
If you don't care, and just want them all to be the same, and they're not, then you need to specify which of the more than one set of values IS the "culprit" as you said...
If you want some other question answered. please explain more clearly...
Based on yr comment, then, to determine if there is more than one id_client_grp, pay_id
Select Count(Distinct id_client_grp, pay_id)
From tempTable
If this = 1 then every record has the same values for these 2 fields.... Any other value indicates that three is more than one set of distinct values in the table.
SELECT DISTINCT p.pay_id,
t.[count]
FROM rishi_table p
INNER JOIN ( SELECT id_client_grp, id_user, COUNT(*) As 'count'
FROM rishi_table
GROUP BY id_client_grp, id_user
HAVING COUNT(*) > 1 ) t
ON p.id_client_grp = t.id_client_grp AND p.id_user = t.id_user
basically create a set with the dupes, and bounce that against the main table to get your offending list.
SELECT DISTINCT id_client_grp, id_user
should let you do something like
IF ##ROWCOUNT > 1 THEN
...
Or possibly SELECT COUNT(DISTINCT id_client_grp, id_user) ...
but that's more vendor-dependent as to its availability and proper syntax.