SQL - Find Available Slots Within Date Range - mysql

I have following database schema:
CREATE TABLE `property` (
`id` INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL
);
CREATE TABLE `venue` (
`id` INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
`property_id` INT(11) NOT NULL,
`name` VARCHAR(100) NOT NULL
);
CREATE TABLE `venue_available` (
`id` INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
`venue_id` INT(100) NOT NULL,
`day` VARCHAR(10) NOT NULL,
`from_time` TIME NOT NULL,
`to_time` TIME NOT NULL,
`lead_time_in_minutes` INT(11)
);
CREATE TABLE `venue_unavailable` (
`id` INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
`venue_id` INT(100) NOT NULL,
`from_datetime` DATETIME NOT NULL,
`to_datetime` DATETIME NOT NULL
);
CREATE TABLE `venue_reservation` (
`id` INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
`venue_id` INT(100) NOT NULL,
`start_datetime` DATETIME NOT NULL,
`end_datetime` DATETIME NOT NULL
);
I want to find properties having venues available from 25th Aug(Sat) to 27th August (Mon) from 10am to 3pm
Here is the SQL query I tried
SELECT
p.id,
p.name AS property_name,
v.name AS venue_name
FROM
venue v
LEFT JOIN
property p ON v.property_id = p.id
-- venue_available
LEFT JOIN
venue_available va_0 ON va_0.venue_id = v.id
LEFT JOIN
venue_available va_1 ON va_1.venue_id = v.id
WHERE 1 = 1
-- venue_available
AND (
(va_0.day = 'sat' AND va_0.from_time <= '2018-08-25 10:00:00' AND va_0.to_time >= '2018-08-25 15:00:00') AND
(va_1.day = 'sun' AND va_1.from_time <= '2018-08-26 10:00:00' AND va_1.to_time >= '2018-08-26 15:00:00')
)
-- venue_unavailable
AND NOT EXISTS (SELECT * FROM venue_unavailable vu WHERE '2018-08-25 10:00:00' <= vu.to_datetime AND '2018-08-26 15:00:00' >= vu.from_datetime)
GROUP BY
p.id;
The problem with the current query is, the condition for venue_available in SQL query seems to work correctly, but when I add the condition for venue_unavailable it returns me the empty result, however based on the data I am expecting 1 result.
Here is the link to SQL fiddle, if you want to play around with schema and fixtures
http://sqlfiddle.com/#!9/33d60f/10
Here is what I am trying to do
1. Get the list of all properties (not venues)
2. List the property only if one or more venue is available after checking with
venue_available
venue_unavailable
venue_reservation
Can you help me with how to go about this?x
Thank you.
UPDATE1
I followed the following post to determine overlapping dates in venue_unavailable Select rows that are not between dates (reservation)

Alright, so the way I solved it is using sub query which is working now.
I am now using the WHERE clause with something like this
WHERE v.id NOT IN (SELECT venue_id FROM provider_block pb WHERE :start_datetime <= pb.to_date AND :end_datetime >= pb.from_date)
This seems to do the job for now.

Related

MySQL 'Unrecognized Data Type' When Creating a Table

