SQL query which selects the last row from each day - mysql

I need your help. I have a table (senosrId, time, data), and I need to select the latest data from each day for one of the sensors for the latest 10 days.

For MS SQL, tested, compiled:
Test table:
CREATE TABLE [dbo].[DataTable](
[SensorId] [int] NULL,
[SensorTime] [datetime] NULL,
[SensorData] [int] NULL
)
Run several times to insert demo data:
insert into DataTable (SensorId, SensorTime, SensorData) select 1, getdate() - 15*rand(), convert(int, rand()*100)
Get last value for each of the last 10 days (actual answer):
select top 10 *
from DataTable
inner join ( -- max time for each day
select SensorId, max(SensorTime) as maxtime, convert(varchar(10), SensorTime, 112) as notneededcolumn
from DataTable
group by SensorId, convert(varchar(10), SensorTime, 112)
) lastvalues on lastvalues.maxtime=DataTable.SensorTime and lastvalues.SensorId=DataTable.SensorId
where DataTable.SensorId=1
order by DataTable.SensorTime desc
Example output:
1 2017-05-17 21:07:14.840 54 1 2017-05-17 21:07:14.840 20170517
1 2017-05-16 23:35:37.220 94 1 2017-05-16 23:35:37.220 20170516
1 2017-05-14 22:35:48.970 8 1 2017-05-14 22:35:48.970 20170514
1 2017-05-13 14:56:34.557 94 1 2017-05-13 14:56:34.557 20170513
1 2017-05-12 22:28:55.400 89 1 2017-05-12 22:28:55.400 20170512

Related

MYSQL , Count week wise and also show sum with empty dates

I have two tables
Table_1 : Routes_Day_plan
Date Status_Id
------------------------
2019-06-09 1
2019-06-10 2
2019-06-09 2
2019-06-11 3
2019-06-14 4
2019-06-14 6
2019-06-15 8
Table_2 : Codes
id code
-------
1 Leave
2 Half_leave
3 Holiday
4 Work
5 Full_Hours
Now my task is to count week wise from table 1 where code (from second table) = Leave,Half_leave,work and than also show the sum , and where date not found show 0 , i write this query it's return data but not empty dates can someone please help ,
My Query:
select COUNT(*) as available, DATE(date)
from Table_1
where status_id in (
select id from codes
where code in ('Leave','Half_leave','work'))
AND DATE(date) >= DATE('2019-06-09') AND DATE(date) <= DATE('2019-06-16')
group by date
UNION ALL
SELECT COUNT(date), 'SUM' date
FROM Table_1
where status_id in (
select id from codes
where code in ('Leave','Half_leave','work'))
AND DATE(date) >= DATE('2019-06-09') AND DATE(date) <= DATE('2019-06-16')
Result Something Like ,
available Dates
------------------------
5 2019-06-09
2 2019-06-10
3 2019-06-11
3 2019-06-12
2 2019-06-14
2 2019-06-15
17 SUM
I want like this
available Dates
------------------------
5 2019-06-09
2 2019-06-10
3 2019-06-11
3 2019-06-12
0 2019-06-13
2 2019-06-14
2 2019-06-15
17 SUM
Your best bet here would be to have a Date Dimension/Lookup table which contains pre-populated dates for the entire year. By joining your record table to this lookup, you essentially allocate your data to each date that actually exist (ex. 2019-06-13) and if your data is not found in the lookup, you will find a null in that field.
The Count function will count a null as a 0. Just make sure you group on the date field from your lookup table and not from your record table.
Make a table, a date dimension that contains all the dates value, from beginning to end. Like this:
Set EndDate = '2099-01-01';
Set RunDate = '1900-01-01';
WHILE RunDate <= EndDate DO
insert into dim_date
(`DATE`)
select
RunDate as DATE
;
Set RunDate = ADDDATE(RunDate,1);
END WHILE;
Create temporary table with dim_date left join Routes_Day_plan and set Status as 0 maybe for record that dont match. Use this temporary table then instead of Routes_Day_plan in your queries.

latest rate and sum of quantity from mysql table

