MySQL count and case with one to many relationship - mysql

My first question here.
I got 2 tables with one to many relationship:
policies
id
issue_date
user_id
insurance_type
1
1-2-2021
100
apartment
2
1-2-2021
200
car
policy_details
id
policy_id
type
1
1
type A
1
1
type A
2
1
type B
3
1
type C
1 policy record can have many policy_details records.
I need to count the policies rows with some case,
and I need to join the policy_details as well because I count them also.
Example to simple query:
SELECT
user_id,
COUNT(CASE WHEN `insurance_type` = 'apartment' THEN 1 ELSE NULL END) as totalApartmentType,
COUNT(CASE WHEN policy_details.type = 'typeA' THEN 1 ELSE NULL END) as totalTypeA
FROM `policies`
JOIN `policy_details` ON policy_details.policy_id = policies.id
AND MONTH(`issue_date`) = 2
GROUP BY (`user_id`)
The problem here is that if 1 policy connected to 2 policy_details records for example,
the count will be 2 and it should be 1, cause I need to count the policy record, not the policy_details joined records.
And if policy_details has 5 records connected to policy, the count will be 5. (should be 1).
The result I need for the query above:
user_id
totalApartmentType
totalTypeA
100
1
2
Can it be done guys?

You can use COUNT(DISTINCT) making sure you generate the id instead of 1 in case the condition is fulfilled:
COUNT(DISTINCT CASE WHEN insurance_type = 'apartment'
THEN policies.id
ELSE NULL
END) AS totalApartmentType,

Related

Kind of simple query in mysql but can't solve it

