SQL - Selecting various counts - mysql

I have the following tables:
User
-------------------
id BIGINT
name text
Session
-------------------
id BIGINT
username text
last_login datetime
And want to get the total user count, AND the count of users which have a session with a last login from today. How can i model the query?

Let me assume that you fix the session table to have userId rather than userName. Then your answer depends on the database. The general idea is something like this:
select count(distinct u.id) as user_count, -- including those with no sessions
sum(case when cast(last_login as date) = CURRENT_DATE
from users u left join
sessions s
on u.id = s.userId ;
This is the sketch of the logic. Removing the time component from a date/time values varies by database. The way of representing the current date varies by database.

Related

How to get login activity of users?

I've a table names loginActivity which stores user ids & login time. I want to count how many times a user logged in the system. You may say it user retention. If a user logs in two or more times a day this will be valued as 1 not 2/3.
I'm having problem with MySQL.
Please check the Table info below
I have tried several times. But query didn't succeed.
SAMPLE QUERY:
select sender_id, (SELECT COUNT(DISTINCT(sender_id)) FROM messages where created_at < '2020-01-26' ) from messages where created_at < '2020-01-26' GROUP BY sender_id HAVING(COUNT(sender_id) > 0)
If I understand correctly, you want for each user to get a number of days in which the user logged in at least once. You can achieve this by grouping by user id and counting distinct days:
SELECT t.uid, COUNT(DISTINCT DATE(t.logindate))
FROM tablename t
GROUP BY t.uid

Query for average response time in mysql

I have a table with columns:
id , conversation_id , session_id , user_id , message , created_at
every time a user starts a conversation with an employee, a new session starts (different session number).all messages between every employees and users are stored in this table. the created_at column is a timestamp. I need to filter out sessions by employee number, and calculate the average response time between the first message a user sends and the first message sent back by a specific employee, for every session disregarding outlying data where either a customer or employee did not reply ( only one user in the session)
i know this is complicated but please help!
in this example in the user_id column, 4 is the employee ( keep in mind there are other employees). everytime a new conversation starts the session_id changes. i have to go through each session for a specific employee, take the timestamp of the first message sent by the customer as well as the employee, take the difference, sum all the differences and then take an average, while making sure that the session actually contains two users ( filtering outlying data).
So far, ive come up with this:
SELECT * FROM messages
WHERE session_id IN (
SELECT session_id FROM messages
WHERE user_id =4 )
GROUP BY session_id, user_id
to get the first message from each customer and employee (gives something like this)
so from this specific example, i would omit line 41040 as it only as the session contains only 1 person (column 3, id 1028) and is considered outlying data
I'm actually appalled by some of the comments... StackOverflow is meant to be a community for helping others. Why bother even taking up comment space if you're gonna complain about my ponctuation or give a vague, useless answer?
Anyways, i figured it out.
Basically, i joined the same table multiple times but only queried the necessary data. In the first join, I queried the messages table with the employee messages and grouped them by session number. In the second join, i did the same procedure but only extracted the messages from the user. By joining them on the session id, it automatically omits any sessions where either a user or employee is not present. By default, the groupby returns the first set of data from the group ( in this situation i didn't have to manipulate the groupby because I was actually looking for the first message in the session), I then took the average of the difference between the message timestamp for the user and employee.In this specific situation, the number 4 is the employee number. Here is what the query looks like Also, the HAVING AVG_RESP > 0 was necessary in this situation to remove outlying data when tests are performed :
SELECT AVG(AVG_RESP)
FROM(
SELECT TIME_TO_SEC(TIMEDIFF(t.created_at, u.created_at )) AS AVG_RESP
FROM (
SELECT * FROM messages
WHERE session_id IN (
SELECT session_id FROM messages
WHERE user_id = 4) AND user_id = 4
GROUP BY session_id
) AS t
JOIN(
SELECT * FROM messages
WHERE session_id IN (
SELECT session_id FROM messages
WHERE user_id = 4) AND user_id != 4
GROUP BY session_id
) as u
ON t.session_id = u.session_id
GROUP BY t.session_id
HAVING AVG_RESP > 0
) as ar
Hopefully this helps someone in the future, unlike the people who leave ridiculous, useless comments.

SQL performance of a large number of sum()s

Within my J2EE web application, I need to generate a bar chart representing the percentage of users in the system with specific alerts. (EDIT - I forgot to mention, the graph only deals with alerts associated with the first situationof each user, thus the min(date) ).
A simplified (but structurally similar) version of my database schema is as follows :
users { id, name }
situations { id, user_id, date }
alerts { id, situation_id, alertA, alertB }
where users to situations are 1-n, and situations to alerts are 1-1.
I've omitted datatypes but the alerts (alertA and B) are booleans. In my actual case, there are many such alerts (30-ish).
So far, this is what I have come up with :
select sum(alerts.alertA), sum(alerts.alertB)
form alerts, (
select id, min(date)
from situations
group by user_id) as situations
where situations.id = alerts.situation_id;
and then divide these sums by
select count(users.id) from users;
This seems far from ideal.
Your recommendations/advice as to how to improve as query would be most appreciated (or maybe I need to re-think my database schema)...
Thanks,
Anthony
PS. I was also thinking of using a trigger to refresh a chart specific table whenever the alerts table is updated but I guess that's a subject for a different query (if it turns out to be problematic).
At first, think about your schema again. You will have a lot of different alerts and you probably don't want to add a single column for every one of those.
Consider changing your alerts table to something like { id, situation_id, type, value } where type would be (A,B,C,....) and value would be your boolean.
Your task to calculate the percentages would then split up into:
(1) Count the total number of users:
SELECT COUNT(id) AS total FROM users
(2) Find the "first" situation for each user:
SELECT situations.id, situations.user_id
-- selects the minimum date for every user_id
FROM (SELECT user_id, MIN(date) AS min_date
FROM situations
GROUP BY user_id) AS first_situation
-- gets the situations.id for user with minimum date
JOIN situations ON
first_situation.user_id = situations.user_id AND
first_situation.min_date = situations.date
-- limits number of situations per user to 1 (possible min_date duplicates)
GROUP BY user_id
(3) Count users for whom an alert is set in at least one of the situations in the subquery:
SELECT
alerts.type,
COUNT(situations.user_id)
FROM ( ... situations.user_id, situations.id ... ) AS situations
JOIN alerts ON
situations.id = alerts.situation_id
WHERE
alerts.value = 1
GROUP BY
alerts.type
Put those three steps together to get something like:
SELECT
alerts.type,
COUNT(situations.user_id)/users.total
FROM (SELECT situations.id, situations.user_id
FROM (SELECT user_id, MIN(date) AS min_date
FROM situations
GROUP BY user_id) AS first_situation
JOIN situations ON
first_situation.user_id = situations.user_id AND
first_situation.min_date = situations.date
GROUP BY user_id
) AS situations
JOIN alerts ON
situations.id = alerts.situation_id
JOIN (SELECT COUNT(id) AS total FROM users) AS users
WHERE
alerts.value = 1
GROUP BY
alerts.type
All queries written from my head without testing. Even if they don't work exactly like that, you should still get the idea!

Mysql form new optimize query

I have Four Tables in database,
users, count, coupon, account
Need number of users from following conditions:
Select All Users from users table then
Select from count where users are not in the list of last 14 days then
Select users which are not in the list, to check that there are in coupon table. If they are not in coupon table then take those users id then
Select account table and check that these users have any count or insertion in the account table. If yes then set all those users in an array or print it out as a result.
Please anybody help me in forming good query?
Thanks!
If I'm right take users
who aren't in the count table for the last 14 days
and
who aren't in the coupon table.
and
who are in the account table
Seems like a weird sql but here is an example:
SELECT
*
FROM
user
WHERE user_id NOT IN
(
SELECT
user_id
FROM
`count`
WHERE
DATE_SUB(CURDATE(),INTERVAL 14 DAY) <= date_column
)
AND user_id NOT IN
(
SELECT
user_id
FROM
coupon
)
AND user_id IN
(
SELECT
user_id
FROM
account
)
If this isn't right, your explanation of what you want is wrong and should be clarified .
Everything could be wrong with this sql, because I don't know your columns (user id? date?) .But I think you can change the sql to your liking.

MySQL query - find "new" users per day

I have a table of data with the following fields
EventID : Int, AutoIncrement, Primary Key
EventType : Int ' Defines what happened
EventTimeStamp : DateTime ' When the Event Happened
UserID : Int ' Unique
The query needs to tell me how many events occurred with new UserIDs for each day in the whole set. So, for each day, how many events exist which have a UserID which doesn't exist in any prior day. I've tried lots, and I can get unique users per day, but can't work out how to get 'NEW' users per day.
Select count(EventId) from table
where
UserId
not in (select UserId from table where EventTimeStamp < now() - interval 1 day)
Thank you all for your help - I've voted up the assistance. Here's what I did:
I created these 2 views (I needed to end up with a view, and had to create 2 as it seems you can't nest select statements within views).
Sightings:
select min(to_days(`Events`.TimeStamp)) AS Day0,
`Events`.TimeStamp AS TimeStamp,
`Events`.UserID AS UserID
from `Events` group by `Events`.UserID order by `Events`.UserID
NewUsers:
select count(distinct Sightings.UserID) AS Count,
date(Sightings.TimeStamp) AS Date from Sightings
group by date(Sightings.TimeStamp)
Good question. I don't have an exact solution but here's the approach I've seen before:
Do a SELECT where you compare the EventTimeStamp with MIN(EventTimeStamp) for a given userID, as determined by a nested SELECT statement on the same table to calculate the MIN timestamp for each ID (e.g. GROUP BY UserID).
First get a table b with for each user when he first arrived,
then join that table to get all events for that user for that day.
SELECT DATE(a.EventTimeStamp), COUNT(*) FROM table a
JOIN
(SELECT MIN(EventTimeStamp) AS EventTimeStamp, UserID from table group by userID) b
ON a.UserID = b.UserID
AND DATE(a.EventTimeStamp) = DATE(b.EventTimeStamp)
GROUP BY DATE(a.EventTimeStamp)
86400-(EventTimeStamp) as new_users