I have a table look like below:
uid added_on rm_id qnty rate
1 2017-10-23 10:48:50 5 2 30
2 2017-10-23 10:48:50 6 4 70
3 2017-10-23 10:48:50 7 5 10
4 2017-10-24 11:02:10 5 10 28
5 2017-10-24 11:02:10 6 2 75
6 2017-10-24 11:03:37 7 1 15
7 2017-10-25 11:02:10 6 5 65
8 2017-10-25 11:03:37 7 8 12
I need the rm_id , its quantity (that is rm_id 5 is 12(2+10) ), and its last added rate(latest rate can find from the latest added_on rate or from last uid for each rm_id). Any way the result should look like below:
Result
rm_id total_qnty rate
5 12 28
6 11 65
7 14 12
I tried to achieve this by using
SELECT `rm_id`, sum(`qnty`),`rate` FROM `stock` group by `rm_id` having max(`uid`)
and
SELECT `rm_id`, sum(`qnty`),`rate` FROM `stock` group by `rm_id` having max(date(`added_on`))
But not getting the result as desired.. please help me..
You need to locate the max date, and from that determine the rate, and apply that rate to the summed quantity.
select
t.rm_id, t.rate, gd.sum_qty, gd.sum_qty * t.rate
from table1 t
inner join (
select rm_id, max(added_on) max_date, sum(qnty) sum_qty
from table1
group by rm_id
) gd on t.rm_id = gd.rm_id and t.added_on = gd.max_date
The data model is strange, why aren't rates separated?
having max(uid) translates to having 8 for rm_id = 7. And MySQL treats numbers > 0 as true, so this becomes having true, i.e. don't limit my results in any way. The aggregated result doesn't contain the single rates anyway, so it's too late to try to get it via HAVING. You'd need an aggregation function for this, such as Oracle's KEEP LAST, but MySQL doesn't feature this.
What you want instead is to get the maximum uid and with its help select the related record:
select
stock.rm_id,
stockagg.sum_qnty,
stock.rate as last_rate
from
(
select
rm_id,
sum(qnty) as sum_qnty,
max(uid) as max_uid
from stock
group by rm_id
) stockagg
join stock on stock.uid = stockagg.max_uid;
You can use subqueries:
SELECT `rm_id`, sum(`qnty`),
(SELECT `rate`
FROM `stock` s1
WHERE `uid` = (SELECT `uid`
FROM `stock` s2
WHERE s2.`rm_id` = s1.`rm_id`
ORDER BY `added_on` DESC
LIMIT 1)
) AS `rate`
FROM `stock`
GROUP BY `rm_id`

MySQL Query To Produce Amount Per Month

