Count is displaying one only row despite multiple rows in database - mysql

Here is my SQL query:
SELECT
COUNT(CASE WHEN `urgency`='1' THEN 1 END) AS verylow,
COUNT(CASE WHEN `urgency`='2' THEN 1 END) AS low,
COUNT(CASE WHEN `urgency`='3' THEN 1 END) AS standard,
COUNT(CASE WHEN `urgency`='4' THEN 1 END) AS high,
COUNT(CASE WHEN `urgency`='5' THEN 1 END) AS critical,
tbl_users.userName
FROM
notes, tbl_users
WHERE
notes.responsible = tbl_users.userID
AND project_id = '4413'
AND (notes.status = 'Ongoing' OR notes.status = 'Not started')
and the output is:
verylow low standard high critical userName
5 1 2 1 1 Nick
However this is wrong because i have multiple users in the database who have assigned tasks. and it looks like this in my database:
urgency userName
3 Nick
5 Nick
4 Nick
3 James
1 James
1 Nick
2 Nick
1 James
1 Nick
1 Nick
Any idea why it doesn't count the urgency for the other user and how many different urgencies he has?

What you are doing is not entirely correct. If you would turn on MySQL mode ONLY_FULL_GROUP_BY, you'd get a warning, because you are selecting a column that is not in the GROUP BY clause without applying an aggregation function. So you need a GROUP BY clause.
The entire query should look like so:
SELECT
COUNT(CASE WHEN `urgency`='1' THEN 1 END) AS verylow,
COUNT(CASE WHEN `urgency`='2' THEN 1 END) AS low,
COUNT(CASE WHEN `urgency`='3' THEN 1 END) AS standard,
COUNT(CASE WHEN `urgency`='4' THEN 1 END) AS high,
COUNT(CASE WHEN `urgency`='5' THEN 1 END) AS critical,
tbl_users.userName
FROM
notes, tbl_users
WHERE
notes.responsible = tbl_users.userID
AND project_id = '4413'
AND (notes.status = 'Ongoing' OR notes.status = 'Not started')
GROUP BY tbl_users.userName;

With COUNT you aggregate your rows. As there is no GROUP BY clause, you aggregate them to one row (rather than, say a row per user).
You are selecting userName. Which? As you select only one row, the DBMS picks one arbitrarily.

Related

MySQL count and case with one to many relationship

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,

How to write a SQL query for multiple Inner Join?

