Is somehow possible to create select a series in mysql? - mysql

Is possible to select a numerical series or date series in SQL? Like create a table with N rows like 1 to 10:
1
2
3
...
10
or
2010-01-01
2010-02-01
...
2010-12-01

If you install common_schema, you can use the numbers table to easily create queries to output those types of ranges.
For example, these 2 queries will produce the output from your examples:
select n
from common_schema.numbers
where n between 1 and 10
order by n
select ('2010-01-01' + interval n month)
from common_schema.numbers
where n between 0 and 11
order by n

An SQL solution:
SELECT *
FROM (
SELECT 1 as id
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
)

Yep! Both MySQL and Microsoft SQL Server (and others) have a BETWEEN operator. I don't remember off the top of my head if it's inclusive or exclusive, but here's a starting point!
http://www.w3schools.com/sql/sql_between.asp

Related

How to select MAX/MIN in MySQL with dynamic between clause

Assume a table with two columns t (a string with TimeStamps) and v (decimal). For each t I want to query the MAXIMUM of the value v in a certain range defined by the current t.
How can i transfer below statement to proper SQL?
select t, max(v for t between t-2MIN and t+2min) from table_name;
Example:
Assume below table.
t
v
1
3
2
2
3
5
4
4
5
8
6
1
I need an SQL-statement which gives me (for e.g. a width 2: max(v for t between t-2 and t+2)) the following result
t
v
1
5
2
5
3
8
4
8
5
8
6
8
Join the table with itself using the range as the joining condition.
SELECT t1.t, MAX(t2.v) AS max_v
FROM table_name AS t1
JOIN table_name AS t2 ON t2.t BETWEEN t1.t - 2 AND t1.t + 2
GROUP BY t1.t
If you use MySQL 8.x I think you should be able to do it using window functions, but I don't know the proper syntax for this.
In MySql 8 you can use a MAX OVER with rows between a range.
select t
, max(v) over (order by t rows
between 2 preceding and 2 following) v
from table_name

Create histogram given start and end values in SQL

I have an SQL table with "start" and "end" columns: for the sake of simplicity, let's assume they are integers between 1 and 10. I would like to somehow obtain a histogram of the values between "start" and "end".
For instance, given the following rows:
start
end
3
8
4
9
I would like to obtain the following histogram:
time
count
1
0
2
0
3
1
4
2
5
2
6
2
7
2
8
2
9
1
10
0
I really have no idea where to start looking in the SQL syntax to get that result -- maybe an inner join?
You can use a recursive CTE to generate times -- if you don't have a handy tally or numbers table. Then join and aggregate:
with recursive cte as (
select 1 as t
union all
select t + 1
from cte
where t < 10
)
select cte.t,
(select count(*)
from t
where cte.t between t.start and t.end
) as cnt
from cte;
Here is a db<>fiddle.

Calculate (sum,max,avg) comma separated column in mysql

I have a table named POIN and has a column which have comma separated values. I want to calculate each values on the comma separated. It's looks duplicate question because it has answered here. But I want to achieved this using single query instead of create a mysql function.
This is my table looks like :
id poin
------------------
1 1,5,9,3,5
2 2,4,8,5
3 4,7,9,1,5,7
Desired result :
id max min sum avg
--------------------------------------
1 1 9 23 4,6
2 8 2 19 4,75
3 9 1 33 5,5
Actually, I searched this in Google and this forum and didn't get a correct answer yet. I can't show what I have tried so far, because I have no clue where to start.
I don't know what application are you design, but I think it was bad design to store values in comma separated instead of create a table details. You can solved this without using a mysql function actually. First, you need to convert comma separated columns into rows and then you can do some calculation. This query may help you :
select id,max(val) as max,min(val) as min,sum(val) as sum,avg(val) as avg
from(
select id,(substring_index(substring_index(t.poin, ',', n.n), ',', -1)) val
from poin_dtl 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 <-- To make this simple, Create a table with one column that has 100 rows.
where n.n <= 1 + (length(t.poin) - length(replace(t.poin, ',', '')))
order by val asc
) as c_rows -- c_rows = convert comma separated columns into rows
group by id
The results should be like this:
id max min sum avg
--------------------------------------
1 1 9 23 4,6
2 8 2 19 4,75
3 9 1 33 5,5

Making one row many rows