I have a table that contains:
id date user_id duration amount
1 2014-01-01 00:00:00 1 1 £10
2 2014-01-02 00:00:00 2 2 £10
3 2014-01-03 00:00:00 3 3 £10
I'm trying to display the amount per month. Any ideas how to do this in a query?
Working on the assumptions that you can extract the month from you datetime easily, so the real question is about the aggregation logic, and that you can create a numbers table.
Here is a simple example that shows the pattern.
sqlfiddle
CREATE TABLE Num (num int);
INSERT INTO Num VALUES (0),(1),(2),(3),(4);
CREATE TABLE Tbl (start int, run int);
INSERT INTO Tbl VALUES (1,2),(2,3);
SELECT start + num active_month
,count(*) * 10 income
FROM Tbl
INNER JOIN
Num ON num < run
GROUP BY start + num
Like Karl, I'm pretty sure some kind of numbers table is necessary here. Personally I like the approach given here, which defines a view (well, several of them) to generate numbers, instead of having to actually store a table full of numbers. Whether you use a table or a view, when you SELECT from it, it just looks like this:
n
---
0
1
2
3
…
With that you can construct a query like this:
SELECT
purchases.purchase_id,
purchases.date_purchased,
purchases.duration,
-- generator_16 is our numbers table
generator_16.n,
-- Below we calculate the year and month (year_mon) in the following way:
-- (1) Get the first day of the year, e.g. if date_purchased is 2012-12-28,
-- this gives us 2012-01-01.
-- (2) Get the month number, e.g. 12 for 2012-12-28) and add that many months
-- to the first day of the year, which gives us the first day of the
-- month, 2012-12-01.
-- (3) Add "n" months, where "n" is the number we get from the numbers table,
-- starting at 0.
DATE_ADD( -- (3)
DATE_ADD( -- (2)
MAKEDATE( YEAR(purchases.date_purchased), 1 ), -- (1)
INTERVAL MONTH(purchases.date_purchased) - 1 MONTH -- (2)
),
INTERVAL generator_16.n MONTH -- (3)
) AS year_mon,
purchases.amount_income / purchases.duration AS amount
FROM purchases
-- The below JOIN means that if `purchases.duration` is 3, we get three rows
-- that have 0, 1, and 2 in the `n` column, which we use as the number of dates
-- to add in (3) above.
JOIN generator_16
ON generator_16.n BETWEEN 0 AND purchases.duration - 1
ORDER BY purchases.purchase_id, year_mon;
This gives us a result like this (SQL Fiddle):
purchase_id date_purchased duration n year_mon amount
----------- -------------- -------- - ------------ ------
1 2013-12-28 … 2 0 2013-12-01 … 7.5
1 2013-12-28 … 2 1 2014-01-01 … 7.5
2 2014-01-04 … 1 0 2014-01-01 … 10
3 2014-02-04 … 6 0 2014-02-01 … 6.6667
3 2014-02-04 … 6 1 2014-03-01 … 6.6667
3 2014-02-04 … 6 2 2014-04-01 … 6.6667
3 2014-02-04 … 6 3 2014-05-01 … 6.6667
3 2014-02-04 … 6 4 2014-06-01 … 6.6667
3 2014-02-04 … 6 5 2014-07-01 … 6.6667
I inserted blank lines to separate the purchase_id groups so you can see how n increases from 0 to duration - 1 with each item in the group. As you can see, year_mon is equal to n months after the first day of the date_purchased month plus n months, and amount is equal to amount_income / duration.
We're almost done, but as you can see year_mon has repetition: 2014-01-01 is shown twice. This is great news, because we can then use GROUP BY to group by that column and SUM(amount) to get the total for that month:
SELECT
DATE_ADD(
DATE_ADD(
MAKEDATE( YEAR(purchases.date_purchased), 1 ),
INTERVAL MONTH(purchases.date_purchased) - 1 MONTH
),
INTERVAL generator_16.n MONTH
) AS year_mon,
SUM(purchases.amount_income / purchases.duration) AS total
FROM purchases
JOIN generator_16
ON generator_16.n BETWEEN 0 AND purchases.duration - 1
GROUP BY year_mon
ORDER BY year_mon;
The only difference between this query and the previous month is that we do GROUP BY year_mon and then SUM(amount_income / duration) to get total for the month, yielding this result (SQL Fiddle):
year_mon total
------------ ------
2013-12-01 … 7.5
2014-01-01 … 17.5
2014-02-01 … 6.6667
2014-03-01 … 6.6667
2014-04-01 … 6.6667
2014-05-01 … 6.6667
2014-06-01 … 6.6667
2014-07-01 … 6.6667
...and of course you can use DATE_FORMAT and CAST or ROUND to get nicely-formatted dates and amounts, or you can do that in your front-end code.
What about :
SELECT a.my_date, a.income, IFNULL(SUM(DISTINCT(a.income)) + sum( b.income ), a.income) as roll_up
FROM (
SELECT purchase_id, DATE_FORMAT( date_purchased, '%y-%m') AS my_date, SUM( amount_income / duration ) AS "income"
FROM incomes
GROUP BY my_date
) AS a
LEFT OUTER JOIN (
SELECT purchase_id, DATE_FORMAT( date_purchased, '%y-%m') AS my_date, SUM(amount_income / duration ) AS "income"
FROM incomes
GROUP BY my_date
) AS b ON ( a.purchase_id > b.purchase_id )
GROUP BY a.purchase_id
It's a bit tricky to do that in one shot - and it might be improved - but that gives the following results :
my_date income roll_up
13-12 8.5000 8.5000
14-01 10.0000 18.5000
14-02 16.6667 35.1667
My data set is :
1 2013-12-28 00:00:00 1 2 15
2 2014-01-04 00:00:00 2 1 10
3 2014-02-04 00:00:00 3 6 40
4 2013-12-29 00:00:00 4 1 1
5 2014-02-28 00:00:00 5 2 20

Select All Columns By Most Recent Date and Highest Version

