Running counter for tickets in SQL - mysql

I am attempting to have a running counter as the first column to easily see how many tickets are being assigned to an agent. The number emulator works but instead of raising the ticket number for the agent it counts the ticket date. My current code is:
Set #row_num = 0;
SELECT #row_num := #row_num + 1 as row_number
, s.firstname
, s.lastname
, t.number
, ts.name AS status
, o.name, t.created
FROM ost_ticket t
JOIN ost_ticket_status ts ON t.status_id = ts.id
JOIN ost_staff s ON t.staff_id = s.staff_id
JOIN ost_user u ON t.user_id = u.id
JOIN ost_organization o ON u.org_id = o.id
JOIN (SELECT #row_num:= 0) N
WHERE s.staff_id IN (27,35,43,45)
AND t.created >= CURDATE() - INTERVAL 7 DAY
ORDER BY s.lastname
How best could I change it where it would have 1, 2, 3, for the agent and then reset for a new agent?

You need a diferent variable to keep track of when agent change.
SET #row_num := 0;
SET #agent :='';
SELECT
#row_number := IIF(#agent = s.firstname + s.lastname,
#row_number + 1, 1
) AS row_number,
#agent := s.firstname + s.lastname AS agent,
s.firstname,
s.lastname,
t.number,
ts.name AS status,
o.name,
t.created
FROM ost_ticket t
JOIN ost_ticket_status ts ON t.status_id = ts.id
JOIN ost_staff s ON t.staff_id = s.staff_id
JOIN ost_user u ON t.user_id = u.id
JOIN ost_organization o ON u.org_id = o.id
WHERE s.staff_id IN (27,35,43,45)
AND t.created >= CURDATE() - INTERVAL 7 DAY
ORDER BY s.lastname

Related

MySQL query runs very slow