I'm creating a materialized view in MySQL to reduce server load when data is queried from a bunch of other tables(one product at a time). My simplified code is as follows:
DROP TABLE IF EXISTS `db`.`view_stock`;
CREATE TABLE IF NOT EXISTS `db`.`view_stock` (
SELECT A.title, on_order,(stock-sales) AS 'Stock' FROM
(SELECT SUM(`bought_products`.`qty`) AS 'on_order'
,`bought_products`.product_id, title FROM
`bought_products` GROUP BY product_id)
A,
(SELECT SUM(num) AS `stock`,product_id FROM plugins__stock GROUP BY
product_id)
B,
(SELECT SUM(`bought_products`.`qty`) AS `sales`
,`bought_products`.`product_id` FROM `storage__bought_products` JOIN
`plugins__orders` WHERE `bought_products`.`order_id` =
`plugins__orders`.`id` AND
((`plugins__orders`.`status` = 'paid') OR
(`plugins__orders`.`status` = 'shipped'))
GROUP BY product_id)
C
WHERE B.product_id = A.product_id AND C.product_id = A.product_id ORDER BY on_order)
When I run the query by itself it works and returns the data as expected. However, when I try to create the table in the above context, I get the following error: Unrecognized data type. (near 'A') This error is highlighted at the beginning of the query where 'A' is first mentioned (near 'A.title').
Heres a sample result:
Title on_order Stock
'Widget' 6 15
'Gadget' 3 10
I've tried using other ways to declare the table, but it seems like nothing is working. Does anyone have any ideas?
The table structure of bought_products is:
CREATE TABLE `bought_products` (
`id` bigint(20) NOT NULL,
`order_id` bigint(20) NOT NULL,
`product_id` bigint(20) NOT NULL,
`qty` int(11) NOT NULL,
`stock_count` int(11) NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
)
The table structure of plugins__stock is:
CREATE TABLE `plugins__stock` (
`id` bigint(20) NOT NULL,
`product_id` bigint(20) NOT NULL,
`num` int(11) NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
The table structure of plugins__orders is:
CREATE TABLE `plugins__orders` (
`id` bigint(20) NOT NULL,
`name` tinytext NOT NULL,
...
`status` enum('open','paid','shipped','deleted')
)
These are obviously shortened to keep the post length short.

MySQL Order by subquery column

I have problem with sql query. The idea is to select all loans that are after payment (status 1/2/3) between 8 and 21 days with calculated value from payment_day til now.
I have already done some query but can't use columns days_after_payment and days_after_part_payment in WHERE section. I would like to have one column like days_after_payment based on loan type.
SELECT l.*,
(SELECT SUM(`value`) FROM `loan_part` WHERE `loan_id` = l.id AND `paid`=0) AS left_to_pay,
-(DATEDIFF((SELECT date FROM `loan_part` WHERE `loan_id` = l.id AND `paid`=0 AND `date`<CURDATE() ORDER BY `date` LIMIT 1), NOW())) AS days_after_part_payment,
-(DATEDIFF(l.payment_date, NOW())) AS days_after_payment
FROM loan l
WHERE (l.type=1 or l.type=2) AND (l.status=1 OR l.status=2 OR l.status=3)
GROUP BY l.client_id
ORDER BY
CASE l.type
WHEN 1 THEN days_after_payment
WHEN 2 THEN days_after_part_payment
ELSE 1 END
ASC
CREATE TABLE IF NOT EXISTS `loan` (
`id` int(11) NOT NULL,
`value` int(11) NOT NULL,
`client_id` int(11) NOT NULL,
`status` int(11) NOT NULL,
`type` int(11) NOT NULL,
`payment_date` date DEFAULT NULL
) ENGINE=MyISAM AUTO_INCREMENT=2068 DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `loan_part` (
`id` int(10) unsigned NOT NULL,
`loan_id` int(11) NOT NULL,
`value` float NOT NULL,
`date` date DEFAULT NULL,
`paid` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=MyISAM AUTO_INCREMENT=1751 DEFAULT CHARSET=utf8;
Update1 : I had to cut unnecessary columns and rewrite it into English from my native language.
ORDER BY 7
"7" means the 7th field in the SELECT. That works for GROUP BY also. I had to see the table definition to count how many in l.*.
How come id is not declared AUTO_INCREMENT?

i get wrong results from mysql join query

I have 2 tables, that i want to join, one is rooms and another is reservations.
Basically I want to search for rooms which are not reserved (not in reservation table) and to get the details of those rooms (which are not in reservation table) from room table.
Here are my tables structure:
CREATE TABLE `room` (
`roomID` int(11) NOT NULL AUTO_INCREMENT,
`hotelID` int(11) NOT NULL,
`roomtypeID` int(11) NOT NULL,
`roomNumber` int(11) NOT NULL,
`roomName` varchar(255) NOT NULL,
`roomName_en` varchar(255) NOT NULL,
`roomDescription` text,
`roomDescription_en` text,
`roomSorder` int(11) NOT NULL,
`roomVisible` tinyint(4) NOT NULL,
PRIMARY KEY (`roomID`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8;
CREATE TABLE `reservation` (
`reservationID` int(11) NOT NULL AUTO_INCREMENT,
`customerID` int(11) NOT NULL,
`hotelID` int(11) NOT NULL,
`reservationCreatedOn` datetime NOT NULL,
`reservationCreatedFromIp` varchar(255) CHARACTER SET greek NOT NULL,
`reservationNumberOfAdults` tinyint(4) NOT NULL,
`reservationNumberOfChildrens` tinyint(4) NOT NULL,
`reservationArrivalDate` date NOT NULL,
`reservationDepartureDate` date NOT NULL,
`reservationCustomerComment` text CHARACTER SET greek,
PRIMARY KEY (`reservationID`)
) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8;
CREATE TABLE `reservationroom` (
`reservationroomID` int(11) NOT NULL AUTO_INCREMENT,
`reservationID` int(11) NOT NULL,
`hotelID` int(11) NOT NULL,
`roomID` int(11) NOT NULL,
PRIMARY KEY (`reservationroomID`)
) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8;
Here is the query that I have right now, which gives me wrong results:
SELECT * FROM room r
LEFT JOIN reservation re
ON r.hotelID = re.hotelID
WHERE re.hotelID = 13
AND NOT
(re.reservationArrivalDate >= '2014-07-07' AND re.reservationDepartureDate <= '2014-07-13')
I also have created a fiddle, with the data from both tables included:
http://sqlfiddle.com/#!2/4bb9ea/1
Any help will be deeply appreciated
Regards, John
i agree that room number was missed,
but query template should looks like
SELECT
*
FROM
room r
LEFT JOIN reservation re
ON r.hotelID = re.hotelID
WHERE r.hotelID = 2
AND NOT (
re.hotelID IS NOT NULL
AND re.reservationArrivalDate >= '2014-07-07'
AND re.reservationDepartureDate <= '2014-09-23'
) ;
You need change table in where statement from reservation to room. Also you need add re.hotelID to where statement as well, because on where statement you need check that record is not null ans only after try to check dates
Given the newly-added reservationroom table, consider using a NOT EXISTS sub-query to find rooms without reservations:
SELECT
*
FROM
room r
WHERE NOT EXISTS
(SELECT
*
FROM
reservationroom rr
WHERE
rr.reservationroomID = r.roomID
)

View help. Pulling data from 3 tables

Okay, I need some major help with this subject. This is what I need the view to do. It needs to take Sum of the DKP_Change Column in the Attendance table
SELECT SUM(a.DKP_Change) FROM Attendance AS a GROUP BY Name
add the value of the initial DKP from the characters table
SELECT b.Inital_DKP FROM Characters AS b GROUP BY Name
Subtract the sum of the raid drops tabe cost
SELECT SUM(c.Cost) FROM Raid_Drops AS c GROUP BY Name
I'm entirely new to the idea of VIEWS and i'm not sure where to begin with, the name of the view should be DKP, the columns should be Name and Total_DKP, where total dkp is calculated from teh above select statements.
Here are the creates for all 3 tables.
CREATE TABLE `Attendance` (
`Date` date NOT NULL,
`Name` varchar(20) NOT NULL,
`Hours` int(11) NOT NULL,
`Penalty` float NOT NULL,
`Rank` set('Raider','Core','Elite') NOT NULL,
`Rate` int(11) NOT NULL,
`DKP_Change` float NOT NULL,
`RecordNumber` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`RecordNumber`)
) ENGINE=MyISAM AUTO_INCREMENT=15 DEFAULT CHARSET=latin1
CREATE TABLE `Characters` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(25) NOT NULL,
`Class` varchar(25) NOT NULL,
`Spec` varchar(25) NOT NULL,
`Position` set('Healer','Tank','DPS') NOT NULL COMMENT 'Healer, Tank, or DPS',
`Usable` set('Cloth','Mail','Plate') NOT NULL COMMENT 'Type of Usable Armor? Cloth, Mail, Or Plate',
`Primary Stat` set('Agility','Strength','Intellect','Healer','Tank') NOT NULL COMMENT 'Used for Sorting Only(ie dps trinket with agility strength dps not eligible)',
`Initial_DKP` int(11) NOT NULL COMMENT 'DKP given at the start of current tier.',
`Total_DKP` int(11) NOT NULL COMMENT 'Huge Complicated Mess.',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
CREATE TABLE `Raid_Drops` (
`Record Number` int(11) NOT NULL,
`Date` date NOT NULL,
`Name of Item` varchar(25) NOT NULL,
`Item Slot` enum('Main Hand','Off Hand','Head','Neck','Shoulder','Back','Chest','Wrist','Hands','Waist','Legs','Feet','Ring 1','Ring 2','Trinket 1','Trinket 2') NOT NULL,
`Player_Name` varchar(25) NOT NULL,
`Cost` float NOT NULL,
PRIMARY KEY (`Record Number`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
You could
Join the three tables in a subselect, grouping on Name
Perform your calculations on the results of each subselect
The only part that's not entirely clear to me is if the names in Characters are unique or not. If they are, you can drop the group by. If they are not, the AVG might give you unexpected results.
SQL Statement
SELECT sumA
, initialB
, sumC
, sumA + initialB - sumC
, a.Name
FROM (
SELECT Name, SUM(DKP_Change) AS sumA
FROM Attendance
GROUP BY Name
) AS a
INNER JOIN (
SELECT Name, Inital_DKP AS initialB
FROM Characters
) AS b ON a.Name = b.Name
INNER JOIN (
SELECT Name, SUM(Cost) AS sumC
FROM Raid_Drops
GROUP BY Name
) AS c ON c.Name = b.Name

Return records after group by with a specific MAX(field)

I search on SO many topics like this, but I can't apply to my query.
This is :
SELECT forum_categories.title, COUNT(DISTINCT forum_topics.id) AS total_topics,
SUM(CASE WHEN forum_messages.original=0 THEN 1 ELSE 0 END) AS total_replies, forum_messages.author,
MAX(forum_messages.date) AS last_message, SUM(CASE WHEN r.user IS NULL THEN 1 ELSE 0 END) to_view
FROM forum_categories
JOIN forum_topics ON forum_topics.category_id=forum_categories.id
LEFT OUTER JOIN (SELECT topic, user FROM forum_visits WHERE user='userA') r ON forum_topics.id=r.topic
JOIN forum_messages ON forum_messages.topic_id=forum_topics.id
GROUP BY forum_categories.id
ORDER BY forum_categories.date
It works: the only problem is that return only the field forum_messages.date with MAX data; intead, I'd like to return the whole row with that MAX field (so the corrispondent author, for example).
So what I should return, in less words, is :
the title for each category; *(at the moment this works)
the number of the topics for that category; (at the moment this works)
the number of replies for all topics for that category; (here there is another condition as you can see, the counter of that replies is get by the message with the filed original=0) (at the moment this works)
the author/data for the last message for that category (HERE there is the problem : it return correctly only the date, not the author);
a flag that indicate if there is any topic that userA haven't been checked yet; (also at the moment works: if SUM return somethings more high than 0, there is a topic not viewed)
this query is supposted to be as faster as possible, because the tables could be very big;
For details, these are my actual tables :
CREATE TABLE IF NOT EXISTS `forum_categories` (
`id` int(11) unsigned NOT NULL auto_increment,
`title` varchar(255) NOT NULL,
`description` varchar(255) NOT NULL,
`date` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `forum_topics` (
`id` int(11) unsigned NOT NULL auto_increment,
`category_id` int(11) unsigned NOT NULL,
`title` varchar(255) NOT NULL,
`author` varchar(255) NOT NULL,
`date` datetime NOT NULL,
`view` int(11) unsigned NOT NULL default '0',
`sticky` tinyint(11) unsigned NOT NULL default '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=25 DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `forum_messages` (
`id` int(11) unsigned NOT NULL auto_increment,
`topic_id` int(11) unsigned NOT NULL,
`author` varchar(255) NOT NULL,
`message` mediumtext NOT NULL,
`date` datetime NOT NULL,
`original` tinyint(11) unsigned NOT NULL default '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=29 DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `forum_visits` (
`id` int(11) unsigned NOT NULL auto_increment,
`topic` int(11) unsigned NOT NULL,
`user` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `forum_visits_unique_idx` (`topic`,`user`)
) ENGINE=MyISAM AUTO_INCREMENT=131 DEFAULT CHARSET=utf8;
Hope that someone can help me!
We can use the query from your previous question to get the message author and the message date, and the query above to get the counters (topics and replies), and join them together:
EDIT: This query works (tested). BUT it got a little complex and has 2 subqueries in it, so I hope someone else posts a better simpler one. If the DB gets very big, this one may get sluggish.
SELECT forum_categories.title,
COUNT(DISTINCT forum_topics.id) AS total_topics,
SUM(CASE WHEN forum_messages.original=0 THEN 1 ELSE 0 END) AS total_replies,
t2.author, t2.last_message
-- first get the counters per category
FROM forum_categories
JOIN forum_topics ON forum_topics.category_id=forum_categories.id
JOIN forum_messages ON forum_messages.topic_id=forum_topics.id
-- Then join a query to get last message per category
JOIN (SELECT forum_categories.id, forum_messages.author,
forum_messages.date AS last_message
FROM forum_categories
JOIN forum_topics ON forum_topics.category_id=forum_categories.id
JOIN forum_messages ON forum_messages.topic_id=forum_topics.id
JOIN (SELECT MAX(m.date) as date, top.category_id
FROM forum_messages m
JOIN forum_topics top ON m.topic_id = top.id
GROUP BY top.category_id) as t
ON t.category_id = forum_topics.category_id AND t.date = forum_messages.date
GROUP BY forum_categories.id) t2
ON t2.id = forum_categories.id
GROUP BY forum_categories.id
To supplement the current output with the missing data, I would probably go like this:
SELECT
forum_stats.*, /* just repeat the already pulled columns (expand it if needed) */
forum_messages.* /* and here you may actually want to be more specific as to
what else you would like to pull from forum_messages */
FROM (
SELECT
forum_categories.id AS category_id,
forum_categories.title,
COUNT(DISTINCT forum_topics.id) AS total_topics,
SUM(CASE WHEN forum_messages.original=0 THEN 1 ELSE 0 END) AS total_replies,
MAX(forum_messages.date) AS last_message,
SUM(CASE WHEN r.user IS NULL THEN 1 ELSE 0 END) AS to_view,
forum_categories.date
FROM forum_categories
JOIN forum_topics ON forum_topics.category_id=forum_categories.id
LEFT OUTER JOIN (
SELECT topic, user FROM forum_visits WHERE user='userA'
) r ON forum_topics.id=r.topic
JOIN forum_messages ON forum_messages.topic_id=forum_topics.id
GROUP BY forum_categories.id
) forum_stats
JOIN forum_topics ON forum_topics.category_id=forum_stats.category_id
JOIN forum_messages ON forum_messages.topic_id=forum_topics.id
AND forum_messages.date=forum_stats.last_message
ORDER BY forum_stats.date
Of course, this assumes that forum_messages.date is not just a date, but a timestamp and that no two messages can share absolutely the same timestamp.