I previously posted a question here: Mysql query with joined tables problems
but didn't get good answers, so I thought I break it down to show only the part that gives me a headache, I might get answers faster to this question, and if I can solve this, I can solve the previous problem too.
The values are referring to an item_id, and I want to get item_id's where the item is referring to (('good' OR 'bad') AND 'fast')
So item_id 1 must be listed, because it is good and also fast
item_id 2 should not listed because it is not fast.
The result i want to get
item_id
1
5
if I have a table like this
id item_id value
1 1 'good'
2 1 'fast'
3 2 'good'
4 2 'slow'
5 3 'good'
6 3 'slow'
7 4 'bad'
8 4 'slow'
9 5 'bad'
10 5 'fast'
11 6 'moderate'
12 6 'fast'
Can someone help me?
One way to see this is: you want a statement per item, so you aggregate and group by item. Criteria come in the HAVING clause.
select item_id
from mytable
group by item_id
having count(case when value = 'fast' then 1 end) >= 1
and count(case when value in ('good','bad') then 1 end) >= 1;
(You can also use sum(case when value = 'fast' then 1 else 0 end) >= 1 or max(case when value = 'fast' then 1 else 0 end) = 1 or some expression along those lines. COUNT(expression) counts expressions that are not null. As I am omitting an ELSE branch, a non-matching record results in null and is thus not counted. Some prefer SUM, some prefer COUNT - it's finally a matter of personal preference.)
One way to do this is with an EXISTS query.
Here's an example:
select t1.item_id
from your_table t1
where t1.value = 'fast'
and exists (
select NULL
from your_table t2
where t2.item_id = t1.item_id
and t2.value in ('good','bad')
);
Considering that a item can be good or bad, not both, I would use the simple way:
select item_id your_table
where value in ('good','bad','fast')
group by item_id
having count(*) = 2
count should be always 2 in those cases.

Find distinct elements that match multiple values from the same column

Lets say this is the table I am talking about
id pId fId
1 1 1
2 2 1
3 2 2
4 3 2
I need to get a list of pId's who have a match to ALL of the given indices in a list of fId's.
What I mean is ->
Consider the list of fId's to be:
(1,2)
Then the result should be
2
Because only pId 2 has a match to all given entries in the list of fId's (which would be 1 and 2).
I couldn't find any way to do it so far - any help is highly appreciated :-)
Aggregate on pid column and use a having clause.
select pid
from tablename
group by pid
having sum(case when fid in (1,2) then 1 else 0 end) >= 2

MySQL Query to find users still inside room

Below is my database table, where I will have Check In and Check Out entry records for attending the conference room.
id registration_id roomno day type
1 101 1 2 In
2 103 1 2 In
3 101 1 2 Out
4 105 1 2 In
5 103 1 2 Out
6 101 1 2 In
7 103 1 2 In
8 101 1 2 Out
9 105 1 2 In
10 103 1 2 Out
Now, I want to select those records, which are still attending the conference. Condition is like their last record should be type = In. There can be multiple In/Out entries for each user during a day.
Please let me know the quickest possible MySQL query.
Thanks
Answer which I ended up using:
select * from `registrations_inouts` t
group by t.registration_id
having max(id) = max(case when type = 'In' then id end)
order by rand() limit 1;
Here is one method using not exists:
select *
from t
where t.type = 'In' and
not exists (select 1
from t t2
where t2.registration_id = t.registration_id and t2.type = 'Out'
);
Another method uses conditional aggregation:
select t.registration_id
from t
group by t.registration_id
having max(id) = max(case when type = 'In' then id end);
Note: both of these assume that the ids are assigned sequentially in time, so larger ids are later in time.

Grouping notifications by type in MySql

I have the following table structure for notifications table.
id user_id post_id type status date seconduser_id
1 1 23 1 0 somedate 4
2 2 25 2 0 somedate 3
3 3 26 1 0 somedate 4
4 4 28 2 1 somedate 5
5 5 21 2 0 somedate 4
---
---
and so on
Here type = 1 means a like and type = 2 means a comment. status = 0 means seconduser_id hasn't seen the notification yet. seconduser_id is the notification recipient.
Is it possible to get notifications for 1 user (example seconduser_id = 4), with notification grouped by type, showing count and the latest user_id for each type (in one query)?
The implementation of this would be something like User3 and 10 other people liked your post.
Edit: So far I have something that pulls all notification for user 4 and then groups in php. I don't think this is efficient and so am looking for better solutions.
The basic answer is an aggregation query. The complication is getting the latest user id for each type. THere is one method, using substirng_index()/group_concat():
select type, count(*),
substring_index(group_concat(user_id order by id desc), ',', 1) as latest_user
from notifications n
where secondaryuser_id = 4 and status = 0
group by type;
I am not sure if you also want to filter by status.
Edit (added by OP):
Using the above code and grouping by both post_id and type. Because you want to say User1 and 10 others liked your post. Which means for each grouped notification, post_id has to be unique.
SELECT *, substring_index(group_concat(user_id order by id desc), ',', 1) as latest_user, COUNT(post_id) AS total
FROM notifications n
WHERE seconduser_id = 4 and status = 0
GROUP BY post_id, type
Try this:
SELECT MAX(user_id) AS LatestUser, type, COUNT(type) AS total
FROM notifications
WHERE seconduser_id = 4
GROUP BY type

Query return 1 row when no data present

I am running several instances of the same query. The below version should not be returning any data but I am getting 1 row with 'null' in each of the columns:
SELECT (CASE ScanName WHEN 'SYSTEM-HQ' THEN 'HQ
System' END) AS System,
sum(CASE pspplMSSeverity WHEN 1 THEN 10 WHEN 2 THEN 9 WHEN
3 THEN 6 WHEN 4 THEN 3 END) AS Score,
(sum(CASE pspplMSSeverity WHEN 1 THEN 10 WHEN 2 THEN 9
WHEN 3 THEN 6 WHEN 4 THEN 3 END)/COUNT(pspplMSSeverity)) AS
Grade
FROM missingpatches
WHERE ScanName like '%SYSTEM-HQ%'
ORDER BY LAST_UPDATE DESC LIMIT 1
How can I modify this query to ensure that I am only returning data when valid values exist?
Could this be due to the use of the Case and Sum within the primary SQL statement that are causing the Null data to be returned?
SELECT (CASE ScanName WHEN 'SYSTEM-HQ' THEN 'HQ
System' END) AS System,
sum(CASE pspplMSSeverity WHEN 1 THEN 10 WHEN 2 THEN 9 WHEN
3 THEN 6 WHEN 4 THEN 3 END) AS Score,
(sum(CASE pspplMSSeverity WHEN 1 THEN 10 WHEN 2 THEN 9
WHEN 3 THEN 6 WHEN 4 THEN 3 END)/COUNT(pspplMSSeverity)) AS
Grade
FROM missingpatches
WHERE ScanName like '%SYSTEM-HQ%'
HAVING System IS NOT NULL # Added
ORDER BY LAST_UPDATE DESC LIMIT 1
Try add a HAVING.
Most probably the table missingpatches contains 1 row that satisfy
WHERE ScanName like '%SYSTEM-HQ%'
i.e. a row with ScanName that contains 'SYSTEM-HQ' but not exactly equal to 'SYSTEM-HQ' which you are equating with in the select's 1st col.
In SQL, the columns are evaluated after tables are joined and where clauses are evaluated. Hence, the row you are seeing, is actually meeting the where clause criteria.