A sample record:
Row(user_id='KxGeqg5ccByhaZfQRI4Nnw', gender='male', year='2015', month='September', day='20',
hour='16', weekday='Sunday', reviewClass='place love back', business_id='S75Lf-Q3bCCckQ3w7mSN2g',
business_name='Notorious Burgers', city='Scottsdale', categories='Nightlife, American (New), Burgers,
Comfort Food, Cocktail Bars, Restaurants, Food, Bars, American (Traditional)', user_funny='1',
review_sentiment='Positive', friend_id='my4q3Sy6Ei45V58N2l8VGw')
This table has more than a 100 million records. My SQL query is doing the following:
Select the most occurring review_sentiment among the friends (friend_id) and the most occurring gender among friends of a particular user visiting a specific business
friend_id is eventually a user_id
Example Scenario:
One user
Has Visited 4 Businesses
Has 10 friends
5 of these friends have visited Business 1 & 2 while other 5 have
visited 3rd business only and none have visited the fourth
Now, for Business 1 and 2, the 5 friends have more positive than
negative sentiments for B1 and have more -ve than +ve sentiment for
B2 and all -ve for B3
I want the following output for this:
**user_id | business_id | friend_common_sentiment | mostCommonGender | .... otherCols**
user_id_1 | business_id_1 | positive | male | .... otherCols
user_id_1 | business_id_2 | negative | female | .... otherCols
user_id_1 | business_id_3 | negative | female | .... otherCols
Here's a simple query I wrote for this in pyspark:
SELECT user_id, gender, year, month, day, hour, weekday, reviewClass, business_id, business_name, city,
categories, user_funny, review_sentiment FROM events1 GROUP BY user_id, friend_id, business_id ORDER BY
COUNT(review_sentiment DESC LIMIT 1
This query will not give what is expected but I'm not sure how exactly to fit in a INNER-JOIN into this?
Man does that data structure make things hard. But lets break it down into steps,
You need to self join to get the data for friends
Once you have the data for friends, perform aggregate functions to get counts of each possible value, grouping by the user and the business
sub query the above in order to make decisions between the values based on counts.
I'm just going to call your table "tags", so the join would be as follows, sadly just like in real life we can't assume everyone has friends, and since you didn't specify to exclude the forever alone crowd, we need to use a left join to keep users without friends.
From tags as user
left outer join tags as friends on user.friend_id = friends.user_id
and friends.business_id = user.business_id
Next you have to figure out what the most common gender/review is for a given user and business combination. This is where the data structure really kicks us in the butt, we could do this in one step with some clever window functions, but I want this answer to be easily understood, so I'm going to use a sub-query and a case statements. For the sake of simplicity I'm assuming binary genders, but depending on the woke level of your app, you can follow the same patterns for additional genders.
select user.user_id, user.business_id
, sum(case when friends.gender = 'Male' then 1 else 0 end) as MaleFriends
, sum(case when friends.gender = 'Female' then 1 else 0 end) as FemaleFriends
, sum(case when friends.review_sentiment = 'Positive' then 1 else 0 end) as FriendsPositive
, sum(case when friends.review_sentiment = 'Negative' then 1 else 0 end) as FriendsNegative
From tags as user
left outer join tags as friends on user.friend_id = friends.user_id
and friends.business_id = user.business_id
where user.business_id = <<your business id here>>
group by user.user_id, user.business_id
Now we just have to grab data from the sub-query and make some decisions, you may want to add some additional options, for instance you may want to add options in case there are no friends, or friends are evenly split between gender/sentiment. same pattern as below though with extra values to choose from.
select user_id
, business_id
, case when MaleFriends > than FemaleFriends then 'Male' else 'Female' as MostCommonGender
, case when FriendsPositive > FriendsNegative then 'Positive' else 'Negative' as MostCommonSentiment
from ( select user.user_id, user.business_id
, sum(case when friends.gender = 'Male' then 1 else 0 end) as MaleFriends
, sum(case when friends.gender = 'Female' then 1 else 0 end) as FemaleFriends
, sum(case when friends.review_sentiment = 'Positive' then 1 else 0 end) as FriendsPositive
, sum(case when friends.review_sentiment = 'Negative' then 1 else 0 end) as FriendsNegative
From tags as user
left outer join tags as friends on user.friend_id = friends.user_id
and friends.business_id = user.business_id
where user.business_id = <<your business id here>>
group by user.user_id, user.business_id) as a
This gives you the steps to follow, and hopefully a clear explanation on how they work. Good luck!

Group Totals from Logs by Month

I have a log table that stores media requests by act_datetime, app_id, location_id, media_id and media_type_id. What I want is each resultset row to contain type totals for each month. For example, log records contain:
I tried using temp tables to extract records by app_id and grouping by month, but I get multiple rows for each total. I can use sub-queries, but how do I get a total row by type for each month?
Any help is greatly appreciated.
Thanks,
Brandon
EDIT
The follow code works combining shared ideas:
This query takes about 13 seconds parsing about 8.1 million rows. Is that acceptable? Lastly how do you display date as 2018-1 as one column? I'm getting errors when converting to string since the date is also used in the group and order by clauses.
I also want to try code construct sum( case when media_type_id = 1 then 1 else 0 end )... to see if get same results and speed.
Thanks for everyone's help!
Assuming this is SQL Server, and not MySQL:
SELECT DATEPART(MONTH, act_datetime) AS [Month],
COUNT(CASE WHEN app_id = 14 AND media_type_id = 1 AND location_id = 1 THEN act_datetime END) AS MP3_Messages_MO,
COUNT(CASE WHEN app_id = 14 AND media_type_id = 1 AND location_id = 2 THEN act_datetime END) AS MP3_Messages_FL,
COUNT(CASE WHEN app_id = 14 AND media_type_id = 3 AND location_id = 1 THEN act_datetime END) AS MP3_Messages_MO,
COUNT(CASE WHEN app_id = 14 AND media_type_id = 3 AND location_id = 2 THEN act_datetime END) AS MP3_Messages_FL,
COUNT(CASE WHEN app_id = 55 AND media_type_id = 1 THEN act_datetime END) AS MP3_Music,
COUNT(CASE WHEN app_id = 55 AND media_type_id = 9 THEN act_datetime END) AS ZIP_Music
FROM YourTable
GROUP BY DATEPART(MONTH, act_datetime);
Note you have included no logic for differing years, data for each Month will do a count irrespective of year.
This is also completely untested, due to lack of consumable data.

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.

How to optimize this in MySQL?

I have table structure as displayed in first table.
And want to fetch Both Male and Female Counts in a single query so that request will go only for one time onto the server.
This is what you need to do:
select gender,
count(case when age between 0 and 20 then 1 else null end) Age_0_20,
count(case when age between 21 and 40 then 1 else null end) Age_21_40
from yourtable
group by gender
Adjust accordingly :)
Update, with clarifications
Note that COUNT aggregate function only counts non-null values. Thus, the else values in the case must be NULL. The When value returns 1 but it could just be any non-null value.
Some people implement this by using SUM:
select gender,
sum(case when age between 0 and 20 then 1 else 0 end) Age_0_20,
sum(case when age between 21 and 40 then 1 else 0 end) Age_21_40
from yourtable
group by gender
The result is going to be absolutely the same.