How do I count multiple columns of events for users? - mysql

I have a table of events that stores the events for users and I want to get the number of two specific events for each user.
An example table is called "events" and it has 2 columns
user_id VARCHAR(50)
event_name VARCHAR(50)
The user_ids are all unique and the event names can be things like login, sent_message, liked_post
How do I, for example, query for the total messages sent per user AND the number of liked_posts per user?

This is the pattern of query you can use:
select user_id,
sent_message_count=sum(case when event_name = 'Sent Message' then 1 else 0 end),
liked_post_count=sum(case when event_name = 'Liked Post' then 1 else 0 end),
from events
group by user_id
Now, you just need to make sure the when part of each case statement fits the criteria you need. The pattern itself - summing a bunch of 1's where the criteria fits, is really the key to achieving the result you're after.

SELECT user_id,
SUM(IF(event_name = 'sent_message', 1, 0)) AS sent_message,
SUM(IF(event_name = 'liked_posts', 1, 0)) AS liked_posts
FROM `events`
GROUP BY user_id

Related

SQL avg of comma occurrencies with condition

I have a table of events where partecipants are listed as a comma separated list of IDs:
0,4,21,33,41
I'm trying to perform the following query to retrieve the average partecipants number for event only when status is equal to 1.
I prepared the following but doesn't work, anyone can help me?
SELECT avg(case when (status = 1 then LENGTH(REPLACE(listofPartecipants, ',', '')) end) avgPartecipants FROM events;
Many thanks
This is the expression if you want to count the number of elements in a list:
SELECT avg(case when status = 1
then LENGTH(REPLACE(listofPartecipants, ',', 'XX')) - length(listofPartecipants) + 1
end) as avgPartecipants
FROM events;
More important than getting this arcane string logic right is fixing your data model. You should not be storing list of ids in a comma-delimited list. You should have a table with one row per event and per participant.

Get the No of Pending, Accepted and Rejected Users from two tables

I have two tables namely register and expressinterest
Where register table contains (user related columns) some are -
i) matri_id (unique but not primary)
ii) mobile
iii) email
iv) last_login
and expressinterest table contains data related to the matches details where columns are namely
i) ei_sender
ii) ei_receiver
iii) ei_response
iv) ei_sent_date
I am comparing the matri_id of register table to ei_sender and ei_receiver of expressinterest table, where one user can send requests to another users and he can receive request from another users.
I have to get the count of Pending, Accepted and Rejected status of all the users present in the register table, but when I am running the query it's running very slow, It takes around 45-60 seconds to fetch only 5000 rows in which the data is not proper (a single ID is coming in 3 rows like Accepted in one row, Rejected in one row and Pending in one row), But I want all the counts to come in a single row like this
r.matri_id | r._email | r.mobile | pending_count | accepted_ count | rejected_count| r.last_login
Some queries which I had tried so far are
select r.matri_id, r.email, r.mobile, r.last_login, e.receiver_response, count(e.receiver_response), e.ei_sender, e.ei_receiver from register r, expressinterest e where r.matri_id = e.ei_sender or r.matri_id = e.ei_receiver GROUP BY e.receiver_response, r.matri_id ORDER BY r.last_login DESC
This is what I want but its taking 5-6 seconds to execute
select matri_id, email, mobile, last_login, (select count(ei_sender) from expressinterest where ei_receiver=matri_id and receiver_response = 'Pending') AS pending_count_mine,
(select count(ei_sender) from expressinterest where ei_sender=matri_id and receiver_response = 'Accepted') AS accepted_count,
(select count(ei_sender) from expressinterest where ei_sender=matri_id and receiver_response = 'Rejected') AS rejected_count FROM register ORDER BY last_login DESC
Thanks
You can replace the multiple correlated subqueries with a single subquery and a left join:
select r.matri_id,
r.email,
r.mobile,
r.last_login,
t.accepted_count,
t.rejected_count,
t.pending_count
from register r
left join (
select ei_sender,
sum(receiver_response = 'Accepted') as accepted_count,
sum(receiver_response = 'Rejected') as rejected_count,
sum(receiver_response = 'Pending') as pending_count
from expressinterest
where receiver_response in ('Rejected', 'Accepted', 'Pending')
group by ei_sender
) t on r.matri_id = t.ei_sender;

