I have days(working days for the school) field in school_calendar table as given below
select days from school_calendars where id=1;
returns MTWHF as result
I want the output as followed
+---------+
Days
+---------+
| M |
| T |
| W |
| H |
| F |
+---------+
Doing it for a string up to 1000 characters long:-
SELECT SUBSTR(days, aNum, 1) AS aDay
FROM school_calendars
INNER JOIN
(
SELECT 1 + units.aCnt + tens.aCnt * 10 + hundreds.aCnt * 100 AS aNum
FROM
(SELECT 0 AS aCnt 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) units.
(SELECT 0 AS aCnt 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) tens,
(SELECT 0 AS aCnt 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) hundreds
) sub0
ON sub0.aNum <= LENGTH(days)
WHERE id=1;
For a short varchar(7) that can be simplified to
SELECT SUBSTR(days, aNum, 1) AS aDay
FROM school_calendars
INNER JOIN
(
SELECT 1 AS aNum UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
) sub0
ON sub0.aNum <= LENGTH(days)
WHERE id=1;
Related
I have the below table structure.
ID | FromDate | ToDate
1 | 2020-01-02 | 2020-06-01
2 | 2020-08-01 | 2020-12-01
3 | 2020-01-02 | 2020-11-28
4 | 2020-04-01 | 2020-05-28
When I pass 2 input parameters named fromDate and toDate, it should select the relevant records.
E.g. If FromDate = 2020-01-01 and ToDate = 2020-06-01, it should return records with IDs 1 and 4 since those two records are between the dates as the parameters supplied.
I am using the below method at the moment and I feel like it's wasting a lot of resources.
AND ToDate IN (select * from
(select adddate('2000-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) selected_date from
(select 0 i 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 i 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 i 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 i 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 i 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 selected_date between '2021-01-02 00:00:00' and '2022-03-02 00:00:00') ORDER BY name ASC,lt.ID ASC<code>
Is there any other way to achieve this task?
select * from table_name where fromDate>'2020-xx-xx' AND ToDate<'2020-xx-xx';
this query format can get records between insertion dates
Problem
I want to show all users for a specific range lets say from 2020-01-01 to from 2020-01-05.
I have a table user where all users are saved. Imagine following table:
user
id | createdAt
1 2020-01-01
2 2020-01-02
3 2020-01-03
4 2020-01-03
5 2020-01-05
The outcome should be (relating to the above user table):
date | count
2020-01-01 1
2020-01-02 2
2020-01-03 4
2020-01-04 4
2020-01-05 5
What I have so far
I have a query that returns the example above as needed:
SELECT COALESCE((SELECT COUNT(*) FROM user u WHERE DATE(u.createdAt) <= DATE(dates.gen_date) AND u.role = 'USER' GROUP BY dates.gen_date ),0) AS y, dates.gen_date AS x FROM user u, (select * 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 DATE(?) and DATE(?)) AS dates GROUP BY x;
But the execution of this statement is really slow by huge number of users and a large time range.
I thought also of a materialized view but this would be only the last way out.
Database used: MySql 5.7
Test
SELECT (dates.mindate + INTERVAL (t0.n+10*t1.n+100*t2.n+1000*t3.n) DAY) `date`,
COUNT(users.id) `count`
FROM users,
(SELECT ? mindate, ? maxdate) dates,
(select 0 n 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 n 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 n 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 n union select 1 union select 2) t3
WHERE users.createdAt <= (dates.mindate + INTERVAL (t0.n+10*t1.n+100*t2.n+1000*t3.n) DAY)
GROUP BY `date`
HAVING `date` <= dates.maxdate
ORDER BY `date`
It must be less expensive.
I've updated "leave" table:
+--------+---------+---------+-------------+----------+----------+
|ID_LEAVE|ID_WORKER| BEGIN_DATE | END_DATE |
+--------+---------+---------+---------+-------------+-----------+
| 5 | 10 | 2019-03-22 07:00:00 |2019-03-25 15:00:00 |
+--------+---------+---------+-------------+----------+----------+
| 6 | 10 | 2019-03-28 07:00:00 |2019-04-12 15:00:00 |
+--------+---------+---------+-------------+----------+----------+
| 7 | 12 | 2019-03-28 07:00:00 |2019-04-09 15:00:00 |
+--------+---------+---------+-------------+----------+----------+
And "Workers table":
+---------+---------+-------+
|ID_WORKER| FNAME | LNAME |
+---------+---------+-------+
| 10 | MARIO | NEED |
+---------+---------+-------+
| 12 | DARIO | MARCO |
+---------+---------+-------+
I can display all workers (all from "workers table") with leaves times.
What i would like to do?
I want filter phrases of FNAME OR LNAME which is joined with workers table.
What i've tried?
I tried filter phrases of FNAME OR LNAME by code in below:
SELECT leave.ID_LEAVE, leave.ID_WORKER, workers.FNAME, workers.LNAME, leave.BEGIN_DATE, leave.END_DATE,
FROM
(SELECT ADDDATE('1970-01-01', t4 * 10000 + t3 * 1000 + t2 * 100 + t1 * 10 + t0) AS date_value
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) calendar
INNER JOIN leave ON calendar.date_value BETWEEN DATE(leave.BEGIN_DATE) AND DATE(leave.END_DATE)
INNER JOIN workers ON leave.ID_WORKER = workers.ID_WORKER
WHERE NOT WEEKDAY(date_value) IN (5, 6) AND (workers.FNAME LIKE 'Ma' OR workers.LNAME LIKE 'Ma')
GROUP BY ID_LEAVE;
But it shows nothing. When i filter LNAME or FNAME by writing Phrase 'Ma' it would show any content like that:
+--------+---------+---------+-------------+----------+--------------------------
|ID_LEAVE|ID_WORKER| FNAME | LNAME | BEGIN_DATE | END_DATE |
+--------+---------+---------+---------+-------------+--------------------+------
| 5 | 10 | MARIO | NEED |2019-03-22 07:00:00 |2019-03-25 15:00:00 |
+--------+---------+---------+-------------+----------+--------------------------
| 6 | 10 | MARIO | NEED |2019-03-28 07:00:00 |2019-04-09 15:00:00 |
+--------+---------+---------+-------------+----------+--------------------------
Any ideas? What should I change in that code? Can i count for your support? Thx for any advice.
You missed '%' symbol in the filter condition.
Also you have to use UPPER() function on the field and the data to be compared,
so that filtering will be case insensitive
SELECT leave.ID_LEAVE, leave.ID_WORKER, workers.FNAME, workers.LNAME, leave.BEGIN_DATE, leave.END_DATE,
FROM
(SELECT ADDDATE('1970-01-01', t4 * 10000 + t3 * 1000 + t2 * 100 + t1 * 10 + t0) AS date_value
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) calendar
INNER JOIN leave ON calendar.date_value BETWEEN DATE(leave.BEGIN_DATE) AND DATE(leave.END_DATE)
INNER JOIN workers ON leave.ID_WORKER = workers.ID_WORKER
WHERE NOT WEEKDAY(date_value) IN (5, 6) AND (UPPER(workers.FNAME) LIKE '%MA%' OR UPPER(workers.LNAME) LIKE '%MA%')
GROUP BY ID_LEAVE;
You can try this query
select leave.ID_LEAVE,
Workers.ID_WORKER,
Workers.FNAME,
Workers.LNAME,
leave.BEGIN_DATE,
leave.END_DATE from Workers
join leave on Workers.ID_WORKER = leave.ID_WORKER
where Workers.FNAME Like '%ma%';
well, this might be easy but I can't seem to find any solution
I have this working query
SELECT
count(*), TIMESTAMPDIFF(DAY, buydate, NOW()) daydif
FROM
order_item
WHERE
buydate > NOW() - INTERVAL 4 DAY
GROUP BY daydif
ORDER BY daydif ASC
but the result is skipping the days with zero item
+----------+--------+
| count(*) | daydif |
+==========+========+
| 5 | 0 |
+----------+--------+
| 9 | 1 |
+----------+--------+
| 2 | 3 |
+----------+--------+
I want to get this result
+----------+--------+
| count(*) | daydif |
+==========+========+
| 5 | 0 |
+----------+--------+
| 9 | 1 |
+----------+--------+
| 0 | 2 | //I want this row in the returned results
+----------+--------+
| 2 | 3 |
+----------+--------+
Update:
From the answers and further search it seems I'm forced to create a helper table and join with it. It's so frustrating that I can't find a sequence producing function like that of MariaDB as mentioned in the comments. Any hint for a more elegant way?
Probably you do not have any items, where the difference is 2 days. SQL can't create something, that is not there.
What you can do is to have a table with a single field of long list of numbers from 0 to up to whatever the difference can be (in this case 4), let's call it tbl_diff, and the field is diff. Left join tbl_diff on your existing query to fill in the gaps. Note, that I also changed the count() to count those records only, where buydate is set.
SELECT
count(buydate), TIMESTAMPDIFF(DAY, buydate, NOW()) daydif
FROM
tbl_diff
LEFT JOIN
order_item on tbl_diff.diff=TIMESTAMPDIFF(DAY, buydate, NOW())
WHERE
diff<=4
GROUP BY daydif
ORDER BY daydif ASC
If you do not want to create a helper table, then you can operate with a series of unions because you only want 5 number (0 to 4):
SELECT
count(buydate), TIMESTAMPDIFF(DAY, buydate, NOW()) daydif
FROM
(SELECT 0 as diff FROM dual
UNION
SELECT 1 FROM dual
UNION
SELECT 2 FROM dual
UNION
SELECT 3 FROM dual
UNION
SELECT 4 FROM dual) tbl_diff
LEFT JOIN
order_item on tbl_diff.diff=TIMESTAMPDIFF(DAY, buydate, NOW())
GROUP BY daydif
ORDER BY daydif ASC
And as suggested by Paul Spiegel I ended up using some helper tables,
for future reference and better help here's the code to I used to generate the tables
create table `stat_helper_calendar` (
`date` DATE NOT NULL,
PRIMARY KEY (`date`)
)
select date_add('2015-01-01', INTERVAL t3.c*1000 + t2.c*100 + t1.c*10 + t0.c DAY) as `date`
from
(select 0 c 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) t0,
(select 0 c 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) t1,
(select 0 c 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) t2,
(select 0 c 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) t3
And this one for integers
CREATE TABLE `stat_helper_num` (
`num` INT NOT NULL,
PRIMARY KEY (`num`)
) SELECT (t4.c * 10000 + t3.c * 1000 + t2.c * 100 + t1.c * 10 + t0.c) AS `num` FROM
(SELECT 0 c 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) t0,
(SELECT 0 c 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) t1,
(SELECT 0 c 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) t2,
(SELECT 0 c 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) t3,
(SELECT 0 c 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) t4
I want to receive results like the sample output.
Listed each day beween two dates and the number of online posts from a company.
For example:
TABLE
id | online_From | online_To | comp_id
11 | 2014-04-02 | 2014-04-05 | 20
12 | 2014-04-11 | 2014-04-16 | 21
13 | 2014-04-03 | 2014-04-07 | 20
17 | 2014-04-29 | 2014-04-30 | 23
19 | 2014-04-04 | 2014-04-11 | 20
I want to receive:
SAMPLE OUTPUT
2014-04-01 | 0
2014-04-02 | 1
2014-04-03 | 2
2014-04-04 | 3
2014-04-05 | 3
2014-04-06 | 2
2014-04-07 | 2
2014-04-08 | 1
2014-04-09 | 1
2014-04-10 | 1
2014-04-11 | 1
2014-04-12 | 0
2014-04-13 | 0
…
2014-05-07 | 0
I used this mysql code to list the requested dates (and also split into y/m/d/w for generate charts):
select selected_date, year(selected_date) as y, day(selected_date) as d, week(selected_date) as w, month(selected_date) as m from
(select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i)
selected_date from (select 0 i 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 i 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 i 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 i 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 i 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 selected_date between '2014-04-01' and '2014-05-07'
GROUP BY y,m,d ORDER BY y ASC, m ASC, d ASC
2014-04-01|2014 |1 |14 |4 |0
2014-04-02|2014 |2 |14 |4 |1
2014-04-03|2014 |3 |14 |4 |2
…
This comes from: How to get list of dates between two dates in mysql select query
I tried a lot of things but I never became the hoped result.
I want to loop trough the dates and check the number of posts that where online that day.
Hopefully somebody can help me!
Thanks
This should get you started if you want a SQL-only answer. Have called your table comp_table...
select d.selected_date,d.d,d.m,d.y,
count(ct.id)
from
(
select selected_date, year(selected_date) as y, day(selected_date) as d, week(selected_date) as w, month(selected_date) as m from
(select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i)
selected_date from (select 0 i 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 i 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 i 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 i 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 i 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 selected_date between '2014-04-01' and '2014-05-07'
GROUP BY selected_date
) d
left outer join comp_table ct on d.selected_date between ct.online_From and ct.online_To
group by d.selected_date
order by 1 asc
;
You might be better off periodically populating an all_dates table to use in place of the inline view though. Would be more performant!
EDIT
If you want to maintain 'gimme all dates even if they have a 0 count' yet filter the results e.g. by the comp_id column then simply change the left outer join to include your filter by clause. For example:
select d.selected_date,d.d,d.m,d.y,
count(ct.id)
from
(
select selected_date, year(selected_date) as y, day(selected_date) as d, week(selected_date) as w, month(selected_date) as m from
(select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i)
selected_date from (select 0 i 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 i 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 i 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 i 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 i 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 selected_date between '2014-04-01' and '2014-05-07'
GROUP BY selected_date
) d
left outer join comp_table ct
on d.selected_date between ct.online_From and ct.online_To
and ct.comp_id = 20
group by d.selected_date
order by 1 asc
;
If you have not already installed it, I recommend you install common_schema. Once that's installed you can use the numbers table to simplify your query.
For example this query should give you the desired output:
select days.day,count(distinct your_table.id) from
(
select '2014-04-01' + interval n day AS day
from common_schema.numbers
having day between '2014-04-01' and '2014-05-07'
) days
left outer join your_table on your_table.online_From <= days.day and your_table.online_To >= days.day
group by days.day
order by days.day