How to perform computation on a temporary table using MySQL? - mysql

I am trying to get the average of a column after doing a division grouped by weeks.
Here is my original table
id
week
amount
div
1
5
50
5
2
5
40
3
3
4
35
3
4
4
60
10
5
6
70
9
First I want to SUM them with group by weeks, and divide the amount/div
week
amount
div
amount/div
5
90
8
11.25
4
95
13
7.30
6
70
9
7.77
Now to get the average of amount/div which is (11.25+7.3+7.77)/3 = 8.77
I just want to get the 8.77
Here is what I tried:
SELECT AVG(amount/ div) from mytable GROUP BY week
but I didn't get the desired result of 8.77

Sample DB made.
http://sqlfiddle.com/#!9/6c7fa7/9
Used FLOOR to match your values 11.25, 7.3, 7.77, usual ROUND(...,2) got 1 cent difference.
CREATE TABLE stats (
id INT PRIMARY KEY AUTO_INCREMENT,
week INT,
amount INT,
divs INT
);
INSERT INTO stats ( week, amount, divs ) VALUES ( 5, 50, 5 );
INSERT INTO stats ( week, amount, divs ) VALUES ( 5, 40, 3 );
INSERT INTO stats ( week, amount, divs ) VALUES ( 4, 35, 3 );
INSERT INTO stats ( week, amount, divs ) VALUES ( 4, 60, 10 );
INSERT INTO stats ( week, amount, divs ) VALUES ( 6, 70, 9 );
SELECT ROUND(AVG(amount_divs), 2)
FROM (
SELECT FLOOR(100* SUM(amount)/SUM(divs))/100 AS amount_divs
FROM stats
GROUP BY week
) x
If you run following query
SELECT AVG(amount/divs)
FROM stats
It has different meaning, it is the average of all records (5 values), not the 3 after SUM

Related

Show all hours even when no data

I am looking to write a query that shows all hours even when no data is present. I have seen some posts where the suggestion is to create a temporary table that has all the hours listed but I am not sure how to do that. Here is my current query:
select DATE_FORMAT(t_stamp, "%h %p") as Hour, count(*) as Count
from cyclehistory
where DATE(t_stamp) = CURRENT_DATE()
group by hour(t_stamp)
This works returns the following
Hour | Count
09 AM | 6
10 AM | 11
1 PM | 5
But I would like it to return
Hour | Count
.
.
.
09 AM | 6
10 AM | 11
11 AM | 0
12 PM | 0
1 PM | 5
.
.
You do need some kind of table of numbers to do this. Here, you just need 24 numbers, from 0 to 23. Say the numbers table is called numbers with column num:
select maketime(n.num, 0, 0) as t_time, count(c.t_stamp) cnt
from numbers n
left join cyclehistory c
on hour(c.tstamp) = n.num
and c.t_stamp >= current_date and c.t_stamp < current_date + interval 1 day
group by n.num
There are multiple ways to build the number table. You can create a table and insert records manually:
create table numbers (num int primary key);
insert into numbers values (0), (1), ..., (23);
For a one-time task, you can create a derived table directly in the query:
select ...
from (
select 0 num
union all select 1
...
union all select 23
) numbers
left join ...
In MySQL 8.0, you can use a recursive query:
with recursive numbers (num) as (
select 0
union all select num + 1 from cte where num < 23
)
select ...
from numbers
left join ...

SQL query which selects the last row from each day

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

select: result based on occurrence of explicit value