why my sql query slow?

I try to create a view which join from 4 tables (tb_user is 200 row, tb_transaction is 250.000 row, tb_transaction_detail is 250.000 row, tb_ms_location is 50 row),
when i render with datatables serverside, it's take 13 secons. even when I filtering it.
I don't know why it's take too long...
here my sql query
CREATE VIEW `vw_cashback` AS
SELECT
`tb_user`.`nik` AS `nik`,
`tb_user`.`full_name` AS `nama`,
`tb_ms_location`.`location_name` AS `lokasi`,
`tb_transaction`.`date_transaction` AS `tanggal_setor`,
sum(CASE WHEN `tb_transaction_detail`.`vehicle_type`=1 THEN 1 ELSE 0 END) AS `mobil`,
sum(CASE WHEN `tb_transaction_detail`.`vehicle_type`=2 THEN 1 ELSE 0 END) AS `motor`,
sum(CASE WHEN `tb_transaction_detail`.`vehicle_type`=3 THEN 1 ELSE 0 END) AS `truck`,
sum(CASE WHEN `tb_transaction_detail`.`vehicle_type`=4 THEN 1 ELSE 0 END) AS `speda`,
sum(`tb_transaction_detail`.`total`) AS `total_global`,
(sum(`tb_transaction_detail`.`total`) * 0.8) AS `total_user`,
(sum(`tb_transaction_detail`.`total`) * 0.2) AS `total_tgr`,
((sum(`tb_transaction_detail`.`total`) * 0.2) / 2) AS `total_cashback`,
(curdate() - cast(`tb_user`.`created_at` AS date)) AS `status`
FROM `tb_user`
JOIN `tb_transaction` ON `tb_user`.`id` = `tb_transaction`.`user_id`
JOIN `tb_transaction_detail` ON `tb_transaction`.`id` = `tb_transaction_detail`.`transaction_id`
JOIN `tb_ms_location` ON `tb_ms_location`.`id` = `tb_transaction`.`location_id`
GROUP BY
`tb_user`.`id`,
`tb_transaction`.`date_transaction`,
`tb_user`.`nik`,
`tb_user`.`full_name`,
`tb_user`.`created_at`,
`tb_ms_location`.`location_name`
thanks
The unfiltered query must be slow, because it takes all records from all tables, joins and aggregates them.
But you say the view is still slow when you filter. The question is: How do you filter? As you are aggregating by user, location and transaction date, it should be one of these. However, you don't have the user ID or the transaction ID in your result list. This doesn't feel natural and I'd suggest you add them, so a query like
select * from vw_cashback where user_id = 5
or
select * from vw_cashback where transaction_id = 12345
would be possible.
As is, you'd have to filter by location name or user nik / name. So if you want it thus, then create Indexes for the lookup:
CREATE idx_location_name ON tb_ms_location(location_name, id)
CREATE idx_user_name ON tb_user(full_name, id)
CREATE idx_user_nik ON tb_user(nik, id)
The latter two can even be turned into covering indexs (i.e. indexes containing all columns used in the query) that may still speed up the process:
CREATE idx_user_name ON tb_user(nik, id, full_name, created_at);
CREATE idx_user_nik ON tb_user(full_name, id, nik, created_at);
As for the access via index, you also may want covering indexes:
CREATE idx_location_id ON tb_ms_location(id, location_name)
CREATE idx_user_id ON tb_user(id, nik, full_name, created_at);

mysql count distinct value

