MySQL AVG(TIMESTAMPDIFF) with GROUP BY - mysql

I have two tables user (one) and transaction (many) and I need to get the average time in days from when a user was created to when they made their first transaction. I'm using AVG(TIMESTAMPDIFF) which is working well, except that the GROUP BY returns an average against every user instead of one single average for all unique users in the transaction table. If I remove the GROUP BY, I get a single average figure but it takes into account multiple transactions from users, whereas I just want to have one per user (the first they made).
Here's my SQL:
SELECT AVG(TIMESTAMPDIFF(DAY, u.date_created, t.transaction_date)) AS average
FROM transaction t
LEFT JOIN user u ON u.id = t.user_id
WHERE t.user_id IS NOT NULL AND t.status = 1
GROUP BY t.user_id;
I'd appreciate it if someone can help me return the average for unique users only. It's fine to break the query down into two, but the tables are large so returning lots of data and putting it back in is a no-go. Thanks in advance.

SELECT AVG(TIMESTAMPDIFF(DAY, S.date_created, S.transaction_date)) AS average
FROM (
SELECT u.date_created, t.transaction_date
FROM transaction t
INNER JOIN user u ON u.id = t.user_id
WHERE t.status = 1
GROUP BY t.user_id
HAVING u.date_created = MIN(u.date_created)
) s
I replaced the LEFT JOIN with an INNER JOIN because I think that's what you want, but it's not 100% equivalant to your WHERE t.user_id IS NOT NULL.
Feel free to put the LEFT JOIN back if need be.

select avg( TIMESTAMPDIFF(DAY, u.date_created, min_tdate) ) as average
from user u
inner join
(select t.user_id, min(t.transaction_date) as min_tdate
from transaction t
where t.status=1;
group by t.user_id
) as min_t
on u.id=min_t.user_id;

Related

Wrong use of inner join function / group function?

I have the following problem with my query:
I have two tables:
Customer
Subscriber
linked together by customer.id=subscriber.customer_id
in the subscriber table, I have records with id_customer=0 (these are email records, that do not have a full customer account)
Now i want to show how many customers I have per day, and how many subscribers with id_customer, and how many subscribers WITH id_customer=0 (emailonlies i call them)
Somehow, i cannot manage to get those emailonlies.
Perhaps it has something to do with not using the right join type.
When i use left join, i get the right amount of customers, but not the right amount of emailonlies. When I use inner join i get the wrong amount of customers. Am i using the group function correctly? i think it has something to do with that.
THIS IS MY QUERY:
` SELECT DATE(c.date_register),
COUNT(DISTINCT c.id) AS newcustomers,
COUNT(DISTINCT s.customer_id) AS newsubscribedcustomers,
COUNT(DISTINCT s.subscriber_id AND s.customer_id=0) AS emailonlies
FROM customer c
LEFT JOIN subscriber s ON s.customer_id=c.id
GROUP BY DATE(c.date_register)
ORDER BY DATE(c.date_register) DESC
LIMIT 10
;`
I'm not entirely sure, but I think in DISTINCT s.subscriber_id AND s.customer_id=0, it runs the AND before the DISTINCT, so the DISTINCT only ever sees true and false.
Why don't you just take
COUNT(DISTINCT s.subscriber_id) - (COUNT(DISTINCT s.customer_id) - 1)?
(The -1 is there because DISTINCT s.customer_id will count 0.)
Got it, only risk is that i get no email onlies if there are no customers on this day, becuase of the left join. But this one works:
SELECT customers.regdatum,customers.customersqty,subscribers.emailonlies
FROM (
(SELECT DATE(c.date_register) AS regdatum,COUNT(DISTINCT c.id) AS customersqty
FROM customer c
GROUP BY DATE(c.date_register)
) AS customers
LEFT JOIN
(SELECT DATE(s.added) AS voegdatum,COUNT(DISTINCT s.subscriber_id) AS emailonlies
FROM subscriber s
WHERE s.customer_id=0
GROUP BY DATE(s.added)
) AS subscribers
ON customers.regdatum=subscribers.voegdatum
)
ORDER BY customers.regdatum DESC
;

Running an SQL query in the loop of another SQL query?