Given is following mysql table:
CREATE TABLE fonts
(`id` int, `fontName` varchar(22), `price` int,`reducedPrice` int,`weight` int)
;
INSERT INTO fonts
(`id`, `fontName`, `price`,`reducedprice`,`weight`)
VALUES
(1, 'regular', 50,30,1),
(2, 'regular-italic', 50,20,1),
(3, 'medium', 60,30,2),
(4, 'medium-italic', 50,30,2),
(5, 'bold', 50,30,3),
(6, 'bold-italic', 50,30,3),
(7, 'bold-condensed', 50,30,3),
(8, 'super', 50,30,4)
;
As an example a user chooses following ids: 1,2,3,5,6,7
which would result in following query/result:
> select * from fonts where id in(1,2,3,5,6,7);
id fontName price reducedPrice weight
1 regular 50 30 1
2 regular-italic 50 20 1
3 medium 60 30 2
5 bold 50 30 3
6 bold-italic 50 30 3
7 bold-condensed 50 30 3
Is it possible to have a kind of "if statement" in a query to return a new field based on column weight. Where a value occurs more than once reducedPrice should be returned as newPrice else price:
id fontName price reducedPrice weight newPrice
1 regular 50 30 1 30
2 regular-italic 50 20 1 20
3 medium 60 30 2 60
5 bold 50 30 3 30
6 bold-italic 50 30 3 30
7 bold-condensed 50 30 3 30
Which means ids 1,2,5,6,7 should be reduced but id 3 not as its weight "2" only occurs once
Please find a fiddle here: http://sqlfiddle.com/#!9/73f5db/1
And thanks for your help!
Write a subquery that gets the number of occurrences of each weight, and join with this. Then you can test the number of occurrences to decide which field to put in NewPrice.
SELECT f.*, IF(weight_count = 1, Price, ReducedPrice) AS NewPrice
FROM fonts AS f
JOIN (SELECT weight, COUNT(*) AS weight_count
FROM fonts
WHERE id IN (1, 2, 3, 5, 6, 7)
GROUP BY weight) AS w ON f.weight = w.weight
WHERE id IN (1, 2, 3, 5, 6, 7)
Updated fiddle
select *,if(occurences>=2,reducedPrice,price) as newPrice from fonts
left join (Select count(id) as occurences, id,weight from fonts
where fonts.id in(1,2,3,5,6,7) group by weight) t on t.weight = fonts.weight
where fonts.id in(1,2,3,5,6,7);
The mysql if keyword reference is here:https://dev.mysql.com/doc/refman/5.1/en/control-flow-functions.html#function_if
Edit: Added fiddle, changed to instances as comment requested.
Updated fiddle:http://sqlfiddle.com/#!9/a93ef/14
SELECT DISTINCT x.*
, CASE WHEN y.weight = x.weight THEN x.reducedPrice ELSE x.price END newPrice
FROM fonts x
LEFT
JOIN
( SELECT * FROM fonts WHERE id IN(1,2,3,5,6,7) )y
ON y.weight = x.weight
AND y.id <> x.id
WHERE x.id IN(1,2,3,5,6,7)
ORDER
BY id;

Removing duplicate values from a dataset

I am developing an SSRS report with the following dataset (Table-1). I am grouping by Account and Period. My goal is to get the Total Expense and the Budget within a group. Because the Budget data is duplicated per group, I cannot use SUM() function for Budget. How do I remove the duplicates so the new dataset looks like this? (Table-2) Please advice. Thank you for your time.
Table-1
ID Account Period Expense Budget
1 100 201301 20 100
2 100 201301 30 100
3 100 201302 10 150
4 100 201302 40 150
5 200 ...................
Table-2
ID Account Period Expense Budget
1 100 201301 20 100
2 100 201301 30 NULL
3 100 201302 10 150
4 100 201302 40 NULL
5 200 ...................
If you really want to make duplicate budgets null try this update command
please check sqlfiddle http://sqlfiddle.com/#!3/1e619/11
Update table1
set budget = null
where id in
(
select aa.id from
(
select id,row_number()
over(partition by Budget order by Period) as rno
from table1
) aa
where rno > 1
);
select * from table1;
good luck.
I would use a windowed function if you have to do that grouping in SQL. If you can do it in SSRS just add a 'Row Grouping Parent' it would be better.
For SQL I would do this:
declare #Temp table ( ID int identity, Account int, period int, expense int, budget int);
insert into #Temp values (100, 201301, 20, 100),(100, 201301, 30, 100),(100, 201302, 10, 150),(100, 201302, 40, 150)
select *
from #Temp
select
ID
, Account
, Period
, Expense
, case when (row_number() over(partition by Budget order by Period) = 1) then Budget end as Budget-- only shows first occurrence of an order amount ordering by person
from #Temp

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