I have been stumped on this for quite awhile. Request#, SlotId, Segment, and Version all make up the primary key. What i want from my stored proc is to be able to retrieve all rows by passing in the Request # and Segment, but for each slot i want the most recent effective date on or before todays date and from that i need the highest version #. I appriciate your time.
Values in database
Request# SlotId Segment Version Effective Date ContentId
A123 1 A 1 2012-01-01 1
A123 2 A 1 2012-01-01 2
A123 2 A 2 2012-02-01 34
A123 2 A 3 2012-02-01 24
A123 2 A 4 2015-01-01 6 //beyond todays date. dont want
Values I want to return from my stored proc when i pass in A123 for Request # and A for Segment.
A123 1 A 1 2012-01-01 1
A123 2 A 3 2012-02-01 24
The query could be written like this:
; WITH cte AS
( SELECT Request, SlotId, Segment, Version, [Effective Date], ContentId,
ROW_NUMBER() OVER ( PARTITION BY Request, Segment, SlotId
ORDER BY Version DESC ) AS RowN
FROM
tableX
WHERE
Request = #Req AND Segment = #Seg --- the 2 parameters
AND [Effective Date] < DATEADD(day, 1, GETDATE())
)
SELECT Request, SlotId, Segment, Version, [Effective Date], ContentId
FROM cte
WHERE Rn = 1 ;
Consider this:
;
WITH A as
(
SELECT DISTINCT
Request
, Segment
, SlotId
FROM Table1
)
SELECT A.Request
, A.SlotId
, A.Segment
, B.EffectiveDate
, B.Version
, B.ContentID
FROM A
JOIN (
SELECT Top 1
Request
, SlotId
, Segment
, EffectiveDate
, Version
, ContentId
FROM Table1 t1
WHERE t1.Request = A.Request
AND t1.SlotId = A.SlotId
AND T1.Segment = A.Segment
AND T1.EffectiveDate <= GetDate()
ORDER BY
T1.EffectiveDate DESC
, T1.Version DESC
) as B
ON A.Request = B.Request
AND A.SlotId = B.SlotId
AND A.Segment = B.Segment

mysql select rows by consecutive date

I have a table of available date blocks (7 days in my case) which may or may not be consecutive:
start_date end_date booked id room_id
2012-07-14 2012-07-21 0 1 6
2012-07-21 2012-07-28 0 2 6
2012-07-28 2012-08-04 1 3 6
2012-08-04 2012-08-11 0 4 6
What I'd like to do is be able to get a result set that gives me one row per X weeks of consecutive unbooked dates, within a date range.
So, for 2 week blocks starting on the 14th of July and using the above table data, I would expect the following:
start_date end_date booked
2012-07-14 2012-07-28 0
The second block of 2 weeks would not be returned as one of the component weeks is booked.
Here are a few ideas I've tried already:
SELECT
MIN(start_date) AS start_date_min,
MAX(end_date) AS end_date_max,
CAST(GROUP_CONCAT(id) AS CHAR) AS ids,
SUM(booked) AS booked
FROM
available_dates
WHERE
(start_date>=20120714 AND end_date<=DATE_ADD(20120714, INTERVAL 14 DAY))
GROUP BY
room_id
HAVING
end_date_max=DATE_ADD(20120714, INTERVAL 14 DAY)
This gets me part of the way, however doesn't get me the consecutive results - that is the important part. It also only returns a single result (probably because of the HAVING clause) when I widen the test data.
Can anyone point me in the right direction?
If you have a calendar or a numbers table:
CREATE TABLE num
( i INT NOT NULL
, PRIMARY KEY (i)
) ;
INSERT INTO num
(i)
VALUES
(0), (1), (2), ..., (1000) ;
You could use something like this:
SELECT
avail.room_id,
MIN(avail.start_date) AS start_date_min,
MAX(avail.end_date) AS end_date_max,
CAST(GROUP_CONCAT(avail.id) AS CHAR) AS ids,
SUM(avail.booked) AS booked
FROM
available_dates AS avail
CROSS JOIN
( SELECT DATE('2012-07-14') AS start_date_check
, 52 AS max_week_check
) AS param
JOIN
num
ON avail.start_date = param.start_date_check + INTERVAL num.i WEEK
AND num.i < param.max_week_check
WHERE
avail.booked = 0
GROUP BY
avail.room_id,
( num.i / 2 )
HAVING
COUNT(*) = 2
You could also have this:
WHERE
1 =1 --- no WHERE condition
GROUP BY
avail.room_id,
( num.i / 2 )
HAVING --- and optionally
SUM(avail.booked) = 0 --- this