So I have the following query which fetches Active Competitions within an organisation and also tries to fetch the user who is leading the competition.
Currently the query correctly fetches the active competitions, and the totalPoints for each user. It now grabs all users, I only want it to grab the top user, so I am assuming the solution lies in the GROUP BY query, some sort of LIMIT?
In this image, you can see the results I am getting. As you can see, I am getting every user for each competition, where I only need the top user for each competition.
http://i.imgur.com/5OXen4e.png
Any idea on how I could solve this?
SELECT c.competitionId, c.name, c.start_date, c.end_date, a.userid, u.name, u.profilePic ,
SUM(activity_weight) AS totalPoints
FROM activity_entries a INNER JOIN users1 u ON u.id = a.userid INNER JOIN competitions c ON c.competitionId = a.competitionId
WHERE c.organisationId = 1 AND c.start_date < now() AND c.end_date > now()
GROUP BY a.userid, c.competitionId ORDER BY c.id DESC
There is a better way. Just run one query with and join to the competitions table. The second query you posted shows that you know how to join tables.

MySQL order by with count and group by issues

My Tables look like:
# Table user
user_id PK
...
# Table buy
buy_id PK
user_id FK
...
# Table offert
offert_id
user_id
...
Well i need to know the last 'buy' of 1 'user' and get the count of 'offert' this 'user' has, I tried something like:
select b.buy_id,count(distinct c.offert_id) as cv from user a
inner join buy b using(user_id) left join offert c using(user_id) where a.user_id=4
group by a.user_id order by b.buy_id desc
but it always returns the first 'buy' not the last, look like this order by doesn't make any effect
I know that i can do it with sub queries but i would like know if is there a way to do it whout use sub queries, maybe using max functions but idk how to do it.
thanks.
Your approach is simply not guaranteed to work. One big reason is that the group by is processed before the order by.
Assuming that you mean the biggest buy_id for each user, you can do this as:
select u.user_id, u.last_buy_id, count(distinct o.offert_id)
from (select u.*,
(select buy_id from buy b where u.user_id = u.user_id order by buy_id desc limit 1
) last_buy_id
from user u
) left outer join
offert o
on o.user_id = u.user_id
group by u.user_id;
The first subquery uses a correlated subquery to get the last buy id for each user. It then joins in offert and does the aggregation. Note that this version includes the user_id in the aggregation.

SQL Join returns only one record

I'm writing a query whereby I'm trying to count the total number of records in report and assignment table, whiles at the same time retrieving information from the main table group. Group has a primary key id which is saved in the other tables as gid. This is the query:
SELECT `group`.`id` AS `gid`
, `group`.`name` AS `g_name`
, COUNT(`report`.`id`) AS `reports`
FROM `group`
LEFT OUTER JOIN `report` ON `report`.`gid` = `group`.`id`
LEFT OUTER JOIN `assignment` ON `assignment`.`gid` = `group`.`id`
WHERE `group`.`active` = 0
ORDER BY
`group`.`name`;
My problem is whenever I execute this only one record is returned even if theirs multiple groups.
Thanks in advance.
Well, your query is far from correct :) First of all, you should not have aggregated functions (in this case count) without a group by clause. Now, even if you have that clause the query will summarize information and you want both: the detail and a summary in the same query. I'd recommend 2 separate queries to retrieve this information, but if you want information mixed in only one query (the detail and also the "total number of records in report and assignment table") try the following query:
SELECT
`group`.id AS gid,
`group`.name AS g_name,
(SELECT COUNT(*) from report) as ReportTotalCount,
(SELECT COUNT(*) from assignment) as AssignmentTotalCount,
FROM `group`
WHERE `group`.`active` = 0
LEFT OUTER JOIN report ON report.gid = `group`.id
LEFT OUTER JOIN assignment ON assignment.gid = `group`.id
ORDER BY `group`.name;
I whish I could understand exactly what you're looking for but this might give you an idea on how to get the result you expect.
Can't see anything obvious in your query that would limit it to returning one record.
You are going to have to break it up to see where the problem is against your existing data.
So how many groups where acitive = 0, ahow many with a corresponding assignment record, etc.
maybe it will help:
SELECT
groupid,
groupname,
reports,
assignments,
FROM
(SELECT group.id, group.name, COUNT(*) AS reports from group
INNER JOIN report ON (report.gid = group.id)
WHERE group.active = 0
GROUP BY group.id ) AS ReportForGroup
CROSS JOIN
(SELECT group.id AS groupid, group.name AS groupname, COUNT(*) AS assignments from group
INNER JOIN assignmentON (assignment.gid = group.id)
WHERE group.active = 0
GROUP BY group.id ) AS AssignmentForGroup
ON (ReportForGroup.groupid = AssignmentForGroup.groupid)
ORDER BY groupname;
I'm can't check it so if LEFT JOIN returns to COUNT(*) 0 or 1. if it returns 0 just change the INNERs to LEFTs and use INNER JOIN between the two queries