I have trouble wondering how do I count distinct value. using if on the select column
I have SQLFIDDLE here
http://sqlfiddle.com/#!2/6bfb9/3
Records shows:
create table team_record (
id tinyint,
project_id int,
position varchar(45)
);
insert into team_record values
(1,1, 'Junior1'),
(2,1, 'Junior1'),
(3,1, 'Junior2'),
(4,1, 'Junior3'),
(5,1, 'Senior1'),
(6,1, 'Senior1'),
(8,1, 'Senior2'),
(9,1, 'Senior2'),
(10,1,'Senior3'),
(11,1, 'Senior3'),
(12,1, 'Senior3')
I need to count all distinct value, between Junior and Senior column.
all same value would count as 1.
I need to see result something like this.
PROJECT_ID SENIOR_TOTAL JUNIOR_TOTAL
1 3 3
mysql query is this. but this is not a query to get the result above.
SELECT
`team_record`.`project_id`,
`position`,
SUM(IF(position LIKE 'Senior%',
1,
0)) AS `Senior_Total`,
SUM(IF(position LIKE 'Junior%',
1,
0)) AS `Junior_Total`
FROM
(`team_record`)
WHERE
project_id = '1'
GROUP BY `team_record`.`project_id`
maybe you could help me fix my query above to get the result I need.
thanks
I think you want this:
SELECT
project_id,
COUNT(DISTINCT CASE when position LIKE 'Senior%' THEN position END) Senior_Total,
COUNT(DISTINCT CASE when position LIKE 'Junior%' THEN position END) Junior_Total
FROM team_record
WHERE project_id = 1
GROUP BY project_id
The CASE will return a null if the WHEN is false (ie ELSE NULL is the default, which I omitted for brevity), and nulls aren't counted in DISTINCT.
Also, unnecessary back ticks, brackets and qualification removed.

SELECT CASE, COUNT(*)

I want to select the number of users that has marked some content as favorite and also return if the current user has "voted" or not. My table looks like this
CREATE TABLE IF NOT EXISTS `favorites` (
`user` int(11) NOT NULL DEFAULT '0',
`content` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`user`,`content`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;
Say I have 3 rows containing
INSERT INTO `favorites` (`user`, `content`) VALUES
(11, 26977),
(22, 26977),
(33, 26977);
Using this
SELECT COUNT(*), CASE
WHEN user='22'
THEN 1
ELSE 0
END as has_voted
FROM favorites WHERE content = '26977'
I expect to get has_voted=1 and COUNT(*)=3 but
I get has_voted=0 and COUNT(*)=3. Why is that? How to fix it?
This is because you mixed aggregated and non-aggregated expressions in a single SELECT. Aggregated expressions work on many rows; non-aggregated expressions work on a single row. An aggregated (i.e. COUNT(*)) and a non-aggregated (i.e. CASE) expressions should appear in the same SELECT when you have a GROUP BY, which does not make sense in your situation.
You can fix your query by aggregating the second expression - i.e. adding a SUM around it, like this:
SELECT
COUNT(*) AS FavoriteCount
, SUM(CASE WHEN user=22 THEN 1 ELSE 0 END) as has_voted
FROM favorites
WHERE content = 26977
Now both expressions are aggregated, so you should get the expected results.
Try this with SUM() and without CASE
SELECT
COUNT(*),
SUM(USER = '22') AS has_voted
FROM
favorites
WHERE content = '26977'
See Fiddle Demo
Try this:
SELECT COUNT(*), MAX(USER=22) AS has_voted
FROM favorites
WHERE content = 26977;
Check the SQL FIDDLE DEMO
OUTPUT
| COUNT(*) | HAS_VOTED |
|----------|-----------|
| 3 | 1 |
You need sum of votes.
SELECT COUNT(*), SUM(CASE
WHEN user='22'
THEN 1
ELSE 0
END) as has_voted
FROM favorites WHERE content = '26977'
You are inadvertently using a MySQL feature here: You aggregate your results to get only one result record showing the number of matches (aggregate function COUNT). But you also show the user (or rather an expression built on it) in your result line (without any aggregate function). So the question is: Which user? Another dbms would have given you an error, asking you to either state the user in a GROUP BY or aggregate users. MySQL instead picks a random user.
What you want to do here is aggregate users (or rather have your expression aggregated). Use SUM to sum all votes the user has given on the requested content:
SELECT
COUNT(*),
SUM(CASE WHEN user='22' THEN 1 ELSE 0 END) as sum_votes
FROM favorites
WHERE content = '26977';
You forgot to wrap the CASE statement inside an aggregate function. In this case has_voted will contain unexpected results since you are actually doing a "partial group by". Here is what you need to do:
SELECT COUNT(*), SUM(CASE WHEN USER = 22 THEN 1 ELSE 0 END) AS has_voted
FROM favorites
WHERE content = 26977
Or:
SELECT COUNT(*), COUNT(CASE WHEN USER = 22 THEN 1 ELSE NULL END) AS has_voted
FROM favorites
WHERE content = 26977