I have the following table named Stock_Level. (It has around 200,000 rows)
| sku | location_id | stock_level | allocated | on_order | date_booked_in | date_last_sold | date_last_stock_take| status_since |
|-----|-------------|-------------|-----------|----------|---------------------|---------------------|---------------------|---------------------|
| 1 | 1 | 100 | 2 | 15 | 2020-12-01 12:34:00 | 2020-11-25 12:34:00 | 2021-01-01 12:00:00 | 2021-02-09 12:55:00 |
| 2 | 1 | 50 | 1 | 0 | 2020-11-02 12:34:00 | 2020-10-09 12:34:00 | 2021-01-01 12:00:00 | 2021-02-09 12:55:00 |
| 3 | 1 | 10 | 0 | 12 | 2020-08-05 12:34:00 | 2020-05-04 12:34:00 | 2021-01-01 12:00:00 | 2021-02-09 12:55:00 |
| 1 | 2 | 80 | 0 | 13 | 2020-09-09 12:34:00 | 2020-12-06 12:34:00 | 2021-01-03 12:00:00 | 2021-02-09 13:55:00 |
| 2 | 2 | 35 | 3 | 0 | 2020-07-10 12:34:00 | 2020-08-05 12:34:00 | 2021-01-03 12:00:00 | 2021-02-09 12:55:00 |
| 1 | 3 | 60 | 1 | 0 | 2020-08-12 12:34:00 | 2020-09-04 12:34:00 | 2021-02-03 12:00:00 | 2021-02-09 12:55:00 |
| 2 | 3 | 9 | 0 | 17 | 2020-05-30 12:34:00 | 2020-01-01 12:34:00 | 2021-02-03 12:00:00 | 2021-02-09 12:55:00 |
PRIMARY key is on sku and location_id
There is also an INDEX on status_since
A SKU can be in multiple locations. When an item is booked in, sold or stock taken, status_since is updated to NOW()
What I'm trying to do
I have a script that runs every 15 minutes, selecting items that have been updated in the last 15 minutes, with the SUM of (stock_level - allocated) for that sku, along with the total number on order and the MAX() values of date_booked_in, date_last_sold and date_last_stock_take
The code that I've currently got is this:
SELECT sku,
SUM(stock_level - allocated) AS available,
SUM(on_order) AS on_order,
MAX(date_booked_in) AS date_booked_in,
MAX(date_last_sold) AS date_last_sold,
MAX(date_last_stock) AS date_last_stock
FROM Stock_Level
WHERE
sku IN(
SELECT sku
FROM Stock_Level
WHERE
status_since>= DATE_SUB(NOW(), INTERVAL 15 MINUTE)
AND status_since<= NOW()
)
GROUP BY sku
This works fine when there are only a few items that have been recently updated; however sometimes many hundreds can be updated within a 15 minute period, and this query grinds to a halt.
I suspect there is a way to do this with a self join rather than using an IN() clause but I can't for the life of me work it out.
What would be some potential ways to rewrite this query?
You can use EXISTS which is much faster than IN and is always recommended:
SELECT sku,
SUM(stock_level - allocated) AS available,
SUM(on_order) AS on_order,
MAX(date_booked_in) AS date_booked_in,
MAX(date_last_sold) AS date_last_sold,
MAX(date_last_stock) AS date_last_stock
FROM Stock_Level
WHERE
EXISTS(
SELECT 1
FROM Stock_Level sl2
WHERE
sl2.status_since>= DATE_SUB(NOW(), INTERVAL 15 MINUTE)
AND sl2.status_since<= NOW()
AND sku.sku = sl2.sku
)
GROUP BY sku
Related
I have a table in an old version of MySQL 5.x like this:
+---------+------------+------------+
| Task_ID | Start_Date | End_Date |
+---------+------------+------------+
| 1 | 2015-10-15 | 2015-10-16 |
| 2 | 2015-10-17 | 2015-10-18 |
| 3 | 2015-10-19 | 2015-10-20 |
| 4 | 2015-10-21 | 2015-10-22 |
| 5 | 2015-11-01 | 2015-11-02 |
| 6 | 2015-11-17 | 2015-11-18 |
| 7 | 2015-10-11 | 2015-10-12 |
| 8 | 2015-10-12 | 2015-10-13 |
| 9 | 2015-11-11 | 2015-11-12 |
| 10 | 2015-11-12 | 2015-11-13 |
| 11 | 2015-10-01 | 2015-10-02 |
| 12 | 2015-10-02 | 2015-10-03 |
| 13 | 2015-10-03 | 2015-10-04 |
| 14 | 2015-10-04 | 2015-10-05 |
| 15 | 2015-11-04 | 2015-11-05 |
| 16 | 2015-11-05 | 2015-11-06 |
| 17 | 2015-11-06 | 2015-11-07 |
| 18 | 2015-11-07 | 2015-11-08 |
| 19 | 2015-10-25 | 2015-10-26 |
| 20 | 2015-10-26 | 2015-10-27 |
| 21 | 2015-10-27 | 2015-10-28 |
| 22 | 2015-10-28 | 2015-10-29 |
| 23 | 2015-10-29 | 2015-10-30 |
| 24 | 2015-10-30 | 2015-10-31 |
+---------+------------+------------+
If the End_Date of the tasks are consecutive,
then they are part of the same project.
I am interested in finding the total number of different projects completed.
If there is more than one project that have the same number of completion days,
then order by the Start_Date of the project.
For this few sample records the expected output would be:
2015-10-15 2015-10-16
2015-10-17 2015-10-18
2015-10-19 2015-10-20
2015-10-21 2015-10-22
2015-11-01 2015-11-02
2015-11-17 2015-11-18
2015-10-11 2015-10-13
2015-11-11 2015-11-13
2015-10-01 2015-10-05
2015-11-04 2015-11-08
2015-10-25 2015-10-31
I am a bit jammed with this.
I would really appreciate any help. Thanks.
Following query should work:
select tmp.projectid, date_sub(max(tmp.ed2), interval max(tmp.projectdays) day) start_date,
max(tmp.ed2) end_date,
max(tmp.projectdays) No_Of_ProjectDays
from
(
select t1.task_id tid1, t1.start_date sd1, t1.end_date ed1,
t2.task_id tid2, t2.start_date sd2, t2.end_date ed2,
case when datediff(t2.start_date, ifnull(t1.start_date,'1000-01-01')) != 1
then (#pid := #pid + 1)
else (#pid := #pid)
end as ProjectId,
case when datediff(t2.start_date, ifnull(t1.start_date,'1000-01-01')) != 1
then (#pdays := 1)
else (#pdays := #pdays + 1)
end as ProjectDays
from tasks t1 right join tasks t2
on t2.task_id = t1.task_id + 1
cross join (select #pid :=1, #pdays := 1) vars
) tmp
group by tmp.projectid
order by max(tmp.projectdays), start_date
Please find the Demo here.
EDIT : I have made changes in the query and link according to new data sample. Please have a look.
This answers -- and answers correctly -- the original version of this question.
Hmmmm . . . I think you can use variables. The simplest way is to generate a sequential number and then subtract this value to get a constant for adjacent rows from the date:
select min(start_date), max(end_date)
from (select t.*, (#rn := #rn + 1) as rn
from (select t.* from tasks t order by end_date) t cross join
(select #rn := 0) params
) t
group by (end_date - interval rn day);
Here is a db<>fiddle.
It's a little tricky problem, but the query below works fine.
It builds two tables, one with Start_Date and other with End_Date
that NOT IN End_Date and Start_Date respectively from Projects table,
and query these tables fetching Start_Date WHERE Start_Date < End_Date grouping by Start_Date
using aggregate function MIN with End_Date to get a complete Project.
DATEDIFF(MIN(End_Date), Start_Date) to calculate project_duration and able to order by project_duration.
SELECT Start_Date, MIN(End_Date) AS End_Date, DATEDIFF(MIN(End_Date), Start_Date) AS project_duration
FROM
(SELECT Start_Date FROM Projects WHERE Start_Date NOT IN (SELECT End_Date FROM Projects)) a,
(SELECT End_Date FROM Projects WHERE End_Date NOT IN (SELECT Start_Date FROM Projects)) b
WHERE Start_Date < End_Date
GROUP BY Start_Date
ORDER BY project_duration ASC, Start_Date ASC;
expected output
+------------+------------+---------------+
| Start_Date | End_Date | project_duration |
+------------+------------+---------------+
| 2015-10-15 | 2015-10-16 | 1 |
| 2015-10-17 | 2015-10-18 | 1 |
| 2015-10-19 | 2015-10-20 | 1 |
| 2015-10-21 | 2015-10-22 | 1 |
| 2015-11-01 | 2015-11-02 | 1 |
| 2015-11-17 | 2015-11-18 | 1 |
| 2015-10-11 | 2015-10-13 | 2 |
| 2015-11-11 | 2015-11-13 | 2 |
| 2015-10-01 | 2015-10-05 | 4 |
| 2015-11-04 | 2015-11-08 | 4 |
| 2015-10-25 | 2015-10-31 | 6 |
+------------+------------+---------------+
I have a simple table that used to store fuel issuing details for different vehicles as follows:
+----+------------+-----+---------------+-------------+
| id | vehicle_no | qty | meter_reading | date_issued |
+----+------------+-----+---------------+-------------+
| 1 | 366 | 50 | 10500 | 2019-09-01 |
| 2 | 366 | 50 | 11020 | 2019-09-02 |
| 3 | 367 | 25 | 25000 | 2019-09-03 |
| 4 | 366 | 50 | 11450 | 2019-09-04 |
| 5 | 368 | 50 | 6000 | 2019-09-05 |
+----+------------+-----+---------------+-------------+
02) Then I need to find no of Kilometers run against issued sum of fuel quantities.
03) I used the following query
select f1.vehicle_no, (select f1.meter_reading-f2.meter_reading)/sum(qty) from fuel f1) from fuel f2 group by vehicle_no
04) I want to get the desired output as follows :
As an example :
the meter reading of id=4 - meter reading of id=2 is 430 Kilometers
the meter reading of id=4 - meter reading of id=1 is 950 Kilometers
the meter reading of id=2 - meter reading of id=1 is 520 Kilometers
But I did not get the expected result. Can anyone help me ?
With a self join:
select
f.id,
ff.id,
f.vehicle_no,
f.date_issued,
ff.date_issued,
f.meter_reading - ff.meter_reading as dif
from fuel f inner join fuel ff
on ff.vehicle_no = f.vehicle_no and ff.date_issued < f.date_issued
See the demo.
Results:
> id | id | vehicle_no | date_issued | date_issued | dif
> -: | -: | ---------: | :------------------ | :------------------ | --:
> 2 | 1 | 366 | 2019-09-02 00:00:00 | 2019-09-01 00:00:00 | 520
> 4 | 1 | 366 | 2019-09-04 00:00:00 | 2019-09-01 00:00:00 | 950
> 4 | 2 | 366 | 2019-09-04 00:00:00 | 2019-09-02 00:00:00 | 430
I am trying to show invoices for every single day, so for that purpose I used group by on created date and sum on subtotal. This is how I done it :
SELECT
`main_table`.*,
SUM(subtotal) AS `total_sales`
FROM
`sales_invoice` AS `main_table`
GROUP BY
DATE_FORMAT(created_at, "%m-%y")
Its working, but I also want to get the Invoice # from and Invoice # to for every date. Is it possible to do it with single query ?
EDIT :
Table Structure :
------------------------------------------------
| id | inoice_no | created_at | subtotal
| 1 | 34 | 2015-03-17 05:55:27 | 5
| 2 | 35 | 2015-03-17 12:35:00 | 7
| 3 | 36 | 2015-03-20 01:40:00 | 3
| 4 | 37 | 2015-03-20 07:05:13 | 6
| 5 | 38 | 2015-03-20 10:25:23 | 1
| 6 | 39 | 2015-03-24 12:00:00 | 6
------------------------------------------------
Output
---------------------------------------------------------------
| id | inoice_no | created_at | subtotal | total_sales
| 2 | 35 | 2015-03-17 12:35:00 | 7 | 12
| 5 | 38 | 2015-03-20 10:25:23 | 1 | 10
| 6 | 39 | 2015-03-24 12:00:00 | 6 | 6
-----------------------------------------------------------------
What I Expect
---------------------------------------------------------------
| id | inoice_no | created_at | subtotal | total_sales | in_from | in_to
| 2 | 35 | 2015-03-17 12:35:00 | 7 | 12 | 34 | 35
| 5 | 38 | 2015-03-20 10:25:23 | 1 | 10 | 36 | 38
| 6 | 39 | 2015-03-24 12:00:00 | 6 | 6 | 39 | 39
-----------------------------------------------------------------
If your invoice number is INTEGER then below query will give you the result what you want:
SELECT DATE_FORMAT(A.created_at, "%m-%y") AS InvoiceDate,
MIN(A.invoiveNo) AS FromInvoiceNo,
MAX(A.invoiveNo) AS ToInvoiceNo,
SUM(A.subtotal) AS total_sales
FROM sales_invoice AS A
GROUP BY InvoiceDate;
I guess salesid is primaryid in sales_invoice table.
select * from(
SELECT
`main_table`.*,
SUM(subtotal) AS `total_sales`
FROM
`sales_invoice` AS `main_table`
GROUP BY
DATE_FORMAT(created_at, "%m-%y")
order by main_table.salesid limit 1
union all
SELECT
`main_table`.*,
SUM(subtotal) AS `total_sales`
FROM
`sales_invoice` AS `main_table`
GROUP BY
DATE_FORMAT(created_at, "%m-%y")
order by main_table.salesid desc limit 1
)a
How to calculate total hours between now and any date but to exclude weekdays?
I'm trying on this way:
select id, creationTime,
time_format(timediff(now(), creationTime), '%H:%m:%s') AS totalspenttime
from tblrezgo where DAYOFWEEK(creationTime) NOT IN (1,7)
This query should remove saturday and sundays from calculation but it seems that includes also those two days.
By running query:
select id, creationTime, DAYOFWEEK(creationTime) FROM tblrezgo
Output is:
+-------------+---------------------+------------+
| ID | creationTime | DAYOFWEEK |
+-------------+---------------------+------------+
| 1 | 2015-10-23 17:12:05 | 6 |
+-------------+---------------------+------------+
| 2 | 2015-10-24 10:23:11 | 7 |
+-------------+---------------------+------------+
| 3 | 2015-10-24 11:51:04 | 7 |
+-------------+---------------------+------------+
| 4 | 2015-10-26 14:30:28 | 2 |
+-------------+---------------------+------------+
| 5 | 2015-10-26 08:24:59 | 2 |
+-------------+---------------------+------------+
| 6 | 2015-10-26 17:29:03 | 2 |
+-------------+---------------------+------------+
| 7 | 2015-10-27 08:16:45 | 3 |
+-------------+---------------------+------------+
If i run my query then totalspenttime for ID = 1 is about 86 hour which is not correct. I've checked and it should be about 41 hours 'til now (if we execlude two days of weekend).
i have a table that has the following columns : s.no,house_no,energy,time
i want to find the total energy for each house for every one hour.
table :
+-----+----------+---------------------+--------+
| sno | house_no | time | energy |
+-----+----------+---------------------+--------+
| 1 | 1 | 2014-10-20 10:00:00 | 5 |
| 2 | 1 | 2014-10-20 10:30:00 | 10 |
| 3 | 2 | 2014-10-20 10:00:00 | 7 |
| 4 | 1 | 2014-10-20 11:01:00 | 3 |
| 5 | 2 | 2014-10-20 11:00:00 | 20 |
+-----+----------+---------------------+--------+
i am trying for 10-11 am.But this query sums the energy of the rows whose time value is greater than 11 am also.
SELECT house_no, sum( energy ) AS sum, time
FROM main
GROUP BY house_no
HAVING (
TIMESTAMPDIFF(
MINUTE , time, '2014-10-20 11:00:00' ) >0)
the result is :
+----------+------+---------------------+
| house_no | sum | time |
+----------+------+---------------------+
| 1 | 18 | 2014-10-20 10:00:00 |
| 2 | 27 | 2014-10-20 10:00:00 |
+----------+------+---------------------+
but the actual answer should be:
+----------+------+---------------------+
| house_no | sum | time |
+----------+------+---------------------+
| 1 | 15 | 2014-10-20 10:00:00 |
| 2 | 7 | 2014-10-20 10:00:00 |
+----------+------+---------------------+
You have to group the time also based on the hours
SELECT house_no, sum( energy ) AS sum, time
FROM main
GROUP BY house_no,DATE_FORMAT(time,'%d %b %Y %H')
HAVING (
TIMESTAMPDIFF(
MINUTE , time, '2014-10-20 11:00:00' ) >0)
DEMO