Creating a table of workers with first shift last week - mysql

I am trying to extract a list of workers who had their first shift during last week.
Right now, my SQL works if I put a specific worker with a first shift in last week in my where clause (WHERE c.temp_nr ='XXXX'), but when I remove that clause, it just timeout
SELECT MIN(p.start_date), CONCAT(c.first_name, ' ', c.last_name) AS 'c.fullname', c.temp_nr
FROM contactstable c
LEFT JOIN projectlines p on c.temp_nr = p.candidate_number
WHERE c.temp_nr ='XXXX' and c.contact_type = 'Candidate'
HAVING MIN(p.start_date) between DATE_SUB( CURDATE( ) , INTERVAL (dayofweek(CURDATE())+6)
DAY )
AND DATE_SUB( CURDATE( ) , INTERVAL (dayofweek(CURDATE()))
DAY )

Use GROUP BY c.temp_nr
SELECT MIN(p.start_date), CONCAT(c.first_name, ' ', c.last_name) AS 'c.fullname', c.temp_nr
FROM contactstable c
LEFT JOIN projectlines p on c.temp_nr = p.candidate_number
WHERE c.contact_type = 'Candidate'
GROUP BY c.temp_nr
HAVING
MIN(p.start_date)
between
DATE_SUB(CURDATE(), INTERVAL (dayofweek(CURDATE())+6) DAY)
AND DATE_SUB(CURDATE() , INTERVAL (dayofweek(CURDATE())) DAY)

Related

Creating SQL indexes on old tables

I have a query that shows all workers with their first shift in the last week, it's gathered from two tables and looks like this:
SELECT MIN(p.start_date),
CONCAT(c.first_name, ' ', c.last_name) AS 'c.fullname',
c.temp_nr
FROM contactstable c
LEFT JOIN projectlines p on c.temp_nr = p.candidate_number
WHERE c.contact_type = 'Candidate'
GROUP BY c.temp_nr
HAVING
MIN(p.start_date)
between
DATE_SUB(CURDATE(), INTERVAL (dayofweek(CURDATE())+6) DAY)
AND DATE_SUB(CURDATE() , INTERVAL (dayofweek(CURDATE())) DAY)
My problem is the query times out, so as I understand it I need to add indexes, but after having searched around on multiple sites, I still have no clue how to go about that.
I am using Workbench 8.0
I would suggest that you write the query like this:
SELECT CONCAT(c.first_name, ' ', c.last_name) AS fullname, c.temp_nr,
(SELECT MIN(p.start_date)
FROM projectlines p
WHERE c.temp_nr = p.candidate_number
) as min_start_date
FROM contactstable c
WHERE c.contact_type = 'Candidate'
HAVING min_start_date BETWEEN DATE_SUB(CURDATE(), INTERVAL (dayofweek(CURDATE())+6) DAY) AND
DATE_SUB(CURDATE() , INTERVAL (dayofweek(CURDATE())) DAY);
Then for this, you want indexes on:
contactstable(contact_type, temp_nr, first_name, last_name)
projectlines(candidate_number, start_date).

How can I display a value of NULL where the join did not find any existing values?

How can I display a value of NULL where the join did not find any existing values?
SELECT u.display_name Associate
, ROUND(SUM(CASE WHEN w.startdate BETWEEN NOW() AND NOW() + INTERVAL 30 DAY THEN w.timeworked END/3600)) '30 Days'
, ROUND(SUM(CASE WHEN w.startdate BETWEEN NOW() AND NOW() + INTERVAL 60 DAY THEN w.timeworked END/3600)) '60 Days'
, ROUND(SUM(CASE WHEN w.startdate BETWEEN NOW() AND NOW() + INTERVAL 90 DAY THEN w.timeworked END/3600)) '90 Days'
FROM worklog w
JOIN cwd_user u
ON u.user_name = w.author
JOIN cwd_membership m
ON m.directory_id = u.directory_id
AND m.lower_child_name = u.lower_user_name
WHERE m.membership_type = 'GROUP_USER'
AND m.lower_parent_name = 'atl_servicedesk_it_agents'
AND w.startdate BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 90 DAY)
GROUP
BY u. display_name
ORDER
BY u.last_name;
So on my join u.user_name = w.author I want to show all values where there is a u.user_name, even if there is not a w.author. The display_name should still show up, but the values for 40, 60, and 90 days would be NULL. Ideally I want to change the NULL to be 0 instead. Users don't appear in the worklog table unless they have logged work, so right now it only shows two rows for the two people who have. I still want to show everyone that exists in m.lower_parent_name = 'atl_servicedesk_it_agents' to know that they have not logged anything.
Anyone have any ideas?
You can use LEFT JOIN, but you need to be careful about all the joins and the WHERE conditions:
SELECT u.display_name as Associate,
ROUND(SUM(CASE WHEN w.startdate BETWEEN NOW() AND NOW() + INTERVAL 30 DAY THEN w.timeworked END/3600)) as `30 Days`,
ROUND(SUM(CASE WHEN w.startdate BETWEEN NOW() AND NOW() + INTERVAL 60 DAY THEN w.timeworked END/3600)) as `60 Days`,
ROUND(SUM(CASE WHEN w.startdate BETWEEN NOW() AND NOW() + INTERVAL 90 DAY THEN w.timeworked END/3600)) as `90 Days`
FROM cwd_user u JOIN -- Not sure if this should be LEFT JOIN or not
cwd_membership m
ON m.directory_id = u.directory_id AND
m.lower_child_name = u.lower_user_name AND
m.membership_type = 'GROUP_USER' AND
m.lower_parent_name = 'atl_servicedesk_it_agents' LEFT JOIN
worklog w
ON u.user_name = w.author AND
w.startdate BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 90 DAY)
GROUP BY u. display_name
ORDER BY u.last_name;
I'm not sure if the join to m should be an inner join or left join. It depends on whether you want filtering based on that table as well.

How to use if condition in where clause in mysql

I have a query that fetch records from two table. I want if condition in where clause with and operator.
Here is my code :
SELECT sfo.customer_id, sfo.increment_id FROM sales_flat_order sfo
INNER JOIN marketplace_partnertype mptype ON sfo.customer_id = mptype.partner_id
WHERE (mptype.type = 'free' AND sfo.status='complete') AND IF #mptype.type=='free' THEN ((DATE( updated_at ) = DATE_SUB( CURDATE( ) , INTERVAL 5 DAY ))ELSE (DATE( updated_at ) = DATE_SUB( CURDATE( ) , INTERVAL 3 DAY );
It shows syntax error near IF #mptype.type=='free'.
It should be appreciable if some help to solve this issues.
Syntax of If condition is:
IF(<cond-expr>, <then-expr>, <else-expr>)
If we change the condition it will be like :-
AND IF (#mptype.type=='free', (DATE( updated_at ) = DATE_SUB( CURDATE( ) , INTERVAL 5 DAY )) ,(DATE( updated_at ) = DATE_SUB( CURDATE( ) , INTERVAL 3 DAY ))
Query will look like:
SELECT sfo.customer_id, sfo.increment_id FROM sales_flat_order sfo
INNER JOIN marketplace_partnertype mptype ON sfo.customer_id = mptype.partner_id
WHERE (mptype.type = 'free' AND sfo.status='complete')AND IF (#mptype.type=='free', (DATE( updated_at ) = DATE_SUB( CURDATE( ) , INTERVAL 5 DAY )) ,(DATE( updated_at ) = DATE_SUB( CURDATE( ) , INTERVAL 3 DAY )))
The syntax is not correct, you can use case-when condition in the where condition as
SELECT sfo.customer_id, sfo.increment_id FROM sales_flat_order sfo
INNER JOIN marketplace_partnertype mptype ON sfo.customer_id = mptype.partner_id
WHERE (mptype.type = 'free' AND sfo.status='complete')
and
case
when #mptype.type = 'free' then DATE( updated_at ) = DATE_SUB( CURDATE( ) , INTERVAL 5 DAY )
else DATE( updated_at ) = DATE_SUB( CURDATE( ) , INTERVAL 3 DAY )
end
There's no need to use IF or CASE WHEN:
SELECT
sfo.customer_id,
sfo.increment_id
FROM
sales_flat_order sfo INNER JOIN marketplace_partnertype mptype
ON sfo.customer_id = mptype.partner_id
WHERE
(mptype.type = 'free' AND DATE(updated_at) = DATE_SUB(CURDATE(), INTERVAL 5 DAY)
OR (mptype.type = 'complete' AND DATE(updated_at) = DATE_SUB(CURDATE(), INTERVAL 3 DAY);
but if you want to use case when you could do it this way:
WHERE
mptype.type IN ('free', 'complete')
AND DATE(updated_at) = DATE_SUB(CURDATE(), INTERVAL CASE WHEN mptype.type='free' THEN 3 ELSE 5 END DAY)

MySQL get single column value of last record

I have a query which looks like:
SELECT max(sp.id) as max_id, p.name as player, max(update_time) as last_seen, min(login_time) as first_seen, s.name as last_server,
sum(sp.play_time) as ontime_total,
sum(case when login_time > NOW() - INTERVAL 1 DAY then sp.play_time end) as ontime_day,
sum(case when login_time > NOW() - INTERVAL 7 DAY then sp.play_time end) as ontime_week,
sum(case when login_time > NOW() - INTERVAL 1 MONTH then sp.play_time end) as ontime_month
FROM session_player sp
INNER JOIN players p ON p.id=sp.player_id
INNER JOIN server s ON s.id=sp.server_id
WHERE p.name = ?
The result:
The issue:
Node22 isn't the last server. I am struggling on finding a way to get the server of the last record within this query. How would you solve this issue, if possible without running a second query.
(This query already takes 2-3s seconds depending on the user, if possible I would like to avoid any overhead and in case you see performance optimization possibilities I would appreciate anything.)
This would work, but its performance you can guess (4-5s):
SELECT
MAX( sp.id ) AS max_id, p.name AS player, MAX( update_time ) AS last_seen, MIN( login_time ) AS first_seen,
SUM( sp.play_time ) AS ontime_total,
SUM( CASE WHEN login_time > NOW( ) - INTERVAL 1 DAY THEN sp.play_time END ) AS ontime_day,
SUM( CASE WHEN login_time > NOW( ) - INTERVAL 7 DAY THEN sp.play_time END ) AS ontime_week,
SUM( CASE WHEN login_time > NOW( ) - INTERVAL 1 MONTH THEN sp.play_time END ) AS ontime_month,
(SELECT s.name
FROM session_player sp
JOIN players p ON p.id=sp.player_id
JOIN server s ON s.id=sp.server_id
WHERE p.name = ?
ORDER BY sp.id DESC
LIMIT 1
) as last_server
FROM session_player sp
INNER JOIN players p ON p.id = sp.player_id
INNER JOIN server s ON s.id = sp.server_id
WHERE p.name = ?
After nearly 3 hours of experimenting I got it and even 260 times faster as before:
SELECT MAX(pd.id) AS max_id, pd.name AS player, MAX( pd.update_time ) AS last_seen, MIN( pd.login_time ) AS first_seen,
SUM( pd.play_time ) AS ontime_total,
SUM( CASE WHEN pd.login_time > NOW( ) - INTERVAL 1 DAY THEN pd.play_time END ) AS ontime_day,
SUM( CASE WHEN pd.login_time > NOW( ) - INTERVAL 7 DAY THEN pd.play_time END ) AS ontime_week,
SUM( CASE WHEN pd.login_time > NOW( ) - INTERVAL 1 MONTH THEN pd.play_time END ) AS ontime_month,
(SELECT s.name
FROM session_player sp
INNER JOIN server s ON s.id=sp.server_id
WHERE max(pd.id)=sp.id
) as last_server
FROM (
SELECT sp.id AS id, sp.server_id as server_id, p.name AS name, sp.login_time AS login_time, sp.update_time AS update_time, sp.play_time AS play_time
FROM session_player sp
INNER JOIN players p ON p.id=sp.player_id
WHERE p.name = ?
) as pd
Try this:
SELECT sp.id as max_id, p.name as player, max(update_time) as last_seen,
min(login_time) as first_seen, s.name as last_server,
sum(sp.play_time) as ontime_total,
sum(case when login_time > NOW() - INTERVAL 1 DAY then sp.play_time end) as ontime_day,
sum(case when login_time > NOW() - INTERVAL 7 DAY then sp.play_time end) as ontime_week,
sum(case when login_time > NOW() - INTERVAL 1 MONTH then sp.play_time end) as ontime_month
FROM session_player sp
INNER JOIN players p ON p.id=sp.player_id
INNER JOIN server s ON s.id=sp.server_id
WHERE p.name = ?
group by sp.player_id
order by sp.id desc limit 1

Setting user defined variables with joins

I have a query like below:
Result gives #sold_count:=SUM(I.quantity) = 10, but #sold_count = 0,
so calculations are all 0.
What should be wrong here?
SET #sold_count :=0;
SELECT
#sold_count:=SUM(I.quantity),
#sold_count,I.from_widget,COUNT(from_widget) as order_count,
(#sold_count * buy_price) as ciro,
(#sold_count * list_price) as liste_ciro,
(#sold_count * widget_price) as vitrin_ciro,
P.*
FROM
tbl_products P
LEFT JOIN tbl_order_items I on I.product_id = P.id
WHERE
P.publish_date BETWEEN DATE_SUB( CURDATE( ) ,INTERVAL 3 MONTH ) AND DATE_SUB( CURDATE( ) ,INTERVAL 0 MONTH )
GROUP BY I.from_widget,I.product_id
ORDER BY publish_date DESC
Don't use variables. Just:
SELECT
SUM(I.quantity),
I.from_widget,
COUNT(from_widget) AS order_count,
SUM(I.quantity) * buy_price AS ciro,
SUM(I.quantity) * list_price AS liste_ciro,
SUM(I.quantity) * widget_price AS vitrin_ciro,
P.*
FROM
tbl_products P
LEFT JOIN tbl_order_items I
ON I.product_id = P.id
WHERE
P.publish_date BETWEEN DATE_SUB( CURDATE( ) , INTERVAL 3 MONTH )
AND DATE_SUB( CURDATE( ) , INTERVAL 0 MONTH )
GROUP BY I.from_widget,
I.product_id
ORDER BY publish_date DESC ;
You could also make the query a nested one, if you don't like using SUM(quantity) many times:
SELECT
sum_quantity * buy_price AS ciro,
sum_quantity * list_price AS liste_ciro,
sum_quantity * widget_price AS vitrin_ciro,
tmp.*
FROM
( SELECT
SUM(I.quantity) AS sum_quantity,
I.from_widget,
COUNT(from_widget) AS order_count,
buy_price,
list_price,
widget_price,
P.*
FROM
tbl_products P
LEFT JOIN tbl_order_items I
ON I.product_id = P.id
WHERE
P.publish_date BETWEEN DATE_SUB( CURDATE( ) , INTERVAL 3 MONTH )
AND DATE_SUB( CURDATE( ) , INTERVAL 0 MONTH )
GROUP BY I.from_widget,
I.product_id
) AS tmp
ORDER BY publish_date DESC ;