I have some rows like this:
school 4
hotel 2
restaurant 6
I would like to do have this output like this:
school 1
school 2
school 3
school 4
hotel 1
hotel 2
restaurant 1
...
restaurant 6
Is there a MySQL query I can run to output it like this (i.e., the number of rows output corresponds to the number in the second field)?
Thanks for JB Nizet for telling me that MySQL would not be able to do this easily on it's own. It inspired me to write this PHP script:
$result = mysqli_query($dbc,"select abbrev,chapters from books");
while ($output = mysqli_fetch_array($result,MYSQL_ASSOC)) {
for ($i=1; $i<=$output['chapters']; $i++) {
echo "{$output['abbrev']}$i\n";
}
}
In Oracle or PostgreSQL, this kind problem can be solved relatively simple with row generator. Unfortunately, MySQL doesn't have any simple way to generate rows, you should have to do it manually. You have to create a view.
create or replace view generator_16 as
select 0 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 union all select 10 union all select 11 union all
select 12 union all select 13 union all select 14 union all
select 15;
This view has one column n and 16 rows from 0 to 15. With this view, you can write a query like this:
select a.name, b.n
from t a, generator_16 b
where b.n <= a.n and b.n > 0;
Since the rows of view starts from 0, there's additional filter condition b.n > 0 to exclude 0. If you define a view which starts from 1, the additional condition would not necessary.
I assume that the number is small (below 16). If you have to deal the case that the number over 15, then you should define an another view which contains 256 rows:
create or replace view generator_256 as
select (hi.n*16 + lo.n) as n
from generator_16 hi, generator_16 lo;
Then update the query to use generator_256.
For more detailed explanation for generator_16, generator_256 views, reading MySQL Row Generator would help you.

Get last 7 days count even when there are no records

How can I make a SQL query that returns me something like
---------------------
|DATE | Count |
---------------------
|2015/01/07 | 7 |
|2015/01/06 | 0 |
|2015/01/05 | 8 |
|2015/01/04 | 5 |
|2015/01/03 | 0 |
|2015/01/02 | 4 |
|2015/01/01 | 2 |
---------------------
When there are no records for the 6th and 3rd?
You need a table of all the sequence numbers from 0 to 6. This is easy to generate in a simple query, as follows.
SELECT 0 AS seq
UNION ALL SELECT 1 UNION ALL SELECT 2
UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6
Next, let's use this to construct a virtual table of seven dates. For this example, we pick today and the six preceding days.
SELECT DATE(NOW())-INTERVAL seq.seq DAY theday
FROM (
SELECT 0 AS seq
UNION ALL SELECT 1 UNION ALL SELECT 2
UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6
) seq
Then you do your summary query. You didn't say exactly how it goes so I will guess. This one gives you the records from six days ago until today. Today is still in progress.
SELECT DATE(i.item_time) theday
COUNT(*) `count`
FROM items i
WHERE i.item_time >= DATE(NOW()) - INTERVAL 6 DAYS
GROUP BY DATE(i.item_time)
Finally, starting with the list of days, let's LEFT JOIN that summary to it.
SELECT thedays.theday, IFNULL(summary.`count`,0) `count`
FROM (
SELECT DATE(NOW())-INTERVAL seq.seq DAY theday
FROM (
SELECT 0 AS seq
UNION ALL SELECT 1 UNION ALL SELECT 2
UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6
) seq
) thedays
LEFT JOIN (
SELECT DATE(i.item_time) theday
COUNT(*) `count`
FROM items i
WHERE i.item_time >= DATE(NOW()) - INTERVAL 6 DAYS
GROUP BY DATE(i.item_time)
) summary USING (theday)
ORDER BY thedays.theday
It looks complex, but it is simply the combination of three basic queries. Think of it as a sandwich, with bread and cheese and tomato stuck together with an ORDER BY toothpick.
Here's a more thorough writeup. http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/
MariaDB version 10 has built-in virtual tables of cardinal number sequences like seq_0_to_6. This is convenient.
You need to build a dummy dates table and left join your current table against it.
SELECT dummy.date, SUM(IFNULL(yourtable.record,0)) recordcount
FROM dummy
LEFT JOIN yourtable on dummy.date=yourtable.date
GROUP BT dummy.date
please note that I'm replacing nulls with a zero.
One solution is to create a calendar table containing all the dates you need. You can then left join it to your data to get what you are after
First of all you have to use left join, converting NULLs to 0s using the IFNULL function. Try to match your table and use left join.