How to show 0 when no data - mysql

I want to show 0 or something i want when no data.And this is my query.
SELECT `icDate`,IFNULL(SUM(`icCost`),0) AS icCost
FROM `incomp`
WHERE (`icDate` BETWEEN "2016-01-01" AND "2016-01-05")
AND `compID` = "DDY"
GROUP BY `icDate`
And this is result of this query.
icDate | icCost
--------------------------
2016-01-01 | 1000.00
2016-01-02 | 2000.00
2016-01-03 | 3000.00
2016-01-04 | 4000.00
2016-01-05 | 5000.00
If every day i want to show data it have a data,It wasn't problem.But it have some day,It don't have data. This will not show this day, Like this.
icDate | icCost
--------------------------
2016-01-01 | 1000.00
2016-01-02 | 2000.00
2016-01-04 | 4000.00
2016-01-05 | 5000.00
But i want it can show data like this.
icDate | icCost
--------------------------
2016-01-01 | 1000.00
2016-01-02 | 2000.00
2016-01-03 | 0.00
2016-01-04 | 4000.00
2016-01-05 | 5000.00
How to write query to get this answer.Thank you.

I made a simulation but I could not see your problem. I created a table for teste and after insert data this was my select. But the test was normal!
SELECT icDate,
format(ifnull(sum(icCost), 0),2) as icCost,
count(icDate) as entries
FROM incomp
WHERE icDate BETWEEN '2016-01-01' AND '2016-01-05'
AND compID = 'DDY'
group by icDate;
This is result of my test, exported in csv file:
icDate | icCost | entries
----------------------------------
2016-01-01 | 8,600.00 | 8
2016-01-02 | 5,600.00 | 4
2016-01-03 | 5,400.00 | 3
2016-01-04 | 0.00 | 1
2016-01-05 | 7,050.00 | 7
Does the icCost field is setting with null value ​​or number zero? Remember some cases that null values ​​setted may be different from other one as empty.

I found the answers, It worked with calendar table.
SELECT tbd.`db_date`,
(SELECT IFNULL(SUM(icCost),0) AS icCost
FROM `incomp`
WHERE icDate = tbd.db_date
AND compID = "DDY"
)AS icCost
FROM tb_date AS tbd
WHERE (tbd.`db_date` BETWEEN "2016-01-01" AND "2016-01-05")
GROUP BY tbd.`db_date`
LIMIT 0,100
Simply, But work.

Ok, you can investigate if you table is filled correctly every day. First you can create a temporary table like this:
CREATE TEMPORARY TABLE myCalendar (
CalendarDate date primary key not null
);
So, after you need to fill this table with valid days. For it, use this procedure:
DELIMITER $$
CREATE PROCEDURE doWhile()
BEGIN
# IF YOU WANT TO USE CURRENT MONTH
#SET #startCount = ADDDATE(LAST_DAY(SUBDATE(CURDATE(), INTERVAL 1 MONTH)), 1);
#SET #endCount = LAST_DAY(sysdate());
# USE TO SET A DATE
SET #startCount = '2016-01-01';
SET #endOfCount = '2016-01-30';
WHILE #startCount <= #endOfCount DO
INSERT INTO myCalendar (CalendarDate) VALUES (#startCount);
SET #startCount = date_add(#startCount, interval 1 day);
END WHILE;
END$$;
DELIMITER ;
You need to run this procedure by command:
CALL doWhile();
Now, run the follow:
SELECT format(ifnull(sum(t1.icCost), 0),2) as icCost,
ifnull(t1.icDate, 'Not found') as icDate,
t2.CalendarDate as 'For the day'
from incomp t1
right join myCalendar t2 ON
t2.CalendarDate = t1.icDate group by t2.CalendarDate;
I think this will help you to find a solution, for example, if exists a register for a day or not.
I hope this can help you!
[]'s

Sorry for my earlier answer. I gave a MSSQL answer instead of a MySQL answer.
You need a calendar table to have a set of all dates in your range. This could be a permanent table or a temporary table. Either way, there are a number of ways to populate it. Here is one way (borrowed from here):
set #beginDate = '2016-01-01';
set #endDate = '2016-01-05';
create table DateSequence(Date Date);
insert into DateSequence
select * 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 #beginDate and #endDate
Your best bet is probably to make a permanent table that has every possible date. That way you only have to populate it once and it's ready to go whenever you need it.
Now you can outer join the calendar table with your inComp table.
set #beginDate date = '2016-01-01'
set #endDate date = '2016-01-05'
select d.Date,
sum(ifnull(i.icCost, 0)) inComp
from DateSequence d
left outer join inComp i on i.icDate = d.Date
where d.Date between #beginDate and #endDate
and i.compID = 'DDY'
group by d.date
order by d.Date;

Related

Get 1 hour incrementation for room availability MySQL

I am trying to get the availability of the rooms in my hotel by 1 hour incrementation. So for example, if the room is booked from 9 AM to 10 AM, and from 12 AM to 3 PM, I am trying to get the 1 hour increments of all other times between available_from to available_to
I am able to left join on the table and get the room availability but just not the time slots.
Here is my related schema:
Hotel:
Id | name
Reservation:
Id | hotel_id | room_id | start | end | status
Rooms:
Id | hotel_id | name | number | available_from | available_to
Here is the query I have so far:
SELECT r.id, r.name, r.number, r.type, r.rating
FROM rooms r
LEFT OUTER JOIN reservations res ON res.room_id = r.id
AND CURRENT_TIMESTAMP BETWEEN r.available_from AND r.available_to
GROUP BY r.id, r.type
Example:
(This is the array I am trying to get back from database. Ignore the property names):
[{"roomNumber":1,"availableTimes":["2019-01-01 00:00:00","2019-01-01 01:00:00","2019-01-01 02:00:00","2019-01-01 03:00:00","2019-01-01 04:00:00","2019-01-01 05:00:00","2019-01-01 06:00:00","2019-01-01 07:00:00","2019-01-01 08:00:00","2019-01-01 09:00:00","2019-01-01 10:00:00","2019-01-01 11:00:00","2019-01-01 12:00:00","2019-01-01 13:00:00","2019-01-01 14:00:00","2019-01-01 15:00:00","2019-01-01 16:00:00","2019-01-01 17:00:00","2019-01-01 18:00:00","2019-01-01 19:00:00","2019-01-01 20:00:00","2019-01-01 21:00:00","2019-01-01 22:00:00","2019-01-01 23:00:00"]}]
I tried the following:
SELECT free_from, free_until
FROM (
SELECT a.end AS free_from,
(SELECT MIN(c.start)
FROM reservations c
WHERE c.start > a.end) as free_until
FROM reservations a
WHERE NOT EXISTS (
SELECT 1
FROM reservations b
WHERE b.start BETWEEN a.end AND a.end + INTERVAL 1 HOUR
)
AND a.end BETWEEN '2019-01-03 09:00' AND '2019-01-03 21:00'
) as d
ORDER BY free_until-free_from
LIMIT 0,3;
But I get one row returned only with 1 result which is incorrect as well. How can I solve this problem?
Sample Data:
Hotel:
1 | Marriott
Reservation:
1 | 1 | 1 | 2019-01-03 15:00:00 | 2019-01-03 17:00:00 | Confirmed
1 | 1 | 1 | 2019-01-03 18:00:00 | 2019-01-03 20:00:00 | Confirmed
Rooms:
1 | 1 | "Single" | 528 | 09:00:00 | 21:00:00
Expected Result
Room Id | Room name | Available Times
1 | "Single" | 2019-01-03 09:00:00, 2019-01-03 10:00:00, 2019-01-03 11:00:00, 2019-01-03 12:00:00, 2019-01-03 13:00:00, 2019-01-03 14:00:00, 2019-01-03 17:00:00, 2019-01-03 20:00:00, 2019-01-03 21:00:00, 2019-01-03 22:00:00, 2019-01-03 23:00:00, 2019-01-03 24:00:00
If you add a Times_Slots table to your data base as shown in this SQL Fiddle:
CREATE TABLE Time_Slots
(`Slot` time);
INSERT INTO Time_Slots
(`Slot`)
VALUES
('00:00:00'),
('01:00:00'),
('02:00:00'),
('03:00:00'),
('04:00:00'),
('05:00:00'),
('06:00:00'),
('07:00:00'),
('08:00:00'),
('09:00:00'),
('10:00:00'),
('11:00:00'),
('12:00:00'),
('13:00:00'),
('14:00:00'),
('15:00:00'),
('16:00:00'),
('17:00:00'),
('18:00:00'),
('19:00:00'),
('20:00:00'),
('21:00:00'),
('22:00:00'),
('23:00:00');
Then the following query will provide room availability for all rooms with reservations:
Query 1:
select r.id
, r.Name
, res_date + interval t.slot hour_second available
from Time_Slots t
join Rooms r
on t.Slot between r.available_from and r.available_to
join (select distinct room_id, date(start) res_date from Reservation) res
on res.room_id = r.id
where (r.id, res_date + interval t.slot hour_second) not in (
select r.room_id
, date(r.start) + interval t.slot hour_second Reserved
from Time_Slots t
join Reservation r
on r.start <= date(r.end) + interval t.slot hour_second
and date(r.start) + interval t.slot hour_second < r.end)
This query works by first selecting the available slots from Times_Slots for each day that has at least one reservation for that room, and then filtering out the reserved time slots.
Results:
| id | Name | available |
|----|--------|----------------------|
| 1 | Single | 2019-01-03T09:00:00Z |
| 1 | Single | 2019-01-03T10:00:00Z |
| 1 | Single | 2019-01-03T11:00:00Z |
| 1 | Single | 2019-01-03T12:00:00Z |
| 1 | Single | 2019-01-03T13:00:00Z |
| 1 | Single | 2019-01-03T14:00:00Z |
| 1 | Single | 2019-01-03T17:00:00Z |
| 1 | Single | 2019-01-03T20:00:00Z |
| 1 | Single | 2019-01-03T21:00:00Z |
In your sample output you indicated that the room was available for 2019-01-03 22:00:00, 2019-01-03 23:00:00, 2019-01-03 24:00:00, however those times are after the Room tables defined availability block, so my query excluded those times.
The first problem you have is your schema setup is poor. You don't have good data normalization. 1) Rename the fields for better clarity. 2) Change these two tables to be like this:
Reservation:
Res_ID | hotel_id | room_id | res_start | res_end | status
Rooms:
Room_ID | hotel_id | room_name | room_number | available_from | available_to
You will need a table that has your time slots defined. You can do it with a CTE and then CROSS JOIN it with your rooms. This is one of the few cases where the CROSS JOIN is useful.
Now do your query like this:
WITH timeslots AS (
SELECT CURRENT_DATE() AS time_slot UNION
SELECT CURRENT_DATE() + 1/24 UNION
SELECT CURRENT_DATE() + 2/24 UNION
SELECT CURRENT_DATE() + 3/24 UNION
SELECT CURRENT_DATE() + 4/24 UNION
SELECT CURRENT_DATE() + 5/24 UNION
SELECT CURRENT_DATE() + 6/24 UNION
SELECT CURRENT_DATE() + 7/24 UNION
SELECT CURRENT_DATE() + 8/24 UNION
SELECT CURRENT_DATE() + 9/24 UNION
SELECT CURRENT_DATE() + 10/24 UNION
SELECT CURRENT_DATE() + 11/24 UNION
SELECT CURRENT_DATE() + 12/24 UNION
SELECT CURRENT_DATE() + 13/24 UNION
SELECT CURRENT_DATE() + 14/24 UNION
SELECT CURRENT_DATE() + 15/24 UNION
SELECT CURRENT_DATE() + 16/24 UNION
SELECT CURRENT_DATE() + 17/24 UNION
SELECT CURRENT_DATE() + 18/24 UNION
SELECT CURRENT_DATE() + 19/24 UNION
SELECT CURRENT_DATE() + 20/24 UNION
SELECT CURRENT_DATE() + 21/24 UNION
SELECT CURRENT_DATE() + 22/24 UNION
SELECT CURRENT_DATE() + 23/24 )
SELECT r.id, r.name, r.number, r.type, r.rating,
t.time_slot AS time_slot_open,
t.time_slot + 1/24 AS time_slot_close,
res.Res_ID
FROM rooms r
CROSS JOIN timeslots t
LEFT JOIN reservation res ON res.hotel_id = r.hotel_id AND res.room_id = r.room_id
AND time_slot_open >= res.res_start AND time_slot_open < res.res_close
That will get you a list of all your hotel rooms with 24 records each. If there is a reservation in that room, then it will show you the reservation ID for that slot. From here, you can either use the data as is, or you can further put this into its own CTE and just select everything from it where the reservation ID is null. You can also join or look up other data about the reservation based on that ID.
Update
If you run a version of MySQL before 8.0, the WITH clause is not supported (See: How do you use the "WITH" clause in MySQL?). You'll have to make this a subquery like this:
SELECT r.id, r.name, r.number, r.type, r.rating,
t.time_slot AS time_slot_open,
t.time_slot + 1/24 AS time_slot_close,
res.Res_ID
FROM rooms r
CROSS JOIN (SELECT CURRENT_DATE() AS time_slot UNION
SELECT CURRENT_DATE() + 1/24 UNION
SELECT CURRENT_DATE() + 2/24 UNION
SELECT CURRENT_DATE() + 3/24 UNION
SELECT CURRENT_DATE() + 4/24 UNION
SELECT CURRENT_DATE() + 5/24 UNION
SELECT CURRENT_DATE() + 6/24 UNION
SELECT CURRENT_DATE() + 7/24 UNION
SELECT CURRENT_DATE() + 8/24 UNION
SELECT CURRENT_DATE() + 9/24 UNION
SELECT CURRENT_DATE() + 10/24 UNION
SELECT CURRENT_DATE() + 11/24 UNION
SELECT CURRENT_DATE() + 12/24 UNION
SELECT CURRENT_DATE() + 13/24 UNION
SELECT CURRENT_DATE() + 14/24 UNION
SELECT CURRENT_DATE() + 15/24 UNION
SELECT CURRENT_DATE() + 16/24 UNION
SELECT CURRENT_DATE() + 17/24 UNION
SELECT CURRENT_DATE() + 18/24 UNION
SELECT CURRENT_DATE() + 19/24 UNION
SELECT CURRENT_DATE() + 20/24 UNION
SELECT CURRENT_DATE() + 21/24 UNION
SELECT CURRENT_DATE() + 22/24 UNION
SELECT CURRENT_DATE() + 23/24 ) t
LEFT JOIN reservation res ON res.hotel_id = r.hotel_id AND res.room_id = r.room_id
AND time_slot_open >= res.res_start AND time_slot_open < res.res_close
Plan A (one row per hour)
Get rid of the T and Z; MySQL does not understand that syntax.
Your motel is in a single timezone, correct? Then using either DATETIME or TIMESTAMP is equivalent.
For a 3-hour reservation, make 3 rows. (It is likely to be messier to work with ranges.)
Alas, you are using MySQL, not MariaDB; the latter has automatic sequence generators. Example: The pseudo-table named seq_0_to_23 acts like a table prepopulated with the numbers 0 through 23.
Finding available times requires having a table with all possible hours for all days, hence the note above.
Either do arithmetic or LEFT for hours:
Since LEFT is simple and straightforward,
I will discuss it:
mysql> SELECT NOW(), LEFT(NOW(), 13);
+---------------------+-----------------+
| NOW() | LEFT(NOW(), 13) |
+---------------------+-----------------+
| 2019-01-03 13:43:56 | 2019-01-03 13 |
+---------------------+-----------------+
1 row in set (0.00 sec)
The second column shows a string that could be used for indicating the 1pm hour on that day.
Plan B (ranges)
Another approach uses ranges. However, the processing is complex since all hours are always associated with either a reservation or with "available". The code gets complex, but the performance is good: http://mysql.rjweb.org/doc.php/ipranges
Plan C (bits)
The table involves a date (no time), plus a MEDIUMINT UNSIGNED which happens to be exactly 24 bits. Each bit represents one hour of the day.
Use various boolean operations:
| (OR) the bits together to see what hours are assigned.
0xFFFFFF & ~hours to see what is available.
BIT_COUNT() to count the bits (hours).
While it is possible in SQL to identify which hours a room is available, it may be better to do that in your client code. I assume you have a PHP/Java/whatever frontend!
etc.
More?
Would you like to discuss any of these in more detail?
You need to join the rooms table with a table of timeslots (24 rows). This will generate a list of all possible timeslots for a given room. Filtering out not-available time slots is trivial:
SELECT rooms.id, rooms.name, TIMESTAMP(checkdates.checkdate, timeslots.timeslot) AS datetimeslot
FROM rooms
INNER JOIN (
SELECT CAST('00:00' AS TIME) AS timeslot UNION
SELECT CAST('01:00' AS TIME) UNION
SELECT CAST('02:00' AS TIME) UNION
SELECT CAST('03:00' AS TIME) UNION
SELECT CAST('04:00' AS TIME) UNION
SELECT CAST('05:00' AS TIME) UNION
SELECT CAST('06:00' AS TIME) UNION
SELECT CAST('07:00' AS TIME) UNION
SELECT CAST('08:00' AS TIME) UNION
SELECT CAST('09:00' AS TIME) UNION
SELECT CAST('10:00' AS TIME) UNION
SELECT CAST('11:00' AS TIME) UNION
SELECT CAST('12:00' AS TIME) UNION
SELECT CAST('13:00' AS TIME) UNION
SELECT CAST('14:00' AS TIME) UNION
SELECT CAST('15:00' AS TIME) UNION
SELECT CAST('16:00' AS TIME) UNION
SELECT CAST('17:00' AS TIME) UNION
SELECT CAST('18:00' AS TIME) UNION
SELECT CAST('19:00' AS TIME) UNION
SELECT CAST('20:00' AS TIME) UNION
SELECT CAST('21:00' AS TIME) UNION
SELECT CAST('22:00' AS TIME) UNION
SELECT CAST('23:00' AS TIME)
) AS timeslots ON timeslots.timeslot >= rooms.available_from
AND timeslots.timeslot < rooms.available_to
CROSS JOIN (
SELECT CAST('2019-01-03' AS DATE) AS checkdate
) AS checkdates
WHERE NOT EXISTS (
SELECT 1
FROM reservations
WHERE room_id = rooms.id
AND TIMESTAMP(checkdates.checkdate, timeslots.timeslot) >= `start`
AND TIMESTAMP(checkdates.checkdate, timeslots.timeslot) < `end`
)
Demo on DB Fiddle
The above query checks availability for one date (2019-01-03). For multiple dates simply add them to checkdates.

MySQL count day of week between two dates over a table

I have a booking table with the following columns:
id, start_date, end_date
I want to know which days have had the most bookings over my dataset.
I can use dayofweek() on the start date and group by this also and use a count(*). But I also want to include the days between the start of booking and end.
An example output wouldbe
dayofweek count(*)
1 1
2 1
3 1
4 2
5 3
6 3
7 1
for the following set
id start_date end_date
1 2017-10-01 2017-10-07
2 2017-10-04 2017-10-07
3 2017-10-06 2017-10-08
I am assuming you wish to know something like how many rooms are filled for each date for the duration between the start and end. The"trick" here is that a long period between start/end will repeat the day or week and/or that the end day of week might be smaller than the start day of week. So, I have:
generated a list of 100,000 dates (1 per row)
joined those dates between the start/end of your table
converted each joined rows to a day of week number to be counted
left joined to a list of 1 to 7, and counted the rows of step 3
NOTE: if the end_date is a "check out date" then it may be necessary to deduct 1 day from each record to compensate (which is not done below).
This approach is available for review here at SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE Table1
(`id` int, `start_date` datetime, `end_date` datetime)
;
INSERT INTO Table1
(`id`, `start_date`, `end_date`)
VALUES
(1, '2017-09-21 00:00:00', '2017-10-07 00:00:00'), ## added this row
(1, '2017-10-01 00:00:00', '2017-10-07 00:00:00'),
(2, '2017-10-04 00:00:00', '2017-10-07 00:00:00'),
(3, '2017-10-06 00:00:00', '2017-10-08 00:00:00')
;
Query:
set #commence := str_to_date('2000-01-01','%Y-%m-%d')
select
w.dy
, count(t.wdy)
from (
select 1 dy union all select 2 dy union all select 3 dy union all
select 4 dy union all select 5 dy union all select 6 dy union all select 7 dy
) w
left join (
select DAYOFWEEK(cal.dy) wdy
from (
select adddate( #commence ,t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) dy
from ( select 0 i 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
cross join (select 0 i 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
cross join (select 0 i 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
cross join (select 0 i 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
cross join (select 0 i 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
) cal
INNER JOIN Table1 t on cal.dy between t.start_date and t.end_date
) t on w.dy = t.wdy
group by
w.dy
Results:
| dy | count(t.wdy) |
|----|--------------|
| 1 | 4 |
| 2 | 3 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
| 6 | 6 |
| 7 | 6 |
Also see: How to get list of dates between two dates in mysql select query where the accepted answer is the basis for the set of cross joins that produces 100,000 dates starting from a nominated date. I modified that however for syntax (explicit cross join syntax), a parameter as start point, and use of union all for efficiency.
You can accomplish this with a recursive table:
WITH cte AS
(
SELECT DATE_ADD(start_date INTERVAL 1 DAY) AS date, end_date, DAYOFWEEK(start_date) AS dw from bookings
UNION ALL
SELECT DATE_ADD(start_date INTERVAL 1 DAY), end_date, DAYOFWEEK(date)
FROM cte WHERE date <= end_date
)
SELECT COUNT(*), dw FROM cte GROUP BY dw

Select missing dates from a table in MySQL

I'm thinking about what would be the best way to do what I'm trying to accomplish. As the title says, I have a table with missing dates in mysql, like this:
+------------+
| 2015-09-01 |
| 2015-09-03 |
| 2015-09-05 |
| 2015-09-06 |
| 2015-09-07 |
+------------+
I want to select them to assign them another column with a null value, so if I also wanted to select the missing dates (days 2 and 4 in my case), which are my options? I thought about making a "calendar" table and select both with a left join and using not in, but that means I'd have to fill it with a TON of dates (in case someone for some reason wants to select data from the year 2500 or 1800, and I don't want that).
I don't like the option of using a calendar table because of performance stuff, but it also brings another question: if I still used a calendar table, and restricted people from selecting dates in the past or in the future (thus freeing me from having to put hundreds of years of margin in case some crazy soul decided to select stuff from there), how could I make the table fill itself so it has a date for the current date without me having to insert the current date manually?
Can a trigger/function run itself everyday and insert the current date there?
Edit: My intention is using the table for chartjs, so it can have "holes" where there's missing data (with null values).
Check this:
select this_date,given_dates from
(SELECT ADDDATE('2015-09-01', INTERVAL #i:=#i+1 DAY) AS this_date
FROM (
SELECT a.a
FROM (SELECT 0 AS a 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) AS a
CROSS JOIN (SELECT 0 AS a 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) AS b
CROSS JOIN (SELECT 0 AS a 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) AS c
) a
JOIN (SELECT #i := -1) r1
WHERE
#i < DATEDIFF('2018-01-30', '2015-09-01')) as a
left JOIN
(select given_dates from
(select '2015-09-01' as given_dates union all
select '2015-09-03' union all
select '2015-09-05' union all
select '2015-09-06' union all
select '2015-09-07') as a) as b
on a.this_date = b.given_dates
sample Result:
2015-09-01 2015-09-01
2015-09-02
2015-09-03 2015-09-03
2015-09-04
2015-09-05 2015-09-05
2015-09-06 2015-09-06
2015-09-07 2015-09-07
2015-09-08
2015-09-09
2015-09-10
2015-09-11
You can get easily the null value of it.

How to make query return 0 instead of empty set if there is no result

How can i make this query to return a row with 0 value if there is no value for each date
SELECT COUNT(id) FROM `panel_messages` WHERE `sent_by` = 'root'
AND `send_date` IN ("1395-4-25","1395-4-24","1395-4-23","1395-4-22","1395-4-21","1395-4-20","1395-4-19")
GROUP BY `send_date`
ORDER BY `send_date` DESC
My expected result is 7 rows like this :
| row1 |
| row2 |
| row3 |
| row4 |
| row5 |
| row6 |
| row7 |
and if there is no result for one of the rows i want it to be 0 which is default value :
| 2 |
| 0 |
| 0 |
| 2 |
| 0 |
| 3 |
| 1 |
But right now i just get 4 rows because if there is no result my query doesn't return anything :
| 2 |
| 2 |
| 3 |
| 1 |
SQL fiddle : http://sqlfiddle.com/#!9/a07486/3
Please give it a try:
SELECT
COALESCE(YT.total,t.total) AS cnt
FROM
(SELECT 0 AS total) t
LEFT JOIN
(
SELECT
COUNT(id) AS total
FROM `panel_messages`
WHERE `sent_by` = 'root'
AND `send_date` IN ("1395-4-25","1395-4-24","1395-4-23","1395-4-22","1395-4-21","1395-4-20","1395-4-19")
GROUP BY `send_date`
ORDER BY `send_date` DESC
) YT
ON 1=1;
Note:
A dummy row has been created with value 0.
Later doing a LEFT JOIN between this dummy table and your query
And finally using COALESCE you can achieve the default count 0 if your main query doesn't return anything.
EDIT:
Query:
SELECT
COALESCE(YT.count,0) AS count
FROM
(
SELECT ADDDATE('1395-01-01', INTERVAL #i:=#i+1 DAY) AS DAY
FROM (
SELECT a.a
FROM (SELECT 0 AS a 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) AS a
CROSS JOIN (SELECT 0 AS a 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) AS b
CROSS JOIN (SELECT 0 AS a 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) AS c
) a
JOIN (SELECT #i := -1) r1
) dateTable
LEFT JOIN
(
SELECT
send_date,
COUNT(id) AS count
FROM
`panel_messages`
WHERE
`sent_by` = 'root'
AND `send_date` IN (
"1395-4-25",
"1395-4-24",
"1395-4-23",
"1395-4-20"
)
GROUP BY
`send_date`
ORDER BY
`send_date` DESC
) AS YT
ON dateTable.DAY = YT.send_date
WHERE dateTable.DAY IN ('1395-04-25','1395-04-24','1395-04-23','1395-04-20');
In order to get zero count for the dates which don't exist you need to create a temporary table where all the dates (under a certain range) reside.
Then making a left join between the date field of this temporary table and send_date field of your table would do the job done almost.
Finally you need to use COALESCE to get 0 if the count is NULL.
WORKING DEMO
try this :
SELECT sent_by ,"1395-4-25" as `SEND DATE`,COUNT(*) FROM `panel_messages` WHERE `sent_by` = 'root' AND `send_date` = "1395-4-25"
union
SELECT sent_by ,"1395-4-24" as `SEND DATE`,COUNT(*) FROM `panel_messages` WHERE `sent_by` = 'root' AND `send_date` = "1395-4-24"
union
SELECT sent_by ,"1395-4-23" as `SEND DATE`,COUNT(*) FROM `panel_messages` WHERE `sent_by` = 'root' AND `send_date` = "1395-4-23"
ORDER BY `SEND DATE` DESC
in this case when date is not found the count(*) return 0; but in the first return null add the 4 select statement and it will return 7 rows now it work but it can be better if i found onother solution i'm going back here
onother answer what you are trying to do is impossible without the union :
but you can try some think else
create a temporary table that contain your date
create Table temporary (
send_date date
);
insert INTO temporay("1395-4-25"),("1395-4-24"),("1395-4-23"),("1395-4-22"),("1395-4-21"),("1395-4-20"),("1395-4-19")
than do select with rigth join between your table and this one now you will have record for the date that don't have send_by
panel_messages.sent_by | panel_messages.send_date | temporary.send_date
root "1395-4-25" "1395-4-25"
root "1395-4-25" "1395-4-25"
null null "1395-4-24"
null null "1395-4-23"
root "1395-4-19" "1395-4-19"
.
.
.
now you count how much message in every day all i did is create a result that can return what you need :
Try this select after you create the temporary table
SELECT temporary.send_date, count(sender_by)
from panel_messages RIGTH JOIN temporary ON (temporary.send_date = panel_messages.send_date)
where
panel_messages.sent_by like 'root'
group by temporary.send_date
ORDER BY send_date DESC;

(i) Create a table from existing one and (ii) display detailed data for which total is stored in table

From a table t1
product_id | quantity | total_amount
I want to create a table t2 with the columns
id | product_id | total_amount/quantity AS product_price
I want each record (t2.product_id, t2.product_price) to be shown t1.quantity times for the respective product:
1 | 400
1 | 400
1 | 400
2 | 75
2 | 75
Could you please tell me, how can I do this using bare SQL?
This seems to create the table, you are looking for:
CREATE TABLE T2 AS
SELECT
#rowid := #rowid + 1 AS id
, product_id
, total_amount / quantity AS product_price
FROM t1, (SELECT #rowid := 0) AS firstID
ORDER BY product_id
;
For the second part of your request, you could try:
SELECT
T.product_id
, T.total_amount / T.quantity AS price
FROM T1 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 <= T.quantity
ORDER BY product_id
;
See it in action: SQL Fiddle.
Please comment if this requires adjustment / further detail.