I have about 5000 rows of data as follow:
id date temperature room
--------------------------------------
0 2013-07-15 76 A
1 2013-08-15 72 A
2 2013-09-15 74 B
3 2013-02-15 71 B
4 2013-03-15 72 B
5 2013-04-15 70 A
...
...
...
5000 2013-08-01 68 A
I can use the query from below to find min temperature in each room.
select room, min(temperature) from table_record group by room.
Now, I need to find all rows that are close to the the min temperature for each room.
I have try using "join" on the same table as below, but it cannot be run.
select t1.room, min(t1.temperature) from table_record t1
join on table_record t2 on
t2.room = t1.room and
t2.temperature * 0.95 (less_or_equal) min(t1.temperature)
group by room
You need to do this in two steps.
SELECT
*
FROM
(
SELECT room, MIN(temperature) AS min_temp FROM TABLE_RECORD GROUP BY room
)
AS ROOM_TEMP
INNER JOIN
TABLE_RECORD
ON TABLE_RECORD.room = ROOM_TEMP.room
AND TABLE_RECORD.temperature <= ROOM_TEMP.min_temp / 0.95
Provided that you have an index on (room, temperature) this should be pretty quick.
Also, note that I use x <= y / 0.95 rather than x * 0.95 <= y. This is to make the lookup faster (manipulate the search criteria once, rather than the searched field on every row).
Related
I have table A
uid dt val_A
10 04/09/2012 34
10 08/09/2012 35
10 10/09/2012 36
100 04/09/2012 40
100 08/09/2012 41
and table B
uid date val_B
10 04/09/2012 1
10 05/09/2012 1
10 06/09/2012 2
10 07/09/2012 2
10 08/09/2012 1
100 07/09/2012 1
100 07/09/2012 3
I want to join them to get table C. I want to join them on uid. Furthermore I want to have a new column val_C which holds the average of val_B where date in B is greater or equal than the corresponding row-value dt in A AND less than the next higher dt value for this uid in table A. It means I want to aggregate the values in B based on date ranges defined in A. The joined table should look like this:
uid dt val_A val_C
10 04/09/2012 34 1.5
10 08/09/2012 35 1
10 10/09/2012 36 0
100 04/09/2012 40 2
100 08/09/2012 41 0
How can this be achieved?
//EDIT
How could a more generalized solution look like where all dates in B2 which are greater than the greatest date in A are being joined & aggregated to the greatest date in A. B2:
uid date val_B
10 04/09/2012 1
10 05/09/2012 1
10 06/09/2012 2
10 07/09/2012 2
10 08/09/2012 1
100 07/09/2012 1
100 07/09/2012 3
100 10/09/2012 4
100 11/09/2012 2
Desired output C2:
uid dt val_A val_C
10 04/09/2012 34 1.5
10 08/09/2012 35 1
10 10/09/2012 36 0
100 04/09/2012 40 2
100 08/09/2012 41 3
If you're on MySQL v8+ that supports LEAD() function, then you can try this:
WITH cte AS (
SELECT uid, dt, val_A,
IFNULL(LEAD(dt) OVER (PARTITION BY uid ORDER BY uid, dt),dt) dtRg
FROM tableA)
SELECT cte.uid, cte.dt, cte.val_A,
AVG(val_B) AS val_C
FROM cte
LEFT JOIN tableB tb1
ON cte.uid=tb1.uid
AND tb1.dt >= cte.dt
AND tb1.dt < cte.dtRg
GROUP BY cte.uid, cte.dt, cte.val_A
The query in common table expression (cte):
SELECT uid, dt, val_A,
IFNULL(LEAD(dt) OVER (PARTITION BY uid ORDER BY uid, dt),dt) dtRg
FROM tableA
will give you a result like this:
As you can see, the dtRg column is generated using LEAD() function which takes the next row dt value according to the ORDER BY. Read more about LEAD() here.
After that, join the cte with tableB on matching uid and where tableB.dt is the same or bigger than the existing tableA.dt - which is now as cte.dt, but lower than cte.dtRg - which is the next date in tableA that was generated by LEAD(). And finally adding AVG(val_B) AS val_C
Demo fiddle
On older MySQL version, you can try this:
SELECT tA.uid, tA.dt, tA.val_A,
AVG(val_B) AS val_C
FROM
(SELECT uid, dt, val_A,
(SELECT dt FROM tableA ta1
WHERE ta1.uid=ta2.uid
AND ta1.dt > ta2.dt LIMIT 1) AS dtRg
FROM tableA ta2) tA
LEFT JOIN tableB tB
ON tA.uid=tB.uid
AND tB.dt >= tA.dt
AND tB.dt < tA.dtRg
GROUP BY tA.uid, tA.dt, tA.val_A;
The difference are as following:
Instead of using LEAD(), it uses correlated subquery in SELECT to get the next dt value of next row in the same uid.
Instead of common table expression, it uses a derived table.
Fiddle for MySQL v5.7 version
I have the following table that I would like to compare
I have to compare dates per employee and if the date range is less than 60 days it has to extract the employee.
example:
the employee with number 4 has 3 records (4479,4192,1982)
The combination of these 3 records (4479-4192, 4479-1982, 4192-1982) the dates must be compared and if one of them is less than 60 days, the employee must be extracted.
below the example table:
http://sqlfiddle.com/#!9/b44d4e
id
idEmployee
date
1228
2
2020-06-11 21:10:53
3382
2
2020-06-11 12:37:04
2223
2
2020-08-17 21:10:57
4479
4
2020-08-17 12:37:08
4192
4
2020-07-29 12:37:08
1982
4
2020-07-29 21:10:56
2627
8
2020-04-16 12:37:02
474
8
2020-04-16 21:10:49
1002
10
2020-05-29 21:10:52
3150
10
2020-05-29 12:37:04
pd: mysql database
Any help or suggestion how should I make the query?
Using an exists approach:
SELECT DISTINCT idEmployee
FROM yourTable t1
WHERE EXISTS (SELECT 1 FROM yourTable t2
WHERE t2.idEmployee = t1.idEmployee AND
DATEDIFF(t2.date, t1.date) > 60);
In plain English, the above query says to return any employee record for which we can find another record of the same employee more than 60 days apart. The distinct select removes duplicates.
You can use a self-join
SELECT DISTINCT a.idEmployee
FROM employees AS a
JOIN employees AS b ON a.idEmployee = b.idEmployee AND DATEDIFF(a.date, b.date) > 60
But instead of comparing all with all, you really only need to compare the most extreme dates with each other. If they're less than 60 days apart, all the other dates must also be less.
SELECT idEmployee
FROM employees
GROUP BY idEmployee
HAVING DATEDIFF(MAX(a.date), MIN(a.date)) > 60
one way is to use exists
select * from table t1
where exists (
select 1 from table t2
where t1.idEmployee = t2.idEmployee
and t2.Id <> t1.id
and abs(datediff(t2.date, t1.date)) < 60)
I faced a difficult problem about SQL query.
Please help me
I have two tables like below.
TABLE_A TABLE_B
Date Value_A Date Value_B
20180201 52 20180131 120
20180202 50 20180201 114
20180205 48 20180203 127
20180206 57 20180204 140
20180207 60 20180206 129
And I want to get this result.
Date Value_A PreValue_B
20180201 52 120
20180202 50 114
20180205 48 140
20180206 57 140
20180207 60 129
Date and Value_A are the same as TABLE_A.
PreValue_B is from Value_B.
But it is the value of the maximum (closest) & previous date of TABLE_B.
so, the closest previous date of 20180201 of TABLE_B is "20180131".
...
the closest previous date of 20180205 is "20180204", so PreValue_B is 140.
20180206 is "20180204", so PreValue_B is 140 again.
and so on...
How to make the SQL Query?
Thanks to everyone!
A typical approach uses correlated subqueries:
select a.*,
(select b.value
from b
where b.date < a.date
order by b.date desc
fetch first 1 row only
) as prevalue_b
from a;
This uses the ANSI standard method for limiting to one row. Some databases spell this as limit or select top 1.
Try this:
SELECT sub.date, sub.a, b.b
FROM
(SELECT a.date, a.a, MAX(b.date) AS b_date
FROM a
INNER JOIN b
ON (a.date > b.date)
GROUP BY a.date, a.a) sub
INNER JOIN b
ON sub.b_date = b.date
ORDER BY sub.date
In the sub-query, find the date that should be selected in b for each date in a. Then join the results back to b, in order to show the b value.
Tested here: http://rextester.com/ERP28040
Struggling with some sql, would appreciate some guidance.
Have two tables logs and sense
logs –
assetid ts nodeid status
1 2017-10-26 14:00:10 73 240
2 2017-10-26 14:00:06 21 160
3 2017-10-26 14:00:04 18 230
4 2017-10-26 14:00:02 19 400
5 2017-10-26 14:00:00 21 190
1 2017-10-26 13:20:08 18 20
2 2017-10-26 13:06:10 20 160
3 2017-10-26 13:03:04 17 230
sense –
status value
20 5
160 37
190 39
230 56
240 58
400 90
Trying to find the correct syntax to only show the latest record (in datetime) of each assetid and then show the corresponding value from the sense table (based on the matching status in both tables) to produce –
assetid ts nodeid status value
1 2017-10-26 14:00:10 73 240 58
2 2017-10-26 14:00:06 21 160 37
3 2017-10-26 14:00:04 18 230 56
4 2017-10-26 14:00:02 19 400 90
5 2017-10-26 14:00:00 21 190 39
Have tried –
Select assetid, ts, nodeid, status, value
From
logs
Join sense X on X.status = logs.status
Group by assetid
Order by ts DESC
But this only outputs 1 row (instead of 5)
assetid ts nodeid status value
1 2017-10-26 14:00:10 73 240 58
Removing
Join sense X on X.status = logs.status
of course outputs all records but that is not required.
Thoughts appreciated.
Regards
Active
Actually your query is returning 5 rows, 1 for each id. But it won't return rows with latest ts for each id. You can verify this by clicking on the link for demo. You can compare results of both queries.
To achieve this task,following query will help you:
Select l.assetid, l.ts, logs.nodeid, X.status, X.value
From
logs
inner Join sense X on X.status = logs.status
inner join (select assetid, max(ts) as ts from logs group by assetid) l
on l.assetid = logs.assetid and logs.ts = l.ts
Group by l.assetid
Order by l.ts DESC;
Click here for Demo
EDIT:
If dataype of ts is string then replace max(ts) in above query with:
max(str_to_date(ts,'%d%m%y'))
Feel free to ask any doubts.
Hope it helps!
Try this
Select a1.assetid, MAX(a1.ts), a1.nodeid, a1.status, X.value
From
logs a1
inner join sense X on X.status = a1.status
Group by assetid, a1.nodeid, a1.status, X.value
Order by ts DESC
Use GROUP BY to find minimum for each assetid and then JOIN with the logs and sense
Select *
FROM logs l
JOIN sense s ON s.status = l.status
JOIN
(
Select assetid, max(ts) maxts
From logs
Group by assetid
) t ON t.assetid = l.assetid and l.ts = t.maxts
demo
On MY SQL 8.0.2
WITH CTE as
(
Select A.assetid, A.ts, A.nodeid, A.status, B.value, row_number() over(PARTITION BY A.assetid ORDER BY A.ts DESC) AS rn
from logs as A
inner join sense B ON A.status=B.status
)
SELECT *
FROM CTE
WHERE rn='1';
I am trying to find a way to get the largest price difference (in a time frame, e.g. 24 hours) in a MySQL table using a source and productId as reference.
Here is a sample product, productId 22.
id price createdAt updatedAt sourceId productId
21 799.00 2017-07-26 19:46:46 2017-07-26 19:46:45 1 22
853 920.00 2017-07-26 06:46:46 2017-07-26 06:46:46 1 22
855 799.00 2017-07-22 16:17:11 2017-07-22 16:17:11 2 22
851 770.00 2017-07-21 16:17:11 2017-07-21 16:17:11 1 22
856 799.00 2017-07-20 16:17:11 2017-07-20 16:17:11 2 22
852 599.00 2017-07-19 16:17:11 2017-07-19 16:17:11 1 22
857 810.00 2017-07-18 16:17:11 2017-07-18 16:17:11 2 22
858 799.00 2017-07-17 16:17:11 2017-07-17 16:17:11 2 22
In the example above for productId 22 I am sorting by createdAt, so in this scenario I'd take id 21 and substract it from id 853, this would give -121, meaning the product went down 121 dollars.
In the full data it's a mush up of prices, sourceIds and productIds. The goal here is to make a result look like this:
id createdAt sourceId productId adjustment
21 2017-07-26 19:46:46 1 22 -121
22 2017-07-26 16:46:46 2 22 201
23 2017-07-26 15:46:46 6 24 -20
Above is kind of how I am trying to get the data to look, so I'll know of the price difference of each product of each source. Then I can control the data, such as ordering by adjustment and seeing which source + product had the largest decrease or increase in a time frame.
I've tried doing a ton of sub-queries, I've probably put in a hundred examples that I've modified from Google. I can piece together parts of this, such as only getting products that have recieved a change of any kind from the past 24 hours. I've tried to merge the last two rows of each product Id, then do a math, and list all the products. It's been 2 days of trying to build this query, is it just best for me to not use queries for everything and do it on my backend?
I've even went to a support site like hackhands and they couldn't figure it out. I've exhausted all of my ideas.
This query breaks down the problem:
1) Getting the records corresponding to start_at time of the window for each product in order to get the baseline price.
2) Gets the the records for the max price for each product in the time frame.
3) Gets the records for the min price for each product in the time frame.
4) Combines 1 and 2 and 3 to form a single record per product and shows the info and the difference between base line price and the highest and lowest in the time frame.
If you only need the bigger of the two you can add and extra layer of select wrapping this query and user GREATER(a,b) to keep one diff or the other.
select BOWPRICE.product_id, BOWPRICE.created_at, BOWPRICE.price,
MAXPRICE.max_price_upd_time, MAXPRICE.max_price, ABS((BOWPRICE.price - MAXPRICE.max_price)) max_price_diff,
MINPRICE.min_price_upd_time, MINPRICE.min_price, ABS((BOWPRICE.price - MINPRICE.min_price)) min_price_diff
from
(
select mainA.product_id, mainA.created_at, mainA.price from SOTEST mainA
where id in (
select id
from SOTEST N
where created_at = (
select min(N1.created_at)
from SOTEST N1
where N1.created_at >= '2017-07-26 00:00:00'
and N1.product_id = N.product_id
)
group by mainT.product_id
)
) BOWPRICE,
(
select mainB.product_id, mainB.updated_at max_price_upd_time, mainB.price max_price from SOTEST mainB
where id in(
select id from SOTEST M
where M.price = (
select max(M1.price)
from SOTEST M1
where M1.created_at >= '2017-07-26 00:00:00'
and M1.created_at < '2017-07-27 00:00:00'
and M1.product_id = M.product_id
group by product_id LIMIT 1
)
)
) MAXPRICE,
(
select mainC.product_id, mainC.updated_at min_price_upd_time, mainC.price min_price from SOTEST mainC
where id in(
select id from SOTEST Q
where Q.price = (
select min(Q1.price)
from SOTEST Q1
where Q1.created_at >= '2017-07-26 00:00:00'
and Q1.created_at < '2017-07-27 00:00:00'
and Q1.product_id = Q.product_id
group by product_id LIMIT 1
)
)
) MINPRICE
where BOWPRICE.product_id = MAXPRICE.product_id
and BOWPRICE.product_id = MINPRICE.product_id