How can I get the succeeding or previous data row result from a query, as I wanted to compute days difference based on date_created for succeeding rows?
SELECT
-- *,
h.date_created,
-- (select date_created where id = h.id and pci_s = h.pci_s + 1) as dc,
h.id,
h.date_created,
CONCAT('B', h.pci_b) AS batch,
h.pci_s,
DATEDIFF(h.date_created, h.date_created) as days_in_stage
FROM
historical h
WHERE
h.pci_b = 1
;
Expected
date_created id date_created batch pci_s days_in_stage
2021-07-18T06:32:26Z 1 2021-07-18T06:32:26Z B1 0 0
2021-07-20T06:32:26Z 4 2021-07-20T06:32:26Z B1 1 2
Here's the jsfiddle
http://sqlfiddle.com/#!9/f32a242/3
Currently using: Mysql 5.7.33
I get the succeeding or previous data row result
that you are looking for LEAD or LAG window function.
but your MySQL version is lower than 8.0 which didn't support window function.
you can try to use a subquery to make LEAD or LAG window function.
Query 1:
SELECT
h.date_created,
h.id,
h.date_created,
CONCAT('B', h.pci_b) AS batch,
h.pci_s,
COALESCE(DATEDIFF(h.date_created,(
SELECT hh.date_created
FROM historical hh
WHERE h.pci_b = hh.pci_b AND h.date_created > hh.date_created
ORDER BY hh.date_created DESC
LIMIT 1
)),0) as days_in_stage
FROM
historical h
WHERE
h.pci_b = 1
Results:
| date_created | id | date_created | batch | pci_s | days_in_stage |
|----------------------|----|----------------------|-------|-------|---------------|
| 2021-07-18T06:32:26Z | 1 | 2021-07-18T06:32:26Z | B1 | 0 | 0 |
| 2021-07-20T06:32:26Z | 4 | 2021-07-20T06:32:26Z | B1 | 1 | 2 |
With MySQL8 you can simply use LAG() or LEAD() window functions.
https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html#function_lag
With MySQL5.x you don't have that so can instead use correlated-sub-queries, for example...
SELECT
-- *,
(SELECT date_created FROM historical WHERE date_created < h.date_created ORDER BY date_Created DESC LIMIT 1) AS prev_date_created,
-- (select date_created where id = h.id and pci_s = h.pci_s + 1) as dc,
h.id,
h.date_created,
CONCAT('B', h.pci_b) AS batch,
h.pci_s,
DATEDIFF(h.date_created, h.date_created) as days_in_stage
FROM
historical h
WHERE
h.pci_b = 1
ORDER BY
h.date_created
;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=0ce3b601688e1de11eda1007bffea9f9
In the new version of mysql you can use Lead and Lag functions for getting next row and previous row data. But if you are not using current version then this will not work, there is other solution.
Please share your mysql version if not using new version.
For eg for new version:
SELECT
-- *,
h.date_created,
-- (select date_created where id = h.id and pci_s = h.pci_s + 1) as dc,
h.id,
h.date_created,
CONCAT('B', h.pci_b) AS batch,
h.pci_s,
DATEDIFF(h.date_created, h.date_created) as days_in_stage,
lead(h.date_created)over (order by h.date_created) next_row_date,
lag(h.date_created)over (order by h.date_created) pre_row_date
FROM
historical h
WHERE
h.pci_b = 1
;
Related
I have a table. It has the following structure
goods_receiving_items
id
item_id
quantity
created_at
I am trying to fetch rows against which have the following conditions
Has one item_id
When the sum of the quantity column equals a certain value
So for example I have the following data
+----+---------+----------+------------+
| id | item_id | quantity | created_at |
+----+---------+----------+------------+
| 1 | 2 | 11 | 2019-10-10 |
| 2 | 3 | 110 | 2019-10-11 |
| 3 | 2 | 20 | 2019-11-09 |
| 4 | 2 | 5 | 2019-11-10 |
| 5 | 2 | 1 | 2019-11-11 |
+----+---------+----------+------------+
I have tried the following query:
SET #sum:= 0;
SELECT item_id, created_at, (#sum:= #sum + quantity) AS SUM, quantity
FROM goods_receiving_items
WHERE item_id = 2 AND #sum<= 6
ORDER BY created_at DESC
If I don't use ORDER BY, then the query will give me ID '1'. But if I use ORDER BY it will return all the rows with item_id = 2.
What should be returned are IDs '5' and '4' exclusively in this order
I can't seem to resolve this and ORDER BY is essential to my task.
Any help would be appreciated
You should use the order by on the resulting set
you could do this using a subquery
SET #sum:= 0;
select t.*
from t (
SELECT item_id
, created_at
, (#sum:= #sum + quantity) as sum
, quantity
FROM goods_receiving_items
WHERE item_id = 2 AND #sum<= 6
) t
ORDER BY created_at DESC
You should try an INNER JOIN with SELECT min(created_at) or SELECT max(created_at)
From MYSQL docs:
...the selection of values from each group cannot be influenced by
adding an ORDER BY clause. Sorting of the result set occurs after
values have been chosen, and ORDER BY does not affect which values the
server chooses.
The answers on the following might help in more detail: MYSQL GROUP BY and ORDER BY not working together as expected
After searching around, I have made up the following query
SELECT
t.id, t.quantity, t.created_at, t.sum
FROM
( SELECT
*,
#bal := #bal + quantity AS sum,
IF(#bal >= $search_number, #doneHere := #doneHere + 1 , #doneHere) AS whereToStop
FROM goods_receiving_items
CROSS JOIN (SELECT #bal := 0.0 , #doneHere := 0) var
WHERE item_id = $item_id
ORDER BY created_at DESC) AS t
WHERE t.whereToStop <= 1
ORDER BY t.created_at ASC
In the above query, $search_number is a variable that holds the value that has to be reached. $item_id is the item we are searching against.
This will return all rows for which the sum of the column quantity makes up the required sum. The sum will be made with rows in descending order by created_at and then will be rearranged in ascending order.
I was using this query to calculate the cost when a certain amount of items are being used in an inventory management system; so this might help someone else do the same. I took most of the query from another question here on StackOverflow
I have a table like this:
timesent |nr | value
2018-10-31 05:23:06 | 4 | Value 3
2018-10-31 05:20:19 | 4 | Value 2
2018-10-31 05:19:35 | 4 | Value 1
2018-10-31 04:55:56 | 3 | Value 2
2018-10-31 03:05:15 | 3 | Value 1
2018-10-31 01:31:49 | 2 | Value 1
2018-10-30 04:11:16 | 1 | Value 1
At the moment, my select looks like this:
SELECT * FROM values WHERE ORDER BY timesent DESC
I want to do an sql-select statement which gives me back only the most recent value of each "nr".
My skills are not good enough to translate that into a sql-statement. I donĀ“t even know what I should google for.
Values is a Reserved Keyword in MySQL. Consider changing your table name to something else; otherwise you will have to use backticks around it
There are various ways to achieve the result for your problem. One way is to do a "Self-Left-Join" on nr (field on which you want to get the maximum timesent value row only).
SELECT v1.*
FROM `values` AS v1
LEFT JOIN `values` AS v2
ON v1.nr = v2.nr AND
v1.timesent < v2.timesent
WHERE v2.nr IS NULL
For MySQL version >= 8.0.2, you can use Window Functions. We will determine Row_Number() for each row over a partition of nr, with timesent in Descending order (Highest timesent value will have row number = 1). Then, use this result-set in a Derived Table and consider only those rows, where row number is equal to 1.
SELECT dt.timesent,
dt.nr,
dt.value
FROM
(
SELECT v.timesent, v.nr, v.value,
ROW_NUMBER() OVER (PARTITION BY v.nr
ORDER BY v.timesent DESC) AS row_num
FROM `values` AS v
) AS dt
WHERE dt.row_num = 1
Yet, another approach is to get the maximum value of timesent for a nr group in a Derived Table. Now join this result-set to the main table, so that only the rows corresponding to max value appear:
SELECT v.timesent,
v.nr,
v.value
FROM
`values` AS v
JOIN
(
SELECT nr, MAX(timesent) AS max_timesent
FROM `values`
GROUP BY nr
) AS dt ON dt.nr = v.nr AND
dt.max_timesent = v.timesent
This is data in table orders.
Does anyone can help me to do the statistics work?
-------------------------------
id | order_amount | createtime
-------------------------------
1 | 10 | 1513605522
2 | 20 | 1513605523
3 | 30 | 1513605524
4 | 40 | 1513605525
-------------------------------
This is the output what I need
-------------------------------
total_income | createtime
-------------------------------
10 | 1513605522
30 | 1513605523
60 | 1513605524
100 | 1513605525
-------------------------------
this is my sql statement, it doesn't give me what i want.
select sum(order_amount) as total_income from order group by create time;
BTW. Is it possible to produce the output....
Any help means a lot to me. Many thanks.
You can use:
set #amt := 0;
select #amt := #amt + order_amount as total_income, createtime
from order
order by createtime asc;
In MySQL, you can do this using a correlated subquery:
select o.*,
(select sum(o2.order_amount)
from orders o2
where o2.createtime <= o.createtime
) as running_amount
from orders o;
Another option -- using non-standard SQL -- is to use variables:
select o.*, (#s := #s + o.order_amount) as running_amount
from (select o.*
from orders o
order by createtime
) o cross join
(select #s := 0) params;
Note that the subquery is only needed in the more recent versions of MySQL.
Actually, MySQL 8.0 finally supports window functions, so it can be done using standard SQL in that version:
select o.*, sum(o.order_amount) over (order by o.createtime) as running_amount
from orders o;
Say this is my table schema
Create Table PowerReading
{ Device VARCHAR(32);
Power INT;
Time TIMESTAMP;
}
Say these are the rows in my table
Device | Power | Time
A3 | 5 | 2013-05-01 17:36:00
A3 | 9 | 2013-05-01 17:37:44
B1 | 11 | 2013-05-01 17:35:14
B1 | 5 | 2013-05-01 17:35:55
B7 | 4 | 2013-05-01 17:34:12
B7 | 0 | 2013-05-01 17:34:44
I've spent like days trying to figure out how to show the reading that is the most recent for each DISTINCT device name. I want an SQL query that gives THIS output from the above table.
Device | Power | Time
A3 | 9 | 2013-05-01 17:37:44
B1 | 5 | 2013-05-01 17:35:55
B7 | 0 | 2013-05-01 17:34:44
I've tried to accomplish using the below code, but it's useless
SELECT * FROM (SELECT Device,Power,MAX(Time) as Max FROM PowerReading GROUP
BY Device,Power) t1 JOIN PowerReading on t1.Device=PowerReading.Device AND
t1.max=PowerReading.Power
What the above code gives me is an output that is not to my desired output
This little issue has been eating my head for days. Please help me? :-)
The derived table was using a group by on power column as well, which is not needed.
SELECT p.* FROM
(SELECT Device, MAX(Time) as maxtime
FROM PowerReading
GROUP BY Device) t1
JOIN PowerReading p on t1.Device = p.Device AND t1.maxtime = p.time
You can alternatively do it using variables:
SELECT Device, Power, Time
FROM (
SELECT Device, Power, Time,
#rn := IF (#dev = Device, #rn + 1,
IF(#dev := Device, 1, 1)) AS rn
FROM PowerReading
CROSS JOIN (SELECT #rn := 0, #dev := '') AS vars
ORDER BY Device, Time DESC) AS t
WHERE t.rn = 1
Variable #rn essentially simulates ROW_NUMBER() OVER (PARTITION BY Device ORDER BY Time DESC window function, available in other RDBMSs.
The above query will select exactly one row per Device even if there are more than one rows sharing the exact same timestamp.
Demo here
One method uses a correlated subquery:
select pr.*
from PowerReading pr
where pr.time = (select max(time) from PowerReading pr2 where pr2.dev = pr.dev);
This version can make optimal use of an index on PowerReading(dev, time).
I have a table with various orders in it:
ID | Date | etc...
1 | 2013-01-01 | etc
2 | 2013-02-01 | etc
3 | 2013-03-01 | etc
4 | 2013-04-01 | etc
5 | 2013-05-01 | etc
6 | 2013-06-01 | etc
7 | 2013-06-01 | etc
8 | 2013-03-01 | etc
9 | 2013-04-01 | etc
10 | 2013-05-01 | etc
I want a query that ends wit the result:
overallTotal | totalThisMonth | totalLastMonth
10 | 2 | 1
But I want to do this in one query! I am trying to find a way to use subqueries to do this. SO far I have:
SELECT * from (
SELECT count(*) as overallTotal from ORDERS
)
How can I combine this with other subqueries so I can get the totals in one query?
UPDATE
Original question was for MySQL, but I need it for Firebird now.
With conditional sums you can do it (MySQL syntax):
select
count(*) as overallTotal,
sum(if(Month(Date)+12*Year(Date)=Month(GetDate())+12*Year(GetDate()), 1, 0))
as totalThisMonth
sum(if(Month(Date)+12*Year(Date)=Month(GetDate())+12*Year(GetDate())-1, 1, 0))
as totalThisMonth;
from mytable
Use the month+12*year formula to avoid the problem with year change.
UPDATE
With Firebird the same applies, you only have to replace Month(x) with EXTRACT (MONTH FROM x), Year(x) with EXTRACT (YEAR FROM x) and Getdate() with CURRENT_TIME. This will look ugly, so I won't put it here, but you could easily do it yourself.
Posubly use SUM of the result of IF statements, with the IF checking that it is a relevant date.
SELECT COUNT(*),
SUM(IF(YEAR(Date) = YEAR(CURDATE()) AND MONTH(Date) = MONTH(CURDATE()), 1, 0))
SUM(IF((YEAR(Date) = YEAR(CURDATE()) AND MONTH(Date) = MONTH(CURDATE()) - 1) OR (YEAR(Date) = YEAR(CURDATE()) - 1 AND MONTH(Date) = 12 AND MONTH(CURDATE()) = 1), 1, 0))
from ORDERS
Complexity is caused by coping with a change of year when looking for the previous month
I see you are already using sub queries, so why not do something like the following,
SELECT
(SELECT count(*) from ORDERS) as overallTotal,
(SELECT count(*) from ORDERS where Date between ... and ...) as totalThisMonth,
(SELECT count(*) from ORDERS where Date between ... and ...) as overallTotal
SELECT
(SELECT COUNT(*) FROM C AS total) AS T,
(SELECT COUNT(*) AS thisMonth FROM C WHERE MONTH(d) = 6) AS A,
(SELECT COUNT(*) AS lastMonth FROM C WHERE MONTH(d) = 5) AS B;
Please notice "month" are "hard coded" -- but it shouldn't be too difficult to extract the "current month" at application level. If you really need to, you could do it at SQL level with something like that:
SELECT
(SELECT COUNT(*) FROM C AS total) AS T,
(SELECT COUNT(*) AS thisMonth FROM C WHERE MONTH(d) = MONTH(NOW()) ) AS A,
(SELECT COUNT(*) AS lastMonth FROM C WHERE MONTH(d) = (MONTH(NOW())+11)%12) ) AS B;