I have a MySQL (v5.7.26) query that runs forever. Here is the query:
SELECT
ur.user_id AS user_id,
sum(r.duration) AS total_time,
count(user_id) AS number_of_workouts
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE
ur.status = 1
AND NOT ur.action_date IS NULL
AND ur.user_id IN (
SELECT user_id
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY
)
AND r.type = 'WORKOUT'
GROUP BY ur.user_id;
I have played a bit with it, by trying to understand where is the problem. For the testing purposes, I tried breaking in two. So:
SELECT user_id
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY;
That returns (very quickly) list of user user_id's.
When I plug the returned result in to the first part of the query, like this:
SELECT
ur.user_id AS user_id,
sum(r.duration) AS total_time,
count(user_id) AS number_of_workouts
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE
ur.status = 1
AND NOT ur.action_date IS NULL
AND ur.user_id IN (1,1,1,4,4,5,6,7,7,7);
AND r.type = 'WORKOUT'
GROUP BY ur.user_id
It runs very fast. My assumption is the IN (Subquery) is the bottleneck.
I was thinking to extract the subquery and get the user_ids, and then used it as a variable, but I am not sure is it the good approach, and additionally I am having issues with it. this is my attempt:
-- first statement
SET #v1 = (SELECT user_id
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY)
-- second statement
SELECT
ur.user_id AS user_id,
sum(r.duration) AS total_time,
count(user_id) AS prefixes
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE
ur.status = 1
AND NOT ur.action_date IS NULL
AND ur.user_id IN (#v1);
AND r.type = 'WORKOUT'
GROUP BY ur.user_id
Problem here is that the first statement returns an error:
Subquery returns more than 1 row.
Expected result are user_id's, that can be duplicates. And I need those duplicated for the count.
How can I fix this?
Try EXISTS instead of IN
...
AND EXISTS (SELECT *
FROM user_resource ur2
WHERE ur2.user_id = ur.user_id
AND ur2.action_date >= now() - INTERVAL 2 DAY)
...
and indices on user_resource (user_id, action_date), user_resource (status, action_date, user_id) and/or user_resource (type).
You could try:
-- first statement
SET #v1 = (SELECT GROUP_CONCAT(user_id)
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY)
-- second statement
SELECT
ur.user_id AS user_id,
sum(r.duration) AS total_time,
count(user_id) AS prefixes
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE ur.status = 1 AND NOT ur.action_date IS NULL AND FIND_IN_SET(ur.user_id,#v1)
AND r.type = 'WORKOUT'
GROUP BY ur.user_id
Additional join will be faster then sub-query:
SELECT
ur.user_id AS user_id,
sum(r.duration) AS total_time,
count(user_id) AS number_of_workouts
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
INNER JOIN (
SELECT user_id
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY
) t ON t.user_id = ur.user_id
WHERE
ur.status = 1
AND NOT ur.action_date IS NULL
AND r.type = 'WORKOUT'
GROUP BY ur.user_id;

Return sub-set of records that don't exists in other table

I have this SQL query that returns me the whole set:
SELECT s.id, s.status, s.created_at
FROM scores s
WHERE s.account_id IN (
SELECT a.id
FROM accounts a
WHERE a.status = 'ACTIVE' AND a.created_at >= DATE(NOW()) - 3 AND a.tool = 'GLR'
) ORDER BY s.created_at ASC;
...but now I want a sub-set of that: all the records in account that doesn't have any record in score.
I've tried these, but no luck so far O:)
--- Using LEFT JOIN
SELECT s.account_id, s.status, s.created_at
FROM scores s
LEFT JOIN accounts a ON s.account_id = a.id
AND a.status = 'ACTIVE'
AND a.created_at >= DATE(NOW()) - 3
AND a.tool = 'GLR'
WHERE s.account_id IS NULL ORDER BY s.created_at ASC;
--- Using EXISTS / NOT EXISTS
SELECT s.account_id, s.status, s.created_at
FROM scores s
WHERE NOT EXISTS (
SELECT 1 FROM accounts a
WHERE s.account_id = a.id
AND a.status = 'ACTIVE'
AND a.created_at >= DATE(NOW()) - 3
AND a.tool = 'GLR'
) ORDER BY s.created_at ASC;
SELECT a.* FROM accounts a LEFT JOIN scores s ON s.account_id = a.id
WHERE a.status = 'ACTIVE' AND a.created_at >= DATE(NOW()) - 3 AND a.tool = 'GLR'
AND s.account_id IS NULL

mysql query taking long time to respond

SELECT t.id
, t.department
, t.owner
, t.client
, u.username as owner_name
, c.name as catagery
, d.dept_name as deptname
, t.periority
, t.status
, t.estimate
, cl.takeaway_name
from tbl_task t
JOIN tbl_user u
ON u.id = t.owner
JOIN tbl_task_catagery c
ON c.id = t.catagery
JOIN tbl_department d
ON d.id = t.department
JOIN tbl_clients cl
ON cl.id = t.client
and t.status = 0
and (t.id in (select task_id
from tbl_task_note tn
where tn.user_id = '69'
and tn.id in (select max(id)
from tbl_task_note tt
where tt.task_id = tn.task_id
)
)
)
order by t.id
Note : The above query is used for check users hold tasks. tbl_task_note table is used for check task notes for separate users task.
With this query you will get the task that have the last task_note registered, including the user, departament, client, and some other.
If it is what you need you can just do this.
select
t.id,
t.department,
t.owner,
t.client,
u.username as owner_name,
c.name as catagery,
d.dept_name as ptname,
t.periority,
t.status,
t.estimate,
cl.takeaway_name
from tbl_task t
INNER JOIN tbl_user u ON u.id=t.owner
INNER JOIN tbl_task_catagery c ON c.id=t.catagery
INNER JOIN tbl_department d ON d.id=t.department
INNER JOIN tbl_clients cl ON cl.id=t.client and t.status=0
INNER JOIN (select * from tbl_task_note where id =
(select max(id) from tbl_task_note)
)tb on tb.task_id = t.id
order by t.id
That way you can improve your query.
You shoud also ensure that your keys compared are foreign keys to get faster consults.

Mysql - dynamic UNION with multiple recieve ids

how to convert this multiple UNION to simple query?
number of artist_id is dynamic.
45,122,95
or
100,20
or
89,9449
or
22,495,700,98
....
(SELECT b.`id`, b.`id`, b.`date`, b.`artist`, b.`title`, b.`photo`
FROM `tags_mp3s` a
INNER JOIN `mp3s` b ON b.`id` = a.`mp3_id` AND a.`artist_id` = 45
ORDER BY b.`date` DESC
LIMIT 5)
UNION ALL
(SELECT b.`id`, b.`id`, b.`date`, b.`artist`, b.`title`, b.`photo`
FROM `tags_mp3s` a
INNER JOIN `mp3s` b ON b.`id` = a.`mp3_id` AND a.`artist_id` = 122
ORDER BY b.`date` DESC
LIMIT 5)
UNION ALL
(SELECT b.`id`, b.`id`, b.`date`, b.`artist`, b.`title`, b.`photo`
FROM `tags_mp3s` a
INNER JOIN `mp3s` b ON b.`id` = a.`mp3_id` AND a.`artist_id` = 95
ORDER BY b.`date` DESC
LIMIT 5)
.....
thanks
In MySQL, you can use variables:
SELECT ta.*
FROM (SELECT m.*,
(#rn := if(#a = t.artist_id, #rn + 1,
if(#a := t.artist_id, 1, 1)
)
) as rn
FROM tags_mp3s t INNER JOIN
mp3s m
ON m.id = t.mp3_id CROSS JOIN
(SELECT #rn := 0, #a := -1) params
WHERE t.artist_id IN (. . . )
ORDER BY t.artist_id, m.date DESC
) ta
WHERE rn <= 5;

selecting only distinct row based on highest timestamp on a join

I perform a join on these two tables in mysql
Challenges table
challenge_ID(int) |to_user(int)|from_user(int)|timestamp
Users table
iduser(int)|email(string)
My join query is this :
Select distinct u.email,c.challenge_id,c.status,c.timestamp from
test.challenges c join test.users u
on
c.to_user=u.iduser
where
c.from_user=9 and (c.status='open' || c.status='rejected')
Order by
c.timestamp DESC
The result what I get from this query is
email |challenge_id| status |timestamp (Descending)
Dan21#rab.edu 5 open 2015-12-09 21:20:26
tommy52#gump.com 4 open 2015-12-09 21:10:22
Dan21#rab.edu 1 rejected 2015-12-08 12:27:00
Notice how Dan21#rab.edu is repeated twice, I want it to display only once and the one displayed should have the latest timestamp i.e.
email |challenge_id| status |timestamp (Descending)
Dan21#rab.edu 5 open 2015-12-09 21:20:26
tommy52#gump.com 4 open 2015-12-09 21:10:22
You should write your query as:
Select u.email, c.challenge_id, c.status, c.timestamp
from test.challenges c join
test.users u
on c.to_user = u.iduser
where c.from_user = 9 and c.status in ('open', 'rejected')
Order by c.timestamp DESC;
Don't use select distinct unless necessary. Then you can do what you want in various ways. Because you have a join and other conditions, I think variables might be the simplest way:
select cu.*
from (Select u.email, c.challenge_id, c.status, c.timestamp,
(#rn := if(#e = u.email, #rn + 1,
if(#e := u.email, 1, 1)
)
) as rn
from test.challenges c join
test.users u
on c.to_user = u.iduser cross join
(select #e := '', #rn := 0) params
where c.from_user = 9 and c.status in ('open', 'rejected')
order by u.email, c.timestamp DESC
) cu
where rn = 1;
EDIT:
I thought the above worked with join and the variables in a single query. But, sometimes MySQL gets confused and you need to use a subquery with variables:
select cu.*
from (select cu.*,
(#rn := if(#e = u.email, #rn + 1,
if(#e := u.email, 1, 1)
)
) as rn
from (Select u.email, c.challenge_id, c.status, c.timestamp,
from test.challenges c join
test.users u
on c.to_user = u.iduser
where c.from_user = 9 and c.status in ('open', 'rejected')
order by u.email, c.timestamp DESC
) cu cross join
(select #e := '', #rn := 0) params
) cu
where rn = 1;
I figure out a query for this which works , not sure if its foolproof or most efficient way to do this
Here it is tho
Select distinct u.email,c.challenge_id,c.status,c.timestamp
from
(select ch.challenge_id,ch.status,ch.from_user,ch.to_user,timestamp,
(select Max(timestamp) from challenges
where to_user=ch.to_user
and from_user=9
) as latest
from test.challenges ch
)c
join test.users u
on
c.to_user=u.iduser
and
c.latest=c.timestamp
where (c.status='open' || c.status='rejected')