I have two tables. Into the first table (activity) there are: user_id, sessions and login_time. Into the second (payments) there's only one column - user_id.
I need to calculate how many sessions did users from "payments" do during 01/01/2018-01/05/2018.
activity
user_id sessions login_time
1 6 01.01.2018
2 2 01.01.2018
1 1 02.01.2018
4 3 02.01.2018
1 2 03.05.2018
3 04.02.2018
table
payments
user_id
1
3
4
My code (calculates wrong):
select login_time, avg(activity.sessions) AS
average_sessions
from activity inner join
payments
on payments.user_id = activity.user_id
where activity.login_time between '2018-01-01' and '2018-05-01'
group by activity.login_time;
Thanks for help!
Output:
output
login_time average_sessions
01.01.2018 6
02.01.2018 2
03.01.2018 0
04.01.2018 0
...
looking to your sample
seems you need the sum of session divided by the number of totals days
select user_id, sum(sessions)/t.num_days
from activity
cross join (
select count(distinct login_time) num_days
from activity
) t
group by user_id
and if you need only the user_id in payments
select user_id, sum(sessions)/t.num_days
from activity
cross join (
select count(distinct login_time) num_days
from activity
) t
inner join payments p on p.user_id = activity.user_id
group by user_id
and looking to your formaed result seems you need
select t1. login_time, ifnull(t2.avg_sess) average_sessions
from (
select distinct login_time
from activity
) t1
left join (
select a.login_time, avg(a.session) avg_sess
from activity a
inner join payments p on a.user_id = p.user_id
group by a.login_time
) t2 on t1.login_time = t2.login_time
Based on your expected output, use the following:
SELECT login_time,
SUM(activity.sessions)/COUNT(DISTINCT activity.user_id) AS average_sessions
FROM activity
INNER JOIN payments ON payments.user_id = activity.user_id
WHERE activity.login_time BETWEEN '2018-01-01' and '2018-05-01'
GROUP BY activity.login_time;
Related
transactions table
column type
id integer
user_id integer
created_at datetime
product_id integer
quantity integer
users table
column type
id integer
name varchar
sex varchar
Write a query to identify customers who placed more than three transactions each in both 2019 and 2020.
Output
column type
customer_name string
My code is:
WITH CTE AS( SELECT u.name, count(user_id) AS "Num_Apperence",YEAR(t.created_at)
FROM transactions t
LEFT JOIN users u
ON t.user_id = u.id
WHERE YEAR(t.created_at) = 2019
GROUP BY u.name
HAVING count(user_id) > 3
)
SELECT Name1
FROM (SELECT u.name AS 'Name1', count(user_id) AS "Num_Apperence",YEAR(t.created_at)
FROM transactions t
LEFT JOIN users u
ON t.user_id = u.id
WHERE YEAR(t.created_at) = 2020
GROUP BY u.name
HAVING count(user_id) > 3) AS T
INNER JOIN CTE
ON CTE.name = T.Name1
This is runnable, but cannot pass the test case. I was wondering, what is going wrong??
One approach would be to use a single query with conditional aggregation to find users who placed more than three transactions in 2019 and 2020:
WITH cte AS (
SELECT user_id
FROM transactions
GROUP BY user_id
HAVING COUNT(CASE WHEN YEAR(created_at) = 2019 THEN 1 END) > 3 AND
COUNT(CASE WHEN YEAR(created_at) = 2020 THEN 1 END) > 3
)
SELECT
u.id,
u.name
FROM users u
INNER JOIN cte t
ON t.user_id = u.id;
I have the following scenario. i have three tables (users, sales, sales_details) Users to Sales is a 1 to 1 relationship and sales to sales_details is 1 to many.
I am running a query where I get all the sales for each user by joining all 3 tables without any issue.
Query looks something like this
SELECT s.month as month,u.name as name, s.year as year, s.date as date,sum(sd.qty) as qty,sum(sd.qty*sd.value) as value,s.id as id,sum(sd.stock) as stock,s.currency as currency,s.user as user
FROM sales as s
left join sales_details as sd on s.id = sd.Sales
inner join users as u on s.user = u.Id
group by s.Id
What I want to do now is add an extra field in my query which will be a subquery.
SELECT SUM(total) AS total_yearly
FROM (
SELECT sum(qty) as total
FROM sales
left join sales_details on sales.Id = sales_details.Sales
WHERE ((month <= MONTH(NOW()) and year = YEAR(NOW()))
or (month >= MONTH(Date_add(Now(),interval - 12 month)) and year = YEAR(Date_add(Now(),interval - 12 month))))
and User = **ID OF USER** ) as sub
This query on its own gives me the sales for the user for the past 12 months while the original query does it per month. I know that the result will be the same for each user but i need it for other calculations.
My problem is how I will join the 2 queries so that the subquery will read the user id from the original one.
Thanks in advance!
Group the second query by user, and then join it with the original query.
SELECT s.month as month,u.name as name, s.year as year, s.date as date,
sum(sd.qty) as qty,sum(sd.qty*sd.value) as value,s.id as id,
sum(sd.stock) as stock,s.currency as currency,s.user as user,
us.total
FROM sales as s
left join sales_details as sd on s.id = sd.Sales
inner join users as u on s.user = u.Id
inner join (
SELECT User, sum(qty) as total
FROM sales
left join sales_details on sales.Id = sales_details.Sales
WHERE ((month <= MONTH(NOW()) and year = YEAR(NOW()))
or (month >= MONTH(Date_add(Now(),interval - 12 month)) and year = YEAR(Date_add(Now(),interval - 12 month)))))
GROUP BY User) AS us ON s.user = us.user
group by s.Id
I have a database with two tables: users and payments.
Each user has many payments and each payment can be successful or failed.
I need to write a query to get all the users who failed the last 4 payments.
This is what I tried so far:
select *
from users u
where u.id in(
select p.user_id
from payments
where p.status = 'failed'
group by p.user_id
having count(p.id) = 4
);
But as you can see this is not only checking for the last 4 payments, but all of them. So, it is returning the users that have failed 4 payments (in global, not only the last 4).
I don't know if it is important but the fields on the tables are:
users:
id | name | email | password
payment:
id | date | status | user_id
| | (can be success or failed) | (FK)
Update:
This sqlfiddle will help to understand what I need.
The query is returning all users with 4 failed payments. But I only need the users whose 4 most recent payments failed. In this case it will be only user with id 5
This works
SELECT x.user_id, count(*) as cnt
FROM (
SELECT a.user_id, a.date, a.status FROM payment AS a WHERE
(SELECT COUNT(*) FROM payment AS b
WHERE b.user_id = a.user_id AND b.date >= a.date) <= 4
ORDER BY a.user_id ASC, a.date DESC) AS x
WHERE x.status = 'failed'
GROUP BY x.user_id
HAVING cnt >=4;
If you want the users, whose last 4 transactions were failed (only last 4, not total 4) then following query should get the job done:
select u.* from users u
where
id in
(select p.user_id from payment p
where (select count(*) from payment p1
where p.user_id = p1.user_id
and p.date <= p1.date
order by p1.user_id asc,p1.date desc
) <= 4
and p.status <> 'success'
group by p.user_id
having count(*)>=4);
check the sqlfiddle
Hope it helps!
You want to use the LIMIT keyword, and specify an ORDER.
Try this
select *
from users u
where u.id in(
select p.user_id
from payments
where p.status = 'failed'
group by p.user_id
having count(p.id) = 4
) ORDER BY p.id DESC LIMIT 4;
Not entirely sure what you are trying to do inside the WHERE statement, but ORDER BY p.id DESC LIMIT 4 will retrieve the four most recent rows.
I think you can use a query like this:
select users.id, users.name, users.email, users.password
from users
left join (
select p1.id, p1.date, p1.status, p1.user_id,
count(p2.id) seq -- this count() creates a sequence number for each user ordered by date
from payment p1
left join payment p2
on p1.user_id = p2.user_id -- here I set sequence for each user
and p1.date <= p2.date -- here I set sequence is ordered by data
group by p1.id, p1.date, p1.status, p1.user_id
) t
on users.id = t.user_id
where t.seq < 5 -- Now filter last 4 sequences of each user's payments
and t.status = 'failed'
group by users.id, users.name, users.email, users.password
having count(*) = 4; -- At last filter those have 4 failed in last 4 sequences
[ SQL Fiddle Demo ]
Let's say we have the following model :
A table of exercises (id, name)
A table of users (id, name, email)
A tables of exams (id, user_id, day)
A join table between exams and exercises (exam_id, exercise_id)
Each user can make one exam each day.
Each exam is made of many exercises (has and belongs to many).
For each user (when logged), I would like to display a table displaying all the exercises and for each the count and percent of exams the user made during a given period of time (using the day attribute of the exams).
At this time I can display all the exercises the user made and their percent during a given period with the following query.
SELECT
o.user_id,
o.exercise_id,
o.exercise_name,
COUNT(*) AS nb_exams,
(COUNT(*) * 100 / (
SELECT COUNT(*)
FROM exams
LEFT JOIN users ON exams.user_id = users.id
WHERE users.id = 1 AND exams.day >= "2015-09-01" AND exams.day <= "2015-09-07"
)) AS percent
FROM (
SELECT
exercises.id AS exercise_id,
exercises.name AS exercise_name,
exams.id AS exam_id,
users.id AS user_id
FROM exercises
LEFT JOIN exercises_exams ON exercises_exams.exercise_id = exercises.id
LEFT JOIN exams ON exercises_exams.exam_id = exams.id
LEFT JOIN users ON exams.user_id = users.id
WHERE users.id = 1 AND exams.day >= "2015-09-01" AND exams.day <= "2015-09-07"
GROUP BY exercise_id,exam_id
) AS o
GROUP BY exercise_id;
But I also want to display the exercises he did not make yet with a value of 0.
Is it possible to do this with one query of mysql ?
EDIT-
Here is the sqlfiddle for the current query. You will notice that only the 3 exercises which are used in exams are returned. I would like to list all the exercises with the count and percent (even the fourth with 0)
Firstly, you should remove the where condition to the join clause in the inner query to see a 0 result when an exercise wasn't performed. Getting a user_id corresponding to the 0 result doesn't make sense. This is the closest solution i can think of.
Fiddle
SELECT
o.user_id,
o.exercise_id,
o.exercise_name,
sum(case when exam_id is null then 0 else 1 end) AS nb_exams,
(sum(case when exam_id is null then 0 else 1 end) * 100 / (
SELECT COUNT(*)
FROM exams
LEFT JOIN users ON exams.user_id = users.id
where exams.day >= "2015-09-01" AND exams.day <= "2015-09-07"
)) AS percent
FROM (
SELECT
exercises.id AS exercise_id,
exercises.name AS exercise_name,
exams.id AS exam_id ,
users.id AS user_id
FROM exercises
LEFT JOIN exercises_exams ON exercises_exams.exercise_id = exercises.id
LEFT JOIN exams ON exercises_exams.exam_id = exams.id
and exams.day >= "2015-09-01" AND exams.day <= "2015-09-07"
LEFT JOIN users ON exams.user_id = users.id
) AS o
GROUP BY exercise_id,exercise_name
I've got 2 tables: Online Orders, Online orders details
I have an issue on how to calculate the total amount of the order in Online Order table based on the detail order table.
The detail table looks like:
id_order Id_product quantity price value
1 2 1 3 3
1 3 2 2 4
2 1 1 5 5
I would like to sum all the values from an id_order and insert them into the total amount of the order in the Online orders table.
Can you help me with the SQL command?
It's not clear what database system you use.
If you want to UPDATE orders.total_amount
This update statement will work under any DB:
update orders
set total_amount = (
select SUM(value)
from orders_details
where id_order = orders.id
)
where EXISTS(select *
from orders_details
where id_order = orders.id)
This update statement works under MySQL:
update orders u
inner join (select id_order, SUM(value) as total
from orders_details
GROUP BY id_order) s on
u.id = s.id_order
set u.total_amoun = s.total
Is this what you want?
SELECT o.id_order, SUM(quantity * price) AS total_price
FROM online o
INNER JOIN detail d
ON d.id_order = o.id_order
GROUP BY o.id_order