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;
Related
Only if the used_at and expired_at is null the credit is counted as remaining credit
user table:
id
email
1
row
2
row
user_credit table:
id(pk)
user_id(fk)
purchase_date
no.of.credits purchased
1
1
2022-07-21
150
2
1
2022-06-03
5
user_credit_history table:
credit_id(fk)
used_at
expired_at
1
2022-06-03
null
1
null
null
2
2022-07-02
null
The query I use is:
select u.id, u.email, u.name, uc.id as credit_id, uc.added_at as purchase_date, uc.credits_added, count(uch.credit_id) as credits_remaining
from user u
left join user_credit uc on uc.user_id = u.id
left join user_credit_history uch on uch.credit_id = uc.
where uc.type='CREDIT'
and uc.credits_added >= 100
and (uch.used_at is null and uch.expired_at is null)
group by uch.credit_id
order by uc.added_at desc;
But this query is giving me all the purchase details of each user but I only need latest one.
instead of joining the table "user_credit_history" you can use a subselect to get the 1 row you want per user by using order by in your subselect for the field "used_at".
1. you can use :-
SELECT
u.id,
u.email,
u.name,
uc.id AS credit_id,
uc.added_at AS purchase_date,
uc.credits_added,
COUNT(uch.credit_id) AS credits_remaining
FROM
user u
LEFT JOIN
(SELECT
user_id, id, added_at, credits_added
FROM
user_credit
GROUP BY user_id
HAVING MAX(purchase_date)) AS uc ON uc.user_id = u.id
LEFT JOIN
user_credit_history uch ON uch.credit_id = uc.id
WHERE
uc.type = 'CREDIT'
AND uc.credits_added >= 100
AND (uch.used_at IS NULL
AND uch.expired_at IS NULL)
GROUP BY uch.credit_id
ORDER BY uc.added_at DESC;
It is difficult to understand because there is no expected results...
This query calculated for each user its latest purchase and fetches it and calculates the remaining credits for the credit_id of the latest purchase of the user and I've added the 100 filter:
select u.id, u.email, u.name, uc.id as credit_id, uc.added_at as purchase_date, uc.credits_added, count(credit_id) as credits_remaining
from
(
select user_id, max(added_at) as latest_purchase
from user_credit
group by user_id
) up
inner join user_credit uc on uc.user_id=up.user_id and uc.added_at=up.latest_purchase
inner join users u on u.id=uc.user_id
inner join user_credit_history uch on uch.credit_id=uc.id
where uc.type='CREDIT' and
uc.credits_added >= 100 and
used_at is null and expired_at is null
group by uch.credit_id, u.id, u.email, u.name, uc.id as credit_id, uc.added_at as purchase_date, uc.credits_added
[DB Table]
SELECT b.first_name, b.last_name, a.pod_name, a.category, c.user_id,
SUM(IF(QUARTER(CURDATE())-1 OR (QUARTER(CURDATE())-2) AND a.user_id, 1, 0)) AS flag FROM kudos a
INNER JOIN users b ON a.user_id = b.id INNER JOIN users_groups c ON a.user_id = c.user_id
INNER JOIN groups d ON c.group_id = d.id WHERE a.group_name = 'G2' AND d.id IN (7,8,9,11,12,13,14,15,16,17,21,22,23,24,25,26,27,28)
AND QUARTER(CURDATE())-1 = a.quarter ORDER BY a.final_score+0 DESC
I need to get the user_ids of those users which are both in quarter 1 and 2 from table.
Tried above query but failed to get expected results.
Can someone please guide me on this?
if you only need user_id then you can do this :
select user_id
from tablename
where quarter in (1,2)
group by user_id
having count(distinct quarter) = 2
another way is to use window function, assuming you have one user id in each quarter:
select * from (
select * , count(*) over (partition by user_id) cn
from tablename
where quarter in (1,2)
) t where cn = 2
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 ]
I have 2 tables one has the user_info and another has user_activies.
I have a query that fetches several rows from user_info table. What I want to add to this query is;
I want to fetch todays user activities and count them from user_activities table.
user_info
| id | name | views | lastlogin | regdate | group_id |
user_activities
| id | userid | activity | date |
Current query
select id, name, views, lastlogin
from user_info
where group_id = 2
ORDER BY user_info.id ASC
How could I concatenate count of the total number of activities has been done today?
Thanks in advance!
i'm assuming that date is of type date:
select
u.id,
u.name,
u.views,
u.lastlogin,
sum(
-- if datetime IF (date(date) = curdate() , 1, 0)
IF (date = curdate() , 1, 0)
) as 'today_activities'
from user_info u
-- using left join you will get the list of all user even if they
-- don't have any activities today with sum activities= 0
LEFT JOIN user_activities a on u.id = a.userid
where
group_id = 2
group by u.id
ORDER BY u.id ASC
You want this:
SELECT i.id, i.name, i.views, i.lastlogin, Count(a.id)
FROM user_info i, user_activities a
WHERE i.group_id = 2 AND i.id = a.userid
AND a.date = CURDATE()
ORDER BY user_info.id ASC;
This gives you a record of every user with a count of todays activities. (You may need to change the `a.date = CURDATE()` to fit your timestamp needs)
This will not give you a list of ervery user. Instead you will have to select a single user. If you select multiple users you will get a random name with the sum of all activities from all selected users.
Or short: This does not solve your problem. Take the JOIN-solution.
you can try this:
SELECT i.id, i.name, i.views, i.lastlogin, Count(a.activities)
FROM user_info i inner join user_activities a
WHERE i.group_id = 2 AND i.id = a.userid
AND a.date = now()
ORDER BY user_info.id
I have three tables, users, activities and purchases.
Users has many activities and purchases, activities has 4 types.
I need to query users like this:
[
{
user_id: 1,
// from activities
post_count: 2,
updated_count: 3,
print_count: 4,
share_count: 5,
// from purchases
purchase_count: 6
},
...
]
I use this sql:
SELECT u.id, post.post_count, updated.update_count, print.print_count, share.share_count, purchase.purchase_count
FROM users as u
LEFT JOIN (
SELECT user_id, activity_type, count(*) as post_count
FROM activities
WHERE activity_type = 1
GROUP BY user_id
) post
ON u.id = post.user_id
LEFT JOIN (
SELECT user_id, activity_type, count(*) as update_count
FROM activities
WHERE activity_type = 2
GROUP BY user_id
) updated
ON u.id = updated.user_id
LEFT JOIN (
SELECT user_id, activity_type, count(*) as print_count
FROM activities
WHERE activity_type = 3
GROUP BY user_id
) print
ON u.id = print.user_id
LEFT JOIN (
SELECT user_id, activity_type, count(*) as share_count
FROM activities
WHERE activity_type = 4
GROUP BY user_id
) share
ON u.id = share.user_id
LEFT JOIN (
SELECT user_id, count(*) AS purchase_count
FROM purchases
GROUP BY user_id
) purchase
ON u.id = purchase.user_id
how can i optimize performance with this query?
Great thanks to Eugen Rieck
I modified his query to this, then it works.
SELECT
users.id AS user_id,
SUM(IF((activities.activity_type=1),1,0)) AS post_count,
SUM(IF((activities.activity_type=2),1,0)) AS update_count,
SUM(IF((activities.activity_type=3),1,0)) AS print_count,
SUM(IF((activities.activity_type=4),1,0)) AS share_count,
IFNULL(purchase.count,0) AS purchase_count
FROM
users
LEFT JOIN activities ON activities.user_id=users.id
LEFT JOIN (
SELECT user_id, count(*) AS count
FROM purchases
GROUP BY user_id
) purchase
ON users.id = purchase.user_id
GROUP BY users.id
Currently you run the activities table 4 times - this could be folded into one:
SELECT
users.id AS user_id,
SUM(IF(activites.activity_type=1,1,0)) AS post_count,
SUM(IF(activites.activity_type=2,1,0)) AS update_count,
SUM(IF(activites.activity_type=3,1,0)) AS print_count,
SUM(IF(activites.activity_type=4,1,0)) AS share_count,
IFNULL(COUNT(purchases.id),0) AS purchase_count
FROM
users
INNER JOIN activities ON activities.user_id=users.id
LEFT JOIN purchases ON purchases.user_id=users.id
GROUP BY users.id