How would i show even null values in this query:
select
case floor(reading_winddirection / 45)
when 0 then 'N'
when 1 then 'NE'
when 2 then 'E'
when 3 then 'SE'
when 4 then 'S'
when 5 then 'SW'
when 6 then 'W'
when 7 then 'NW'
end windgroup,
count(*) cnt,
round(100 * count(*) / sum(count(*)) over()) percentage
from simulation_readings
group by windgroup
Now the query returns ie:
N 66 66
E 2 2
SE 1 1
SW 1 1
But i wish it to return all cases even if they have no values and set them to 0
I think that you want a left join with a fixed list if values
select
d.windgroup,
count(s.reading_winddirection) cnt,
coalesce(round(
100 * count(s.reading_winddirection)
/ nullif(sum(count(s.reading_winddirection)) over(), 0)
), 0) percentage
from (
select 0 n, 'N' windgroup
union all select 1, 'NE'
union all select 2, 'E'
union all select 3, 'SE'
union all select 4, 'S'
union all select 5, 'SW'
union all select 6, 'W'
union all select 7, 'NW'
) d
left join simulation_readings s
on floor(s.reading_winddirection / 45) = d.n
group by d.windgroup
In MySQL < 8.0, this would look like:
select
d.windgroup,
coalesce(c.cnt, 0) cnt,
coalesce(round(100 * c.cnt, 0 / nullif(t.total, 0)), 0) percentage
from (
select 0 n, 'N' windgroup
union all select 1, 'NE'
union all select 2, 'E'
union all select 3, 'SE'
union all select 4, 'S'
union all select 5, 'SW'
union all select 6, 'W'
union all select 7, 'NW'
) d
cross join (select count(*) total from simulation_readings) t
left join (
select floor(reading_winddirection / 45) n, count(*) cnt
from simulation_readings
group by n
) c on c.n = d.n
group by d.windgroup
Related
I have function (from this question) which groups values by every 5 minutes and calculate min/avg/max:
SELECT (FLOOR(clock / 300) * 300) as period_start,
MIN(value), AVG(value), MAX(value)
FROM data
WHERE clock BETWEEN 1200000000 AND 1200001200
GROUP BY FLOOR(clock / 300);
However, due to missing values, some five-minute periods are skipped, making the timeline inconsistent. How to make it so that in the absence of data for a certain period, the value of max / avg / min becomes 0, instead of being skipped?
For example:
If I have timestamp - value
1200000001 - 100
1200000002 - 300
1200000301 - 100
1200000601 - 300
I want to get this: (select min/avg/max, time between 1200000000 and 1200001200)
1200000000 - 100/200/300
1200000300 - 100/100/100
1200000600 - 300/300/300
1200000900 - 0/0/0
Instead of this: (time between 1200000000 and 1200001200)
1200000000 - 100/200/300
1200000300 - 100/100/100
1200000600 - 300/300/300
1200000900 - THIS LINE WILL NOT BE, I will only get 3 lines above. No data between 1200000900 and 1200001200 for calculation.
My Answer:
Generate first table with required time range, and then left join this generated table on query with common group by operator. Such like this:
select * from
(select UNIX_TIMESTAMP(gen_date) as unix_date from
(select adddate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) gen_date from
(select 0 t0 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 t1 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 t2 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 t3 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 t4 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
where gen_date between '2017-01-01' and '2017-12-31') date_range_table
left join (
SELECT (FLOOR(clock / 300) * 300) as period_start,
MIN(value), AVG(value), MAX(value)
FROM table
WHERE clock BETWEEN 1483218000 AND 1514667600
GROUP BY FLOOR(clock / 300)) data_table
on date_range_table.unix_date = data_table.period_start;
Use recursive CTE (available in MariaDB starting from 10.2.2) and generate base calendar table:
WITH RECURSIVE
cte AS ( SELECT #timestart timestart, #timestart + 300 timeend
UNION ALL
SELECT timestart + 300, timeend + 300 FROM cte WHERE timeend < #timeend)
SELECT cte.timestart,
COALESCE(MIN(value), 0) min_value,
COALESCE(AVG(value), 0) avg_value,
COALESCE(MAX(value), 0) max_value
FROM cte
LEFT JOIN example ON example.clock >= cte.timestart
AND example.clock < cte.timeend
GROUP BY cte.timestart;
https://dbfiddle.uk/?rdbms=mariadb_10.3&fiddle=f5c41b7596d56f1d7babe075f19302ec
I am not very sure but here's a link which can solve your problem
https://www.sqlservercurry.com/2009/06/find-missing-identity-numbers-in-sql.html
You can try this one;
with seq as (
select
(step-1)* 300 + (select (FLOOR(min(clock) / 300) * 300) from data) as step
from
(select row_number() over() as step from data) tmp
where
tmp.step-1 < (select(max(clock)-min(clock))/ 300 from data))
SELECT seq.step as period_start, MIN(value), AVG(value), MAX(value)
FROM seq left join data on (seq.step=(FLOOR(clock / 300) * 300))
WHERE clock BETWEEN 1622667600 AND 1625259600
GROUP BY period_start
Alternative answer is generate first table with required time range, and then left join this generated table on query with common group by operator.
my apologies if this question sounds a little boring for someone I really turn here because I have already tried to solve my problem in different ways and I have not been able to obtain good results.
I am trying to convert a MySQL query to Laravel Query Builder, I show you the query:
SELECT DAY(AAA.fecha_hora_entrada) as DAY, IFNULL(BBB.SALES, 0) SALES, IFNULL(BBB.NET, 0) NET
FROM (
SELECT fecha_hora_entrada
FROM (
SELECT MAKEDATE(YEAR(NOW()), 1) +
INTERVAL (MONTH(NOW()) - 1) MONTH +
INTERVAL daynum DAY fecha_hora_entrada
FROM (
SELECT t * 10 + u daynum
FROM (SELECT 0 t UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) A,
(SELECT 0 u UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) B
ORDER BY daynum
) AS AA
) AS AA
WHERE MONTH(fecha_hora_entrada) = MONTH(NOW())
) AS AAA
LEFT JOIN (SELECT (DATE(fecha_hora_entrada)) AS fecha_hora_entrada, (SUM(neto)) AS NET, (COUNT(neto)) AS SALES
FROM t_derivados
GROUP BY DATE(fecha_hora_entrada)) BBB
ON AAA.fecha_hora_entrada = BBB.fecha_hora_entrada;
What I have tried so far is the following. So far I have managed to pass part of the MySQL query to Query Builder
public function salesYear()
{
return DB::table(DB::table(DB::table(DB::table(DB::table(null, null)
->selectRaw('0 t UNION SELECT 2 UNION SELECT 3'),'A')
->orderBy('daynum')
->selectRaw('t * 10 + u daynum'), 'AA')
->selectRaw('MAKEDATE(YEAR(NOW()), 1) + INTERVAL (MONTH(NOW()) - 1) MONTH + INTERVAL daynum DAY fecha_hora_entrada'), 'AA')
->addSelect('fecha_hora_entrada')
->whereRaw('MONTH(fecha_hora_entrada) = MONTH(NOW())'), 'AAA')
->selectSub('DAY(AAA.fecha_hora_entrada)', 'DAY')
->selectSub('IFNULL(BBB.SALES, 0)', 'SALES')
->selectSub('IFNULL(BBB.NET, 0)', 'NET')
->leftJoinSub(DB::table('t_derivados')
->selectSub('DATE(fecha_hora_entrada)', 'fecha_hora_entrada')
->selectSub('SUM(neto)', 'NET')
->selectSub('COUNT(neto)', 'SALES')
->groupBy(DB::raw('DATE(fecha_hora_entrada)')), 'BBB', 'AAA.fecha_hora_entrada', 'BBB.fecha_hora_entrada')
->toSql();
}
I know it's a bit boring to read everything .. but let's focus on the next sub-sentence:
SELECT t * 10 + u daynum
FROM (SELECT 0 t UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) A,
(SELECT 0 u UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) B
ORDER BY daynum
...
The truth is I have not found a way to add the second FROM to Query Builder
SELECT 0 u UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) B
...
Someone expert on the subject could help me please. Thank you very much in advance
I have two database table name called "tablestr table" and "restbookingtable":
tablestr:
str_id is primary key
restbooking:
bookingsection_id is foreign key
in booking table i storing str_id multiple values with comma separated and My query is
SELECT `str_id` FROM (`rest_tablestr`) WHERE str_id NOT IN (
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(t.bookingsection_id, ",", n.n), ",", -1) value FROM rest_restaurantbooking t
CROSS JOIN (
SELECT a.N + b.N * 10 + 1 n FROM (
SELECT 0 AS N
UNION ALL
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL
SELECT 9) a , (
SELECT 0 AS N
UNION ALL
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL SELECT 9)
b ORDER BY n ) n
WHERE n.n <= 1 + (LENGTH(t.bookingsection_id) -
LENGTH(REPLACE(t.bookingsection_id, ",", ""))) AND
t.res_id = 21 AND
t.booking_status not in ("cancelled","departed","noshow") AND
((t.bookingstart_time <= "2015-06-12 19:45:00" AND t.bookingend_time >= "2015-06-12 22:15:00") OR
(t.bookingend_time >= "2015-06-12 19:45:00" AND t.bookingend_time <= "2015-06-12 22:15:00") OR
(t.bookingstart_time >= "2015-06-12 19:45:00" AND t.bookingstart_time <= "2015-06-12 22:15:00") OR
(t.bookingstart_time >= "2015-06-12 19:45:00" AND t.bookingend_time <= "2015-06-12 22:15:00")) ) AND
`res_id` = '21' AND
`area_id` = '28' AND
`wait_table` = 'no' AND
`availability` = 'yes';
Result Set:
Can any body help me to rewrite query with left outer join or can be optimize query.
I have a table with some rows that have common groups:
Id Name Group
1 ABC1 G1
2 ABC2 G1
3 ABC3 G1
4 AXX3 G2
At some point I know the group I need to query (G1 for example). Then I need to query the left most common characters in the Name field among the rows filtered by their Group. So in this case, I'd get ABC.
Can this be performed in one query ? I need to do this in the simplest way possible.
You can do it by brute force:
select groupid,
(case when min(left(name, 10)) = max(left(name, 10)) then left(name, 10)
when min(left(name, 9)) = max(left(name, 9)) then left(name, 9)
when min(left(name, 8)) = max(left(name, 8)) then left(name, 8)
when min(left(name, 7)) = max(left(name, 7)) then left(name, 7)
when min(left(name, 6)) = max(left(name, 6)) then left(name, 6)
when min(left(name, 5)) = max(left(name, 5)) then left(name, 5)
when min(left(name, 4)) = max(left(name, 4)) then left(name, 4)
when min(left(name, 3)) = max(left(name, 3)) then left(name, 3)
when min(left(name, 2)) = max(left(name, 2)) then left(name, 2)
when min(left(name, 1)) = max(left(name, 1)) then left(name, 1)
end) as CommonPrefix
from t
group by groupid;
If you don't like typing so much, you can also do:
select groupid,
max(case when min(left(name, n.n)) = max(left(name, n.n)) then left(name, n.n) end)
from t cross join
(select 1 as n union all selet 2 union all select 3 . . .
) n
group by groupid;
(Or use a where clause to get the information for one group.) For this example, just keep adding integers to the n subquery up to the length you want to test.
Sorry about dirty integer generator:
select TT.g, max( MA ) from
(select t.g, max( left(t.Name,N.n ) ) MA, min( left(t.Name,N.n) ) MI
from t cross join
(select 0 as n union all select 1 union all select 3 union all
select 4 union all select 5 union all select 6 union all
select 6 union all select 7 union all select 8 union all select 9) N
group by t.g, N.n
) TT
where TT.MA = TT.MI
group by TT.g
Results:
+------+-----------+
| g | max( MA ) |
+------+-----------+
| G1 | ABC |
| G2 | AXX3 |
+------+-----------+
You can find the maximum substring by counting everything:
select k.common, k.setSize, k.number from
(select left(name, z.n) common, count(*) setSize, z.n number
from t
join (select 0 as n union all select 1 union all select 3 ...) as z
group by left(name, z.n)) k
order by k.setSize desc, k.number desc
which will return you the number of entries having the same common characters in the first x characters. The first line will give you the total number of entries followed by the top subgroup.
I have a table with 4 fields (id, Year, Week, Totals).
I need a query, I guess using join, to fill zero values based on the year and week fields.
In my example I need to fill zero values for the weeks 3 and 4 / Year 2013
Rec Id, Year, Week, Totals
1, '2012', '52', '23'
2, '2013', '1' , '9'
3, '2013', '2' , '4'
Missing record from DB -> null, '2013', '3' , '0'
Missing record from DB -> null, '2013', '4' , '0'
4, '2013', '5' , '5'
5, '2013', '6' , '6'
6, '2013', '7' , '5'
That was a fun one! OK, here we go. First off, I'll give you the simple version, which relies on a couple assumptions:
You have at least one entry in your table already for each year
You have at least one of each week in your table, for any given year. IE: this query returns all numbers from 1 to 52:
SELECT DISTINCT week FROM your_table
Given those constraints, this query should do what you want:
INSERT INTO your_table (id, year, week, totals)
SELECT null, y, w, 0 FROM (
SELECT DISTINCT week w FROM your_table
) weeks
CROSS JOIN
(
SELECT DISTINCT year y FROM your_table
) years
WHERE
(y > (select min(year) from your_table) OR w > (select min(week) from your_table where `year`=y))
AND
(y < (select max(year) from your_table) OR w < (select max(week) from your_table where `year`=y))
AND
NOT EXISTS (select year, week from your_table where `year`=y AND `week`=w)
If condition 2 might not be satisfied - if there are some weeks that are missing in every year, you can replace this line
SELECT DISTINCT week w FROM your_table
with
SELECT
(TWO_1.SeqValue + TWO_2.SeqValue + TWO_4.SeqValue + TWO_8.SeqValue + TWO_16.SeqValue + TWO_32.SeqValue) w
FROM
(SELECT 0 SeqValue UNION ALL SELECT 1 SeqValue) TWO_1
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 2 SeqValue) TWO_2
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 4 SeqValue) TWO_4
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 8 SeqValue) TWO_8
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 16 SeqValue) TWO_16
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 32 SeqValue) TWO_32
HAVING w >= 1 AND w <= 52
Giving this more general case:
INSERT INTO your_table (id, year, week, totals)
SELECT null, y, w, 0 FROM (
SELECT
(TWO_1.SeqValue + TWO_2.SeqValue + TWO_4.SeqValue + TWO_8.SeqValue + TWO_16.SeqValue + TWO_32.SeqValue) w
FROM
(SELECT 0 SeqValue UNION ALL SELECT 1 SeqValue) TWO_1
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 2 SeqValue) TWO_2
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 4 SeqValue) TWO_4
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 8 SeqValue) TWO_8
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 16 SeqValue) TWO_16
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 32 SeqValue) TWO_32
HAVING w >= 1 AND w <= 52
) weeks
CROSS JOIN
(
SELECT DISTINCT year y FROM your_table
) years
WHERE
(y > (select min(year) from your_table) OR w > (select min(week) from your_table where `year`=y))
AND
(y < (select max(year) from your_table) OR w < (select max(week) from your_table where `year`=y))
AND
NOT EXISTS (select year, week from your_table where `year`=y AND `week`=w)
(You can use a similar technique to generate the list of years if condition 1 isn't satisfied, but I'm guessing you don't have entire year-long holes.)
Finally, this could be simplified a bit if you have a unique index on year and week. If you do not yet have such an index, you could create it like so:
ALTER TABLE `your_table` ADD CONSTRAINT date UNIQUE (
`year`,
`week`
)
and if you want, you could remove it when you're done, like so:
ALTER TABLE `your_table` DROP INDEX date;
In that case, the final part of the where clause can be removed:
AND
NOT EXISTS (select year, week from your_table where `year`=y AND `week`=w)
because the INSERT IGNORE will skip any rows for which that unique year/week combination already exists.
Kudos to this answer for the range-generating code: https://stackoverflow.com/a/8349837/160565