I have a MYSQL Table with the following structure called daily_measurements
+------------+----------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+---------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | NO | | 0 | |
| date | datetime | NO | MUL | 0000-00-00 00:00:00 | |
| weight | float | NO | | 0 | |
| bicep | float | NO | | 0 | |
| chest | float | NO | | 0 | |
| waist | float | NO | | 0 | |
| neck | float | NO | | 0 | |
| thigh | float | NO | | 0 | |
| hips | float | NO | | 0 | |
| shoulders | float | NO | | 0 | |
| knee | float | NO | | 0 | |
| ankle | float | NO | | 0 | |
| created_on | datetime | NO | | 0000-00-00 00:00:00 | |
+------------+----------+------+-----+---------------------+----------------+
I need to retrieve a list of every user's weight for there first and last entry.
I've tried various combinations of GROUP BY, MIN(date), MAX(date), etc. but I can't seem to figure out a way to do it efficiently.
The only way I've been able to get this to work is to do the following query on the users table, w/ 2 subqueries, but since there are aprox 30,000 users and > 200,000 measurements the query chokes up pretty bad.
SELECT u.id,
(SELECT user_id, weight, date FROM daily_measurements WHERE user_id = u.id ORDER BY date DESC limit 1) as starting_weight,
(SELECT user_id, weight, date FROM daily_measurements WHERE user_id = u.id ORDER BY date ASC limit 1) as ending_weight
FROM users u
Any help would be appreciated.
My solution:
SELECT
u1.user_id,
u2.first_entry_weight,
u1.weight AS last_entry_weight
FROM daily_measurements u1
INNER JOIN (SELECT
u1.user_id,
u1.weight AS first_entry_weight,
u2.fe,
u2.le
FROM daily_measurements u1
INNER JOIN (SELECT
daily_measurements.user_id,
MIN(date_entry) fe,
MAX(date_entry) le
FROM daily_measurements
GROUP BY daily_measurements.user_id) u2
ON u1.user_id = u2.user_id
AND u1.date_entry = u2.fe) u2
ON u1.user_id = u2.user_id
AND u1.date_entry = u2.le
can not test it and it's performance at the moment but I thing u can start from the following query:
SELECT
u.id,
SUBSTRING_INDEX( GROUP_CONCAT(CAST(d.weight AS CHAR) ORDER BY d.date ASC ), ',', 1 ) as starting_weight,
SUBSTRING_INDEX( GROUP_CONCAT(CAST(d.weight AS CHAR) ORDER BY d.date DESC), ',', 1 ) as ending_weight
FROM users as u
LEFT JOIN daily_measurements as d ON (u.id = d.user_id)
edit please treat this as a suggestion for your Query...
with such amount of users "JOIN" could be hundreds times faster then two SELECT sub-queries
SELECT A.user_id,
B.weight InitialWeight,
B.`date` InitialDate,
C.weight LatestWeight,
C.`date` LatestDate
FROM
(
SELECT user_id,MIN(id) idmin,MAX(id) idmax
FROM daily_measurements GROUP BY user_id
) A
INNER JOIN daily_measurements B ON (A.user_id=B.user_id AND A.idmin = B.id)
INNER JOIN daily_measurements C ON (A.user_id=C.user_id AND A.idmax = C.id);
Please make sure you have an index like this
ALTER TABLE daily_measurements ADD UNIQUE INDEX userid_id_ndx (user_id,id);
Try this:
select tb.* from daily_measurements tb
join (
select user_id, MIN(date) firstDate, MAX(date) lastDate
from daily_measurements
group by user_id
) temp
on tb.user_id = temp.user_id
and (tb.date = temp.firstDate or tb.date = temp.lastDate)
The subquery will identify first date and last date rows for each user_id, and main query will fetch the rows again to get all the data.
Related
i have a following tables in MySQL database:
+------------------------+
| Users |
+----+--------+----------+
| id | name | role |
+----+--------+----------+
| 1 | Martin | admin |
+----+--------+----------+
| 2 | George | admin |
+----+--------+----------+
| 3 | John | employee |
+----+--------+----------+
+-------------------------+
| Forms |
+----+--------------------+
| id | type |
+----+--------------------+
| 10 | marketing_form |
+----+--------------------+
| 11 | client_survey_form |
+----+--------------------+
| 12 | client_survey_form |
+----+--------------------+
+---------------------------------------------+
| UsersAssignToForms |
+----+---------+---------+--------------------+
| id | user_id | form_id | additional_comment |
+----+---------+---------+--------------------+
| 20 | 1 | 10 | Lorem ipsum... |
+----+---------+---------+--------------------+
| 21 | 2 | 10 | Lorem ipsum.... |
+----+---------+---------+--------------------+
| 22 | 3 | 10 | null |
+----+---------+---------+--------------------+
| 23 | 3 | 11 | null |
+----+---------+---------+--------------------+
I would like to have result:
+---------+---------+------------+--------------------+--------------------+
| user_id | form_id | first_name | form_type | additional_comment |
+---------+---------+------------+--------------------+--------------------+
| 1 | 10 | Martin | marketing_form | Lorem ipsum... |
+---------+---------+------------+--------------------+--------------------+
| 3 | 11 | John | client_survey_form | null |
+---------+---------+------------+--------------------+--------------------+
| null | 12 | null | client_survey_form | null |
+---------+---------+------------+--------------------+--------------------+
First of all i would like to limit number of users returned from join query (one user per one form). If user with admin role is assigned to form i would like to display this user (prioritize admin role over employee role) and limit number of returned users to 1, if admin is not assign, but employee is assigned query should return this user, if no-one is assign query should return nulls (left or right join probably).
I saw this question on stackoverflow - MySQL JOIN with LIMIT 1 on joined table, but unfortunately first answer has n+1 issue and rest of answers was made with simple one join. For my purposes i need to join more tables but wouldn't like to design this tables above to clarify what i would like to achieve, but it's very important.
So my query will looks like probably:
SELECT u.id, f.id, u.name, f.type, uf.additional_comment, [more selects from other tables...] FROM Forms as f
LEFT JOIN Users as u ON ......
INNER JOIN UsersAssignToForms as uf ON .....
[here i would like to put more and more inner joins.....]
In MySql >= 8.0 you can number the rows using some criteria (for each Form starting from one and order by u.role ASC and u.id ASC), then you can filter rows with number one:
WITH sq AS (SELECT u.id AS user_id, f.id AS form_id, u.name, f.type, uf.additional_comment,
ROW_NUMBER() OVER (PARTITION BY f.id ORDER BY u.role ASC, u.id ASC) AS num
FROM Forms AS f
LEFT JOIN UsersAssignToForms AS uf ON f.id = uf.form_id
LEFT JOIN Users AS u ON u.id = uf.user_id)
SELECT *
FROM sq
WHERE num = 1;
Before MySql 8.0 you can try something like this (the idea is the same but with different implementation):
SELECT sq2.user_id, sq2.form_id, sq2.name, sq2.type, sq2.additional_comment
FROM (
SELECT
sq1.*,
#row_number:=CASE WHEN #form_id = sq1.form_id THEN #row_number + 1 ELSE 1 END AS num,
#form_id:= sq1.form_id
FROM (SELECT u.id AS user_id, f.id AS form_id, u.name, f.type, uf.additional_comment
FROM Forms AS f
LEFT JOIN UsersAssignToForms AS uf ON f.id = uf.form_id
LEFT JOIN Users AS u ON u.id = uf.user_id
ORDER BY f.id ASC, u.role ASC, u.id ASC) AS sq1
ORDER BY sq1.form_id) AS sq2
WHERE sq2.num = 1;
I have 2 tables which looked like:
user_dataset
+------+---------------+------------+
|userid| Register_time | Country |
+------+---------------+------------+
| 1 | 03/02/17 | TW |
| 2 | 20/03/17 | SG |
| 3 | 26/03/17 | PH |
| 4 | 05/02/17 | VN |
| 5 | 01/10/17 | ID |
| 6 | 03/09/17 | MY |
| ...| ........ | ... |
+------+---------------+------------+
order_dataset
+--------+--------+------------+--------+------------+
|orderid | userid | itemid | gmv | order_time |
+--------+--------+------------+--------+------------+
|1030132 | 3 | 3366770 | 27,0 | 24/04/17 |
|1030137 | 5 | 6130641 | 69,0 | 02/02/17 |
|1030147 | 1 | 6770063 | 87,0 | 25/04/17 |
|1030153 | 6 | 4193426 | 82,0 | 05/11/17 |
|1030155 | 4 | 8825994 | 29,0 | 03/07/17 |
|1030160 | 2 | 5660916 | 44,0 | 30/01/17 |
|....... | ... | ... | ... | ... |
+--------+--------+------------+--------+------------+
I have been told to Write a SQL statement to find the number of users who made their first order in each country, each day.
I don't understand the question, can someone explain to me what the output looks like? I'm not asking the Queries but if someone willing to write it would be wonderful tho.
Thank you in advance
Join the table user_dataset to a query that returns from order_dataset the date of the 1st order of each userid and aggregate:
select u.country, o.order_time, count(*) counter
from user_dataset u inner join (
select userid, min(order_time) order_time
from order_dataset
group by userid
) o on o.userid = u.userid
group by u.country, o.order_time
If there is a case a user has placed 2 orders at the same day, then instead of count(*) use count(distinct userid).
SELECT a.userid, a.purchase_time,c.country
FROM purchase_tab a
INNER JOIN
(SELECT userid, MIN(purchase_time) As first_occurence
FROM purchase_tab
GROUP BY userid) b
ON a.userid = b.userid AND a.purchase_time = b.first_occurence
INNER JOIN user_tabs c
ON a.userid=c.userid
ORDER BY a.userid
SELECT
country,
order_time,
COUNT(*)
FROM user u
LEFT JOIN(
SELECT
userid,
MIN(order_time) AS min_date
FROM order
GROUP BY userid
) o
ON u.userid = o.userid
LEFT JOIN order ot
ON u.userid = ot.userid
WHERE order_time = min_date
GROUP BY country, order_time
ORDER BY country;
I'm currently writing a ticket system that has three tables
one for users:
users
+----+-----------+----------+
| ID | FirstName | LastName |
+----+-----------+----------+
| 1 | First | User |
| 2 | Second | User |
| 3 | Third | User |
| 4 | Fourth | User |
| 5 | Fifth | User |
+----+-----------+----------+
one for tickets:
ticket
+----+---------------+
| ID | TicketSubject |
+----+---------------+
| 1 | Ticket #1 |
| 2 | Ticket #2 |
| 3 | Ticket #3 |
| 4 | Ticket #4 |
+----+---------------+
and one to assign users to tickets to action (can be more than one user per ticket):
ticket_assigned
+----+----------+--------+
| ID | TicketID | UserID |
+----+----------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 3 | 5 |
| 5 | 3 | 3 |
+----+----------+--------+
I'm trying to create a summary to show each user, and how many tickets they have assigned to them, example:
+------------+-------+
| Name | Count |
+------------+-------+
| First | 2 |
| Second | 1 |
| Third | 1 |
| Fourth | 0 |
| Fifth | 1 |
| Unassigned | 2 |
+------------+-------+
Note that the last entry is "unassigned", this is the number of records in the ticket table that DONT appear in the ticket_assigned table (thus being, unassigned). Also further note that user "Fourth" is zero, in that that user has no records in the ticket_assigned table.
Here is the current MySQL query I am using:
SELECT
CASE
WHEN users.FirstName IS NULL
THEN 'Unassigned'
ELSE users.FirstName
END as 'UserName',
COUNT(*) as 'TicketCount'
FROM tickets
LEFT OUTER JOIN ticket_assigned ON tickets.ticket_id = ticket_assigned.ticket_id
LEFT OUTER JOIN users ON ticket_assigned.user_id = users.user_id
GROUP BY ticket_assigned.user_id
ORDER BY UserName;
Problem with this is that it's not showing any of the users that don't feature in the ticket_assigned table, I'm essentially getting this:
+------------+-------+
| Name | Count |
+------------+-------+
| First | 2 |
| Second | 1 |
| Third | 1 |
| Fifth | 1 |
| Unassigned | 2 |
+------------+-------+
Is anyone able to assist and tell me how I can modify my query to include users that have no records in the ticket_assigned table? Thanks in advance!
Use a LEFT JOIN with a subquery to aggregate tickets:
SELECT t1.FirstName,
COALESCE(t2.ticket_count, 0) AS num_tickets
FROM users t1
LEFT JOIN
(
SELECT UserID, COUNT(*) AS ticket_count
FROM ticket_assigned
GROUP BY UserID
) t2
ON t1.ID = t2.UserID
UNION ALL
SELECT 'Unassigned', COUNT(*)
FROM tickets t
WHERE NOT EXISTS (SELECT 1 FROM tickets_assigned ta
WHERE ta.ticketId = t.id)
In MySQL, I think you need a left join and union all:
select u.id, u.firstname, count(ta.userId) as num_tickets
from users u left join
tickets_assigned ta
on ta.userId = u.id
group by u.id, u.firstname
union all
select NULL, 'Unassigned', count(*)
from tickets t
where not exists (select 1
from tickets_assigned
where ta.ticketId = t.id
);
I included the u.id in the aggregations. I'm uncomfortable just aggregating (and reporting) by first name, because different people frequently have the same first name, even in a relatively small group.
SELECT
u2.Firstname, IFNULL(tmp.count, 0) AS count
FROM users u2
LEFT JOIN (
SELECT u.id, u.Firstname, COUNT(1) as count
FROM ticket_assigned ta
LEFT JOIN ticket t ON t.id = ta.ticketID
LEFT JOIN users u ON u.id = ta.userID
GROUP BY u.id
) tmp ON tmp.id = u2.id
UNION
SELECT
'Unassigned', count(1) AS count
FROM ticket
WHERE id NOT IN (SELECT ticketid FROM ticket_assigned)
Here's the columns for table users.
+--------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+-----------------+------+-----+---------+----------------+
| uid | int(6) unsigned | YES | | NULL | |
| score | decimal(6,2) | YES | | NULL | |
| status | text | YES | | NULL | |
| date | datetime | YES | | NULL | |
| cid | int(7) unsigned | NO | PRI | NULL | auto_increment |
+--------+-----------------+------+-----+---------+----------------+
I want the difference between a user's most current score and earliest score. I tried:
select co1.uid, co1.score, co1.date from users as co1, (select uid, score, min(date) from users group by uid) as co2 where co2.uid = co1.uid;
This does not work. I also tried
select co1.uid, co1.score, co1.date from users as co1, (select uid, score, max(date) - min(date) from users group by uid) as co2 where co2.uid = co1.uid;
Result I get:http://pastebin.com/seR81WbE
Result I want:
uid max(score)-min(score)
1 40
2 -60
3 23
etc
I think the simplest solution is two joins:
select u.uid, umin.score, umax.score
from (select uid, min(date) as mind, max(date) as maxd
from users
group by uid
) u join
users umin
on u.uid = umin.uid and umin.date = u.mind join
users umax
on u.uid = umax.uid and umax.date = u.maxd;
I should note: if you know the scores are only increasing, you can do the much simpler:
select uid, min(score), max(score)
from users
group by uid;
My table structure looks like this:
tbl.users tbl.issues
+--------+-----------+ +---------+------------+-----------+
| userid | real_name | | issueid | assignedid | creatorid |
+--------+-----------+ +---------+------------+-----------+
| 1 | test_1 | | 1 | 1 | 1 |
| 2 | test_2 | | 2 | 1 | 2 |
+--------+-----------+ +---------+------------+-----------+
Basically I want to write a query that will end in a results table looking like this:
(results table)
+---------+------------+---------------+-----------+--------------+
| issueid | assignedid | assigned_name | creatorid | creator_name |
+---------+------------+---------------+-----------+--------------+
| 1 | 1 | test_1 | 1 | test_1 |
| 2 | 1 | test_1 | 2 | test_2 |
+---------+------------+---------------+-----------+--------------+
My SQL looks like this at the moment:
SELECT
`issues`.`issueid`,
`issues`.`creatorid`,
`issues`.`assignedid`,
`users`.`real_name`
FROM `issues`
JOIN `users`
ON ( `users`.`userid` = `issues`.`creatorid` )
OR (`users`.`userid` = `issues`.`assignedid`)
ORDER BY `issueid` ASC
LIMIT 0 , 30
This returns something like this:
(results table)
+---------+------------+-----------+-----------+
| issueid | assignedid | creatorid | real_name |
+---------+------------+-----------+-----------+
| 1 | 1 | 1 | test_1 |
| 2 | 1 | 2 | test_1 |
| 2 | 1 | 2 | test_2 |
+---------+------------+-----------+-----------+
Can anyone help me get to the desired results table?
SELECT
IssueID,
AssignedID,
CreatorID,
AssignedUser.real_name AS AssignedName,
CreatorUser.real_name AS CreatorName
FROM Issues
LEFT JOIN Users AS AssignedUser
ON Issues.AssignedID = AssignedUser.UserID
LEFT JOIN Users AS CreatorUser
ON Issues.CreatorID = CreatorUser.UserID
ORDER BY `issueid` ASC
LIMIT 0, 30
On the general knowledge front, our illustrious site founder wrote a very nice blog article on this subject which I find myself referring to over and over again.
Visual Explanation of SQL Joins
Use this:
SELECT
`issues`.`issueid`,
`issues`.`creatorid`,
`creator`.`real_name`,
`issues`.`assignedid`,
`assigned`.`real_name`
FROM `issues` i
INNER JOIN `users` creator ON ( `creator`.`userid` = `issues`.`creatorid` )
INNER JOIN `users` assigned ON (`assigned`.`userid` = `issues`.`assignedid`)
ORDER BY `issueid` ASC
LIMIT 0 , 30
SELECT DISTINCT (i.issueid, i.creatorid, i.assignedid, u.real_name)
FROM issues i, users u
WHERE u.userid = i.creatorid OR u.userid = assignedid
ORDER BY i.issueid ASC
LIMIT 0 , 30
Not sure if the parenthesis are needed or not.
Does this work?
SELECT
i.issueid,
i.assignedid,
u1.real_name as assigned_name,
i.creatorid,
u2.real_name as creator_name
FROM users u1
INNER JOIN issues i ON u1.userid = i.assignedid
INNER JOIN users u2 ON u2.userid = i.creatorid
ORDER BY i.issueid
SELECT
i.issueid,
i.assignedid,
a.real_name,
i.creatorid,
c.real_name
FROM
issues i
INNER JOIN users c
ON c.userid = i.creatorid
INNER JOIN users a
ON a.userid = i.assignedid
ORDER BY
i.issueid ASC