Google sheet Query last 7 rows of a table - google-sheets-query

I have a large table (sample sheet included here) of data where I use a query to total and average columns C and D based on the day of the week, but I'd also like to include into this query a total for the last 7 rows (The last week) grouped by column A (day of the week).
This is the query I'm using:
=Query(A2:D,"SELECT A, sum(C), sum(D), Avg(C), Avg(D)
WHERE A <> ''
GROUP BY A
ORDER BY A
LABEL A 'Day',sum(C) 'Total Checked In',sum(D) 'Total Checked Out',Avg(C) 'Average Checked In',Avg(D) 'Average Checked Out'")
Can anyone help me out to add a final column to show just the last 7 rows?
Here is the sample sheet: https://docs.google.com/spreadsheets/d/18PMhuBrZLscYufS8eonViZfbz2kstyyWFzmt8cZlRBE/edit?usp=sharing
Many thanks.

I figured this out in case anyone is interested:
=QUERY(A2:D30,"Select sum(C), sum(D) Where B >= date '" & text(P1,"yyyy-mm-dd") & "' group by A Label sum(C) 'This Week Checked In', sum(D) 'This Week Checked Out'")
I used this formula to get the last cell (date) =INDEX(B3:B30,COUNTA(B3:B30),1)
Then deducted a week in P1 which is used in the query.

Related

How to query available item leases based on a date range in MySQL?

We have a business that rents out international phone numbers to customers when traveling. When a customer makes an order We want to display to the customer the available phone numbers for his booking dates based on his start_date and end_date and numbers which is not occupied yet.
Since these phone numbers are rented out, I need to select from the table ONLY those numbers that are not rented out yet for dates that would interfere with the current customers dates.
I also don't want to rent out any phone number prior to 7 days after its end date. Meaning, If a customer booked a phone number for 1-1-2020 through 1-20-2020, I don't want this phone number to be booked by another customer before 1-27-2020. I want the phone number to have a 7 day window of being clear.
I have a table with the phone numbers and a table with the orders that is related to the phone numbers table via phone_number_id. The orders table has the current customers start_date and end_date for travel without the phone number id saved yet to it. The orders table also has the start_date and end_date for all other customers dates of travel as well as which phone_number_id was assigned/booked up for their travel dates.
How would the MySQL query look like when trying to select the phone numbers that are available for the current customers dates?
I build below query at the moment
SELECT x.id
, x.area_code
, x.phone_number
, y.start_date
, y.end_date
FROM vir_num_table x
LEFT
JOIN orderitemsdetail_table y
ON y.vn_id = x.id
WHERE y.start_date BETWEEN '2020-01-11' AND '2020-01-18'
OR y.start_date IS NULL
I've build this query but stuck here how can I add end_date logic.
Any help would be appreciated! Thanks in advance.
The way I'd approach the problem would be to look at conceptually, is as a cross product of the set of all phone numbers, along with the reservation timeframe, and then exclude those where there's a conflicting reservation.
A conflict would be an overlap, existing reservation that has a start_date before the end of the proposed reservation AND has an end_date on or after the start of the proposed reservation.
I'd do an anti-join pattern, something like this:
SELECT pn.phone_number
FROM phone_number pn
LEFT
JOIN reservation rs
ON rs.phone_number = pn.phone_number
AND rs.start_dt <= '2019-12-27' + INTERVAL +7 DAY
AND rs.end_dt > '2019-12-20' + INTERVAL -7 DAY
WHERE rs.phone_number IS NULL
That essentially says get all rows from phone number, along with matching rows from reservations (rows that overlap), but then exclude all the rows that had a match, leaving just phone_number rows that did not have a match.
We can make the < test a <= or , subtract 8 days, to tailor the "7 day" window before; we can tweak as we run the query through the test cases,
We can achieve an equivalent result using a NOT EXISTS and a correlated subquery. Some people find this easier to comprehend than the ant-join, but its essentially the same query, doing the same thing, get all rows from phone_number but exclude the rows where there is a matching (overlapping) row in reservation
SELECT pn.phone_number
FROM phone_number pn
WHERE NOT EXISTS
( SELECT 1
FROM reservation rs
WHERE rs.phone_number = pn.phone_number
AND rs.start_dt <= '2019-12-27' + INTERVAL +7 DAY
AND rs.end_dt > '2019-12-20' + INTERVAL -7 DAY
)
There are several questions on StackOverflow about checking for overlap, or no overlap, of date ranges.
See e.g.
How to check if two date ranges overlap in mysql?
PHP/SQL - How can I check if a date user input is in between an existing date range in my database?
MySQL query to select distinct rows based on date range overlapping
EDIT
Based on the SQL added as an edit to the question, I'd do the query like this:
SELECT pn.`id`
, pn.`area_code`
, pn.`phone_number`
FROM `vir_num_table` pn
LEFT
JOIN `orderitemsdetail_table` rs
ON rs.vn_id = pn.id
AND rs.start_date <= '2020-01-18' + INTERVAL +7 DAY
AND rs.end_date > '2020-01-11' + INTERVAL -7 DAY
WHERE rs.vn_id IS NULL
The two "tricky" parts. First is the anti-join, understanding how that works. (An outer join, to return all rows from vir_num_table but exclude any rows that have a matching row in reservations. The second tricky part is checking for the overlap, coming up with the conditions: r.start <= p.end AND r.end >= p.start, then tweaking whether we want to include the equals as an overlap, and tweaking the extra seven days (easiest to me to just subtract the 7 days from the beginning of the proposed reservation)
... now occurs to me like we need to add a guard period of 7 days on the end of the reservation period as well, doh!
Here's a query plus sorting algo to choose the optimal phone number selection for maximum utilization efficiency (i.e. getting as close as possible to exactly 7 days before and after each use).
I set it to give open ends a weight of 9, so that "near perfect" fits (7-8 days before or after) would be selected ahead of open-ended numbers. This will yield a slight efficiency improvement, as open numbers can accommodate any reservation. You can adjust this for your needs. If you set this to 0, for example, it would always select open numbers first.
SELECT ph.phone_number,
COALESCE(
MIN(
IF(res.end_date > res.start_date > '2020-01-18',
NULL, -- ignore before-comparison for reservations starting and ending after date range
DATEDIFF('2020-01-11', res.end_date)
), 9) AS open_days_before,
COALESCE(
MIN(
IF(res.start_date < res.end_date < '2020-01-11',
NULL, -- ignore after-comparison for reservations starting and ending before date range
DATEDIFF(res.start_date, '2020-01-18')
), 9) AS open_days_after
FROM phone_number ph
LEFT JOIN reservation res
ON res.phone_number = ph.phone_number
AND res.end_date >= CURRENT_DATE() - INTERVAL 6 DAY
GROUP BY ph.phone_number
HAVING open_days_before >= 7
AND open_days_after >= 7
ORDER BY open_days_before + open_days_after
LIMIT 1
Edit: updated to add grouping, because I realize this is an aggregate problem.
Edit 2: bug fix, changed MAX to MIN
Edit 3: added res.end_date >= CURRENT_DATE - INTERVAL 6 DAY to ignore past reservations, limiting aggregate data and treating phone number with no reservations between 6 days ago and the beginning of the new order as "open on the front-end"
Edit 4: added IF conditions to eliminate reservations outside the given before-or-after comparison ranges (e.g. comparing reservations after the selected range from influencing the "open days before" number), to prevent negative numbers, except when there's overlap with the selected range.
Based on the info you've added then you shouldn't need to check the start date of phone numbers which have been booked out.
You customer provides you with a start date and an end date.
You only rent out phone numbers 7 days after their last lease ended
All you need to do is fetch back phone numbers which either:
- Are not rented out and therefor aren't in the orderitems table
- OR have an end_date which is 7 days before the new customer's start date.
Here you go:
SELECT
`main_table`.`id`,
`main_table`.`area_code`,
`main_table`.`phone_number`,
`orderitemsdetail_table`.`start_date`,
`orderitemsdetail_table`.`end_date`
FROM
`vir_num_table` AS `main_table`
LEFT JOIN
`orderitemsdetail_table` AS `orderitemsdetail_table` ON main_table.id = orderitemsdetail_table.vn_id
WHERE
(DATE_ADD(orderitemsdetail_table.end_date, INTERVAL 7 DAY) < '<CUSTOMER START DATE>'
AND orderitemsdetail_table.start_date > '<CUSTOMER END DATE>')
OR orderitemsdetail_table.id IS NULL

Calculate date and time in two separate records

Can anyone please help me. I am trying to calculate the difference between record entry 1 and record entry 2 and so and so on date and time in two separate records
I want to add another row to calculate the difference Between Entry 1 and Entry 2. I know that if i did have a check in and check out then i could just subtract time out from time in and its sorted. But have never tried between records
Update And the Code that i'm using is:
SELECT USERINFO.name, CHECKINOUT.CHECKTIME
FROM CHECKINOUT, USERINFO
WHERE (((USERINFO.name)=[Enter Name]))
ORDER BY CHECKINOUT.CHECKTIME DESC;
Select
name,
DateValue(CHECKTIME) AS Date,
TimeValue(CHECKTIME) AS Time
From CHECKOUT
It could be:
Select *,
(CheckTime - (Select Top 1 CheckTime
From [Employee Check] As T
Where T.Name = [Employee Check].Name
And T.CheckTime < [Employee Check].CheckTime
Order By T.CheckTime Desc)) As CheckTimeDiff
From [Employee Check]

How to sum order total amount by week

I am trying to retrieve last 3 months records. I need to sum order total amount by week. I have made following query.
select CONCAT("Week", WEEK(created_at)) as week_number, sum(total_cost)
from certified_diamond_orders
where created_at > 2016-11-22
and status = "delivered"
group by week("created_at")
But I am only getting one record with this. Infact my table has 2 years entries. Also I was trying to figure out how I can pull week start date and end date to diplay on my chart.
Any suggestions where I am making mistake?
week("created_at") looks like you're trying to determine the week of the string "created_at" rather than the column created_at. Which might explain why you're only getting one row in your result.
The date 2016-11-22 also looks suspiciously like a sum instead of a date (2016 - 11 - 22 = 1983 vs "2016-11-22"
Try this:
SELECT
CONCAT('Week', WEEK(created_at)) AS week_number,
SUM(total_cost)
FROM certified_diamond_orders
WHERE
created_at > '2016-11-22' AND
status = 'delivered'
GROUP BY WEEK(created_at)

MySQL Group By Order and Count(Distinct)

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?

SQL query for calculating a monthly total out of a running total

I have a table titled "Reports" in MySQL that has a column titled "Flow_Total" which has a running total value that goes up every day and never resets, what i need is a query that takes the values that are stored in the "Flow_Total" column and divide them by month and tells me how much the value goes up every month.
This is how i would like to see the data:
https://skydrive.live.com/redir?resid=BC22A6E2F92CE833!11843&authkey=!ACgipFLKDJTBlN8
The value for the month is written on the last day of that month.
A summary of what i want to do is subtract the monthly change from the Flow_Total and display it in a separate column titled Monthly Total.
Maybe not the most pleasing SQL to the eyes, but this should do what you're asking; it'll just self join the table with itself delayed 1 month and calculate the difference from that.
SELECT DATE_FORMAT(MAX(a.`DATE`), '%b-%y') `DATE`,
MAX(a.`FLOW_TOTAL`) `Flow Total`,
(MAX(a.`FLOW_TOTAL`) - MAX(b.`FLOW_TOTAL`)) `Monthly Total`
FROM Reports a
LEFT JOIN Reports b
ON YEAR(a.`DATE`) = YEAR(DATE_ADD(b.`DATE`, INTERVAL 1 MONTH)) AND
MONTH(a.`DATE`) = MONTH(DATE_ADD(b.`DATE`, INTERVAL 1 MONTH))
GROUP BY YEAR(a.`DATE`), MONTH(a.`DATE`)
ORDER BY a.`DATE` DESC;
An SQLfiddle for testing.