Counting results from multiple tables with same column

I have a system where, essentially, users are able to put in 3 different pieces of information: a tip, a comment, and a vote. These pieces of information are saved to 3 different tables. The linking column of each table is the user ID. I want to do a query to determine if the user has any pieces of information at all, of any of the three types. I'm trying to do it in a single query, but it's coming out totally wrong. Here's what I'm working with now:
SELECT DISTINCT
*
FROM tips T
LEFT JOIN comments C ON T.user_id = C.user_id
LEFT JOIN votes V ON T.user_id = V.user_id
WHERE T.user_id = 1
This seems to only be getting the tips, duplicated for as many votes or comments there are, even if the votes or comments weren't made by the specified user_id.
I only need a single number in return, not individual counts of each type. I basically want a sum of the number of tips, comments, and votes saved under that user_id, but I don't want to do three queries.
Anyone have any ideas?
Edit: Actually, I don't even technically need an actual count, I just need to know if there are any rows in any of those three tables with that user_id.
Edit 2: I almost have it with this:
SELECT
COUNT(DISTINCT T.tip_id),
COUNT(DISTINCT C.tip_id),
COUNT(DISTINCT V.tip_id)
FROM tips T
LEFT JOIN comments C ON T.user_id = C.user_id
LEFT JOIN votes V ON T.user_id = V.user_id
WHERE T.user_id = 1
I'm testing with user_id 1 (me). I've made 11 tips, voted 4 times, and made no comments. My return is a row with 3 columns: 11, 0, 4. That's the proper count. However, I tested it with a user that hasn't made any tips or comments, but has voted 3 times, that returned 0 for all counts, it should have returned: 0, 0, 3.
The problem that I'm having seems to be that if the table that I'm using for the WHERE clause doesn't have any rows from that user_id, then I get 0 across the board, even if the other tables DO have rows with that user_id. I could use this query:
SELECT
(SELECT COUNT(*) FROM tips WHERE user_id = 2) +
(SELECT COUNT(*) FROM comments WHERE user_id = 2) +
(SELECT COUNT(*) FROM votes WHERE user_id = 2) AS total
But I really wanted to avoid running multiple queries, even if they're subqueries like this.
UPDATE
Thanks to ace, I figured this out:
SELECT
(COUNT(DISTINCT T.tip_id) + COUNT(DISTINCT C.tip_id) + COUNT(DISTINCT V.tip_id)) AS total
FROM users U
LEFT JOIN tips T ON U.user_id = T.user_id
LEFT JOIN votes V ON U.user_id = V.user_id
LEFT JOIN comments C ON U.user_id = C.user_id
WHERE U.user_id = 4
the users table contains the actual information bout the user including, obviously, the user id. I used the user table as the parent, since I could be 100% sure that the user would be present in that table, even if they weren't in the other tables. I got the proper count that I wanted with this query!
As I understand your question. You want to count the total comments + tips + votes for each user. Though is not really clear to me take a look at below query. I added columns for details this is a cross tabs query as someone teach me.
EDITED QUERY:
SELECT
COALESCE(COALESCE(t2.tips,0) + COALESCE(c2.comments,0) + COALESCE(v2.votes,0)) AS `Totals`
FROM parent p
LEFT JOIN (SELECT t.user_id, COUNT(t.tip_id) AS tips FROM tips t GROUP BY t.user_id) t2
ON p.user_id = t2.user_id
LEFT JOIN (SELECT c.user_id, COUNT(c.tip_id) AS comments FROM comments c GROUP BY c.user_id) c2
ON p.user_id = c2.user_id
LEFT JOIN (SELECT v.user_id, COUNT(v.tip_id) AS votes FROM votes v GROUP BY v.user_id) v2
ON p.user_id = v2.user_id
WHERE p.user_id = 1;
Note: This used a parent table in order to get the result of a table which doesn't in other table.
The reason why I use a sub-query in my JOIN is to create a virtual table that will get the sum of tip_id for each table. Also I'm having problem with the DISTINCT using the same query of yours, so I end up with this query.
I know you prefer not using sub-queries, but I failed without a sub-query. For now this is all I can.