I have a query that brings back dates of inbound and outbound payments, and for each outbound payment i want to calculate the number of days since the previous inbound payment.
E.g.
SELECT
ps.clientid AS 'clientid',
psi.id AS 'scheduleid',
case when psi.status IN (4,5,6) then 'IB' when psi.status = 9 then 'OB' END AS 'type',
case when psi.status IN (4,5,6) then FROM_UNIXTIME(psit.date_cleared_on) when psi.status = 9 then FROM_UNIXTIME(psi.due_date) END AS 'date'
FROM payment_schedule_inbound psi
LEFT JOIN payment_schedule_inbound_transaction psit ON psit.payment_schedule_inbound_id = psi.id
INNER JOIN payment_schedule ps ON ps.id = psi.payment_schedule_id
WHERE psi.`status` IN (4,5,6,9)
AND ps.clientid IN (913244,913174) /*example id's, will usually run on multiple at same time or likely the full book*/
ORDER BY ps.clientid,(case when psi.status = 9 then psi.due_date else psit.date_cleared_on END)
(my CRM system stores dates as unixtime for some reason - not my fault, i didn't build the thing!)
What i want to do is, for each 'OB' event, display the datediff between that date and the previous 'IB' event for that clientid. In an ideal world i'd then like to have it only show the number of working days (excluding weekends), but that's whole other can of worms i can get to later!
I know the theory behind it would be to join the query back in on itself and get the max(date) of an IB event where the date is less than the date of the 'OB' event, but i'm just a layman and it's all got a bit much for me!
Any advice would be appreciated.
Thanks,
Ben.
Related
Frontend dev here trying to get a query working.
A bit of context, we have a site where users can keep track of time and our goal is to get them to 1000 hours of time tracked.
For this we have:
a pretty default users table, with a column track_outdoors (0 or 1, since they can enable or disable it) and a meta column (json field)
A timers table, where each row has a total_time column
What I want to do is select all users who:
Have tracking enabled (track_outdoors = 1),
Do not have MORE than 1000 hours total_time tracked,
Have not received the reminder email (check if meta column has 'ac_outdoors_outdoors_reminder_sent_at')
Where the last time they tracked time was more than 2 weeks ago
I've got the basic part done, which is retrieving the users who have enabled tracking, together with their total time tracked:
SELECT
u.id,
u.firstname,
u.track_outdoors,
SUM(t.total_time) AS total
FROM
users AS u
LEFT JOIN timers AS t ON u.id = t.user_id
WHERE
u.track_outdoors = 1
AND JSON_EXTRACT(u.meta, '$.ac_outdoors_outdoors_reminder_sent_at') IS NULL
GROUP BY
u.id
Now the two parts I'm having trouble with is using the sum to check if the total_time is smaller than 1000 and checking if the last time tracking was more than two weeks ago.
Apparently I cant use the SUM inside of the WHERE statement.
I tried searching on how to do a where last relationship is x time ago, but all I find is how to query records x days ago. (It needs to be the latest record x days ago, if that makes sense).
I think for the SUM in the WHERE statement I might need to use a subquery, but I'm not sure if that's true or how I'm supposed to do that. For the 2 weeks ago check, I understand how to check where the date is two weeks ago but not how to check that for the latest record for the user.
Any help would be much appreciated!
Thanks to the comment/answer provided by #Akina I was able to finish my query.
The result is:
SELECT
u.id,
u.firstname,
u.track_outdoors,
SUM(t.total_time) AS total
FROM
users AS u
LEFT JOIN timers AS t ON u.id = t.user_id
WHERE
u.track_outdoors = 1
AND JSON_EXTRACT(u.meta, '$.ac_outdoors_outdoors_reminder_sent_at') IS NULL
GROUP BY
u.id
HAVING total < 1000 AND MAX( t.created_at ) < CURRENT_DATE - INTERVAL 2 WEEK
So I needed to use HAVING for checking the total and MAX to check for the date of the tracker to be more than two weeks ago.
I am making a covid log db for easy contact tracing.
these are my tables
log_tbl (fk_UserID, fk_EstID, log_date, log_time)
est_tbl (EstID, EstName)
user_tbl (User_ID, Name, Address, MobileNumber)
I wanted to write a statement that shows when and where an individual (User_ID)
enters an Establishment (EstID),
SELECT l.*
FROM log_tbl l
WHERE (l.EstID, l.log_date) IN (SELECT l2.EstID, l2.log_date
FROM log_tbl l2
WHERE l2.User_ID = 'LIN78JFF5WG'
);
[Result of Query]1
this currently works,
but it still has to be filterd by +-2 hours based on the time the when User_ID was logged on log_tbl, so that it would narrow down result when first query would spit out 1000 logs. Because these Results will be Contacted, and to reduce Costs, it needs to be narrowed down to less than 50%.
So, table below should not include first 2 and last one because it doesn't fit with 1, the date, and 2 the time, in relation to the searched userLIN78JFF5WG
[Unfiltered Result]2
FROM log_tbl
WHERE User_ID = 'LIN78JFF5WG'
AND (BETWEEN subtime(log_tbl.log_time, '02:00:00') AND addtime(log_tbl.log_time, '02:00:00'
I know this is wrong, but I don't have any idea how to join the two queries
and result should include
EstID, Name, Address, MobileNumber, log_date, log_time sorted by Date
Imagine it like this,
There are 3 baskets full of tomatoes,
2 of the baskets have rotten tomatoes inside.
Do you throw away the whole basket full of tomatoes?
No.. you select the rotten tomato, and others close to it, and throw them away.
I need that for the DB, instead of Getting Result for the Whole Day,
I only need the People who are in close contact with The Target user.
is it possible to do this on mysql? I have to use mysql because of reasons..
Here I include the data sample fiddle:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=050b2103d3adf5828524f49066c12e74
MySQL supports window functions with the range window frame specification. I would suggest:
select l.*
from (select l.*,
sum(case when fk_UserID = 'LIN78JFF5WG' then 1 else 0 end) over
(partition by log_date
order by log_time
range between interval 2 hour preceding and interval 2 hour following
) as cnt_user
from log_tbl l
) l
where cnt_user > 0;
Here is a db<>fiddle.
You can then annotate the results would other columns from other tables to get your final result.
This should be much faster than alternative methods.
Note, however, that you have a flaw in this logic, because it is not checking four hours between 0:00-2:00 a.m. and 22:00-0:00. You can store the date/time in a single column to make it easier to get a more accurate list.
I am not fully understand your requirements.
but I write sample sql so that we can make it clear
select *,(select UNIX_TIMESTAMP(CONCAT(log_date," ",log_time)) as ts from log_tbl where fk_UserID='LIN78JFF5WG') as target_time
from
log_tbl as l
-- simple join query.to get intend information
left join user_tbl as u on (u.User_id=l.fk_UserID)
left join est_tbl as e on (l.fk_EstID=e.EstID)
-- mysql datediff only return day as unit.so we convert to timestamp to do the diff
where UNIX_TIMESTAMP(CONCAT(l.log_date," ",l.log_time)) - target_time between 60*60*2 and 60*60*2
-- solution two
-- but I suggest you divide it into two sql like this.
select UNIX_TIMESTAMP(CONCAT(log_date," ",log_time)) as ts from log_tbl where fk_UserID='LIN78JFF5WG';
-- we get the user log timestamp.and use it in next query
select *
from
log_tbl as l
-- simple join query.to get intend information
left join user_tbl as u on (u.User_id=l.fk_UserID)
left join est_tbl as e on (l.fk_EstID=e.EstID)
-- mysql datediff only return day as unit.so we convert to timestamp to do the diff
where UNIX_TIMESTAMP(CONCAT(l.log_date," ",l.log_time)) - [target_time(passed by code)] between 60*60*2 and 60*60*2
I am creating a pipeline report so we can count opportunities that have been added each week.
My query is:
SELECT
sum(IF(o.date_entered > date_sub(now(), INTERVAL 1 WEEK), 1,0))
Pretty simple and works. The problem is, sales now also wants to count any opportunity as new that has been moved out of a loss status. So, I left-joined to an audit table to include this use case. But now, it counts every instance of the audit table for a given account where the field = sales_stage and the before_value is a loss status. So, and not that this would happen that often if ever, but if an opportunity moves from loss to lead, back to loss, and back to lead, it will count it as 2 new opportunities. I just want to get the latest instance of field=sales_stage and before_value is a loss status, and count that one time.
I want something like a sub-query in the left join, and I keep trying to use MAX, but nothing's working. Here's part of my join:
INNER JOIN opportunities o ON ao.opportunity_id=o.id
LEFT JOIN opportunities_audit oa ON o.id=oa.parent_id
AND after_value_string = 'Loss'
AND date_created > date_sub(now(), INTERVAL 1 WEEK)
Does anybody know the solution to this type of problem? Thank you in advance for any advice!
I am trying to create SQL query that calculates the number of hours between two dates (2015-01-01 12:12:12). The complexity of this query is on sum up hours only between Monday to Friday from 8am to 6pm.
I need it to calculate how long it took for a issue logged in our Mantis BT system to be resolved. Our clients can log issues outside our working hours, but I only want to calculate hours based on our working hours.
We calculate the difference between the date/time when the issue was logged (submitted_date) to when the issue was resolved (maximum updated_date and "resolved" status). The difficulty is that an issue can be resolved, and then reopened in the future. By using the maximum updated_date, my script is including the days that the issue was in "resolved" status, which is not correct. It should only include days while issue is open.
SELECT
proj.name,
bugs.id,
summary,
users2.username AS Assigned_to,
CASE bugs.status
WHEN '80' THEN 'Resolved'
WHEN '90' THEN 'Closed'
END AS Status,
bugs.date_submitted AS date_submitted,
MAX(date_modified) AS last_date_modified
FROM
mantis_bug_table bugs
LEFT JOIN mantis_bugnote_table notes ON notes.bug_id = bugs.id
INNER JOIN mantis_bug_history_table hist ON bugs.id = hist.bug_id
LEFT JOIN mantis_user_table users2 ON users2.id = bugs.handler_id
INNER JOIN mantis_project_table proj ON proj.id = bugs.project_id
WHERE
bugs.project_id = 102
AND hist.field_name = 'status'
AND bugs.status IN (80,90)
AND date_modified >= '2015-01-01'
GROUP BY
summary;
This tutorial is written for SQL, but can be replicated easily in MySQL.
You just need to define your working hours, then reference these to calculate the time.
What is the best way to think about the Group By function in MySQL?
I am writing a MySQL query to pull data through an ODBC connection in a pivot table in Excel so that users can easily access the data.
For example, I have:
Select
statistic_date,
week(statistic_date,4),
year(statistic_date),
Emp_ID,
count(distict Emp_ID),
Site
Cost_Center
I'm trying to count the number of unique employees we have by site by week. The problem I'm running into is around year end, the calendar years don't always match up so it is important to have them by date so that I can manually filter down to the correct dates using a pivot table (2013/2014 had a week were we had to add week 53 + week 1).
I'm experimenting by using different group by statements but I'm not sure how the order matters and what changes when I switch them around.
i.e.
Group by week(statistic_date,4), Site, Cost_Center, Emp_ID
vs
Group by Site, Cost_Center, week(statistic_date,4), Emp_ID
Other things to note:
-Employees can work any number of days. Some are working 4 x 10's, others 5 x 8's with possibly a 6th day if they sign up for OT. If I sum the counts by week, I get anywhere between 3-7 per Emp_ID. I'm hoping to get 1 for the week.
-There are different pay code per employee so the distinct count helps when we are looking by day (VTO = Voluntary Time Off, OT = Over Time, LOA = Leave of Absence, etc). The distinct count will show me 1, where often times I will have 2-3 for the same emp in the same day (hits 40 hours and starts accruing OT then takes VTO or uses personal time in the same day).
I'm starting with a query I wrote to understand our paid hours by week. I'm trying to adapt it for this application. Actual code is below:
SELECT
dkh.STATISTIC_DATE AS 'Date'
,week(dkh.STATISTIC_DATE,4) as 'Week'
,month(dkh.STATISTIC_DATE) as 'Month'
,year(dkh.STATISTIC_DATE) as 'Year'
,dkh.SITE AS 'Site ID Short'
,aep.LOC_DESCR as 'Site Name'
,dkh.EMPLOYEE_ID AS 'Employee ID'
,count(distinct dkh.EMPLOYEE_ID) AS 'Distinct Employee ID'
,aep.NAME AS 'Employee Name'
,aep.BUSINESS_TITLE AS 'Business_Ttile'
,aep.SPRVSR_NAME AS 'Manager'
,SUBSTR(aep.DEPTID,1,4) AS 'Cost_Center'
,dkh.PAY_CODE
,dkh.PAY_CODE_SHORT
,dkh.HOURS
FROM metrics.DAT_KRONOS_HOURS dkh
JOIN metrics.EMPLOYEES_PUBLIC aep
ON aep.SNAPSHOT_DATE = SUBDATE(dkh.STATISTIC_DATE, DAYOFWEEK(dkh.STATISTIC_DATE) + 1)
AND aep.EMPLID = dkh.EMPLOYEE_ID
WHERE dkh.STATISTIC_DATE BETWEEN adddate(now(), interval -1 year) AND DATE(now())
group by dkh.SITE, SUBSTR(aep.DEPTID,1,4), week(dkh.STATISTIC_DATE,4), dkh.STATISTIC_DATE, dkh.EMPLOYEE_ID
The order you use in group by doesn't matter. Each unique combination of the values gets a group of its own. Selecting columns you don't group by gives you somewhat arbitrary results; you'd probably want to use some aggregation function on them, such as SUM to get the group total.
Grouping by values you derive from other values that you already use in group by, like below, isn't very useful.
week(dkh.STATISTIC_DATE,4), dkh.STATISTIC_DATE
If two rows have different weeks, they'll also have different dates, right?