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
Related
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 ...
I have two tables
Table_1 : Routes_Day_plan
Date Status_Id
------------------------
2019-06-09 1
2019-06-10 2
2019-06-09 2
2019-06-11 3
2019-06-14 4
2019-06-14 6
2019-06-15 8
Table_2 : Codes
id code
-------
1 Leave
2 Half_leave
3 Holiday
4 Work
5 Full_Hours
Now my task is to count week wise from table 1 where code (from second table) = Leave,Half_leave,work and than also show the sum , and where date not found show 0 , i write this query it's return data but not empty dates can someone please help ,
My Query:
select COUNT(*) as available, DATE(date)
from Table_1
where status_id in (
select id from codes
where code in ('Leave','Half_leave','work'))
AND DATE(date) >= DATE('2019-06-09') AND DATE(date) <= DATE('2019-06-16')
group by date
UNION ALL
SELECT COUNT(date), 'SUM' date
FROM Table_1
where status_id in (
select id from codes
where code in ('Leave','Half_leave','work'))
AND DATE(date) >= DATE('2019-06-09') AND DATE(date) <= DATE('2019-06-16')
Result Something Like ,
available Dates
------------------------
5 2019-06-09
2 2019-06-10
3 2019-06-11
3 2019-06-12
2 2019-06-14
2 2019-06-15
17 SUM
I want like this
available Dates
------------------------
5 2019-06-09
2 2019-06-10
3 2019-06-11
3 2019-06-12
0 2019-06-13
2 2019-06-14
2 2019-06-15
17 SUM
Your best bet here would be to have a Date Dimension/Lookup table which contains pre-populated dates for the entire year. By joining your record table to this lookup, you essentially allocate your data to each date that actually exist (ex. 2019-06-13) and if your data is not found in the lookup, you will find a null in that field.
The Count function will count a null as a 0. Just make sure you group on the date field from your lookup table and not from your record table.
Make a table, a date dimension that contains all the dates value, from beginning to end. Like this:
Set EndDate = '2099-01-01';
Set RunDate = '1900-01-01';
WHILE RunDate <= EndDate DO
insert into dim_date
(`DATE`)
select
RunDate as DATE
;
Set RunDate = ADDDATE(RunDate,1);
END WHILE;
Create temporary table with dim_date left join Routes_Day_plan and set Status as 0 maybe for record that dont match. Use this temporary table then instead of Routes_Day_plan in your queries.
outputting mysql values that haven't been entered into the database in more than 24 hours.
I have tried to join a secondary table and compare date inputs where the date is larger than 1 day but when i run the query it doesn't seem to filter out the data that has been entered in the previous 24 hours.
SELECT t1.value,t1.date,t1.time
FROM filter t1 LEFT JOIN filter t2
ON (t1.value= t2.value AND concat(t1.date,' ',t1.time) <
concat(t2.date,t2.time)) WHERE t2.date > curdate() - interval 1 day;
http://www.sqlfiddle.com/#!9/9d41ea/2
my ideal outcome is to have the below example where date is 06/01/19
value date
1 01/01/19
1 05/01/19
2 05/01/19
2 06/01/19
3 03/01/19
3 04/01/19
4 05/01/19
4 06/01/19
select value(number) not present in database in the past day ( 05/01/19 and 06/01/19) display result
numbers present in past day 1.2.4 dont display in results
numbers not present in the past day 3 display in results
query such as
select value,date from filter where date < curdate() - interval 1 day but not > curdate() - interval 1 day
In this example I get all the values that are expected to have transacted in the last 24 hours then left join to see if they have,
drop table if exists t;
CREATE TABLE t (
value VARCHAR(45),
date DATE,
time TIME
);
INSERT INTO t (value,date,time)
VALUES ('1','2019-01-10','14:30:00'),
('1','2019-01-10','14:30:00'),
('1','2019-01-13','14:31:00'),
('1','2019-01-14','14:32:00'),
('2','2019-01-10','14:30:00'),
('2','2019-01-13','14:31:00'),
('2','2019-01-14','14:32:00'),
('3','2019-01-10','14:30:00'),
('3','2019-01-13','14:31:00'),
('3','2019-01-14','14:32:00'),
('4','2019-01-10','14:30:00'),
('4','2019-01-13','14:31:00'),
('4','2019-01-14','14:32:00'),
('5','2019-01-01','14:00:00');
select s.value,s.dt,t.dt
from
(
select distinct value,last24hr.dt
from t
cross join (select date_sub(now(), interval 1 day) dt) last24hr
) s
left join
(select value,cast(concat(date,' ',time) as datetime) dt from t) t
on s.value = t.value and s.dt <= t.dt
where t.dt is null;
+-------+---------------------+------+
| value | dt | dt |
+-------+---------------------+------+
| 5 | 2019-01-13 12:08:23 | NULL |
+-------+---------------------+------+
1 row in set (0.00 sec)
None of your sample fail of course so I added 5 which will probably not be found by the time you test this(if you do)
I have the next structure in a MySQL database:
boats
id name
-------------
1 name1
2 name2
boat_prices
id boat_id date duration price is_default
---------------------------------------------------------------
1 1 '2018-01-01' 1 100
2 1 '2018-01-01' 2 200
3 1 null null 100 1
4 2 '2018-01-02' 2 400
5 2 '2018-01-02' 4 800
6 2 null null 200 1
7 3 '2018-01-03' 5 1500
8 3 null null 300 1
The boats have a price for a specific date and duration in days.
All boats have a default "from" price that is identified by date = null and duration = null.
But, not all boats have prices for all days.
When I search for boat prices for a specific date and duration, the query should return all rows with a price for that date and duration, and in case a boat hasnĀ“t got a price for that date return its "from" default price.
Example: For the date = '2018-01-01 and duration = 1, the result should be:
boat_prices
id boat_id date duration price is_default
----------------------------------------------------------------
1 1 '2018-01-01' 1 100
6 2 null null 200 1
8 3 null null 300 1
I did this query example just to simplify, but please take into account apart from this, the query has some other joins with other tables.
I need help with the query.
I believe Rick was on the right direction having left join, but you probably need TWO. One to get the boat prices that qualify the date interested in, another explicitly for the default.
select
b.id,
b.name,
DefPrice.price as DefaultPrice,
Specials.price as SpecialsPrice,
COALESCE( Specials.price, DefPrice.price ) as DiscountOrDefaultPrice
from
( select #parmDate = '2018-01-01' ) sqlvars,
boats b
JOIN boat_prices DefPrice
on b.id = DefPrice.boat_id
AND DefPrice.date IS NULL
AND DefPrice.Duration IS NULL
LEFT JOIN boat_prices Specials
on b.id = Specials.boat_id
AND Specials.date <= #parmDate
AND #parmDate <= Date_Add( Specials.Date, INTERVAL (Specials.duration -1 ) DAY )
Now, you could always return only the one price in question by doing a COALESCE() in case there is no Specials price, it gets the default via the DiscountOrDefaultPrice column.
Take your pick version of which column(s) you want to run with. This should get ALL boats, regardless of some special price based on durations. As you change whatever your parameter date in question is -- even if you do a current date, it will work. This is because you are testing the date in question against ALL possible special boat prices and its beginning to beginning + duration end date range. If you have multiple prices that overlap dates, that will just return those multiple rows that overlap.
My Adding of the duration is subtracting 1. For example, if your date is 2018-01-01 and its good for 1 day, does that mean it is only good for that one day? or up to and including 2018-01-02. The -1 forces the qualification to just the one day. So the price on 2018-01-01 good for 1 day is ONLY 2018-01-01.
Your other example for 2018-01-02 has two day duration. To me, indicating 2 days including 01-02 through 01-03. Two actual days.
CONFIRMATION from comment about dates and range
I guess my interpretation was wrong then on your data needs. Your sample of TWO dated boat price records apparently is not enough. You stated you want ALL boats regardless of qualification of a special price record. So you must start with the boat and the join to get all possible "Default" pricing no matter what. It is only the LEFT-JOIN component that needs to be adjusted.
That being said, lets simulate more data. Assume you have the following
Boad ID Date Duration Rate
1 2018-01-01 1 x
1 2018-01-02 4 y
2 2018-01-02 2 z
2 2018-01-04 4 a
3 2018-01-03 5 b
If I provide the date 2018-01-01, what rate records should I see?
If I provide date 2018-01-03, what records?
If I provide date 2018-01-05, what records?
For the particular date "2018-01-01" and duration of 1, i will use an UNION clause like this:
(Note: Edited for add is_default column)
-- Get prices for particular day and duration.
(SELECT
boat_id,
date,
duration,
price,
0 AS is_default
FROM
boat_prices
WHERE
date = "2018-01-01" AND duration = 1)
UNION
-- Add defaults prices for those don't have a price on the particular day and duration
(SELECT
boat_id,
date,
duration,
price,
is_default
FROM
boat_prices
WHERE
date IS NULL
AND
duration IS NULL
AND
boat_id NOT IN (SELECT boat_id
FROM boat_prices
WHERE date ="2018-01-01" AND duration = 1))
EXAMPLE WITH STORED PROCEDURE SOLUTION
DELIMITER //
CREATE PROCEDURE GetPricesByDateAndDuration(IN pDate DATE, IN pDuration INT)
BEGIN
-- Get prices for particular day and duration.
(SELECT
boat_id,
date,
duration,
price,
0 AS is_default
FROM
boat_prices
WHERE
date = pDate AND duration = pDuration)
UNION
-- Add defaults prices for those don't have a price on the particular day and duration
(SELECT
boat_id,
date,
duration,
price,
is_default
FROM
boat_prices
WHERE
date IS NULL
AND
duration IS NULL
AND
boat_id NOT IN (SELECT boat_id
FROM boat_prices
WHERE date = pDate AND duration = pDuration))
END //
DELIMITER ;
Then you can call the procedure like this:
CALL GetPricesByDateAndDuration('2018-01-01', 1);
Instead of that clunky output, consider:
boat_id price default
-----------------------------
1 100
2 300 (default)
Something like this should generate that:
SELECT boat_id,
IF(b.price IS NULL, dflt.price, b.price) AS price,
IF(b.price IS NULL, '(default)', '') AS default
FROM boat_prices AS dflt
LEFT JOIN boat_prices AS b USING(boat_id)
WHERE dflt.date IS NULL
AND dflt.duration IS NULL
AND '2018-01-01' >= b.date
AND '2018-01-01' < b.date + INTERVAL b.duration DAY
GROUP BY boat_id
I am having an issue with a SELECT command in MySQL. I have a database of securities exchanged daily with maturity from 1 to 1000 days (>1 mio rows). I would like to get the outstanding amount per day (and possibly per category). To give an example, suppose this is my initial dataset:
DATE VALUE MATURITY
1 10 3
1 15 2
2 10 1
3 5 1
I would like to get the following output
DATE OUTSTANDING_AMOUNT
1 25
2 35
3 15
Outstanding amount is calculated as the total of securities exchanged still 'alive'. That means, in day 2 there is a new exchange for 10 and two old exchanges (10 and 15) still outstanding as their maturity is longer than one day, for a total outstanding amount of 35 on day 2. In day 3 instead there is a new exchange for 5 and an old exchange from day 1 of 10. That is, 15 of outstanding amount.
Here's a more visual explanation:
Monday Tuesday Wednesday
10 10 10 (Day 1, Value 10, matures in 3 days)
15 15 (Day 1, 15, 2 days)
10 (Day 2, 10, 1 day)
5 (Day 3, 5, 3 days with remainder not shown)
-------------------------------------
25 35 15 (Outstanding amount on each day)
Is there a simple way to get this result?
First of all in the main subquery we find SUM of all Values for current date. Then add to them values from previous dates according their MATURITY (the second subquery).
SQLFiddle demo
select T1.Date,T1.SumValue+
IFNULL((select SUM(VALUE)
from T
where
T1.Date between
T.Date+1 and T.Date+Maturity-1 )
,0)
FROM
(
select Date,
sum(Value) as SumValue
from T
group by Date
) T1
order by DATE
I'm not sure if this is what you are looking for, perhaps if you give more detail
select
DATE
,sum(VALUE) as OUTSTANDING_AMOUNT
from
NameOfYourTable
group by
DATE
Order by
DATE
I hope this helps
Each date considers each row for inclusion in the summation of value
SELECT d.DATE, SUM(m.VALUE) AS OUTSTANDING_AMOUNT
FROM yourTable AS d JOIN yourtable AS m ON d.DATE >= m.MATURITY
GROUP BY d.DATE
ORDER BY d.DATE
A possible solution with a tally (numbers) table
SELECT date, SUM(value) outstanding_amount
FROM
(
SELECT date + maturity - n.n date, value, maturity
FROM table1 t JOIN
(
SELECT 1 n UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5
) n ON n.n <= maturity
) q
GROUP BY date
Output:
| DATE | OUTSTANDING_AMOUNT |
-----------------------------
| 1 | 25 |
| 2 | 35 |
| 3 | 15 |
Here is SQLFiddle demo