create table patientAppointment
(
pa_id bigint not null identity primary key,
pa_date datetime not null,
pa_turnNumber smallint not null,
pa_status tinyint not null
)
Assuming you are passing a string and NOT a datetime, you can simply convert it.
Example
Select AsDateTime = try_convert(datetime,'29-09-2020 1:30 PM',105)
Returns
AsDateTime
2020-09-29 13:30:00.000
No, you don't want to do that. You want to store the data using the correct data type, and that is datetime (or some variant such as datetime2(0).
You can format it as a string for output. In fact, you can add a computed column to do just that:
create table patientAppointment (
pa_id bigint not null identity primary key,
pa_date datetime not null,
pa_turnNumber smallint not null,
pa_status tinyint not null,
pa_date_mmddyyyy as (convert(varchar(255), pa_date, 105) + ' ' + convert(varchar(255), convert(time, pa_date))))
);
pa_date_mmddyyyy has the value converted as a string, which can be used in queries.
Below is DDL for the table I want to create. However, I want the attribute 'Appointment_datetime' to be a future date and during working hours (between 8:00AM and 5:00PM). I can get the future date part with -'CHECK (Appointment_datetime >= GETDATE()) But how do I get between 8AM and 5PM ontop of this constraint?
CREATE TABLE tAppointment
(
Appointment_ID int NOT NULL PRIMARY KEY,
Appointment_datetime datetime NOT NULL, -- CHECK CONSTRAINTS NEEDED
Appointment_week int NOT NULL,
Appointment_room varchar(5) NOT NULL,
Vet_ID int NOT NULL REFERENCES tVet(Vet_ID),
Owner_ID int NOT NULL REFERENCES tOwner(Owner_ID),
Pet_ID int NOT NULL REFERENCES tPet(Pet_ID)
)
You can just add it in. Here is a method using the hour:
CHECK (Appointment_datetime >= GETDATE() AND
DATEPART(HOUR, GETDATE()) NOT BETWEEN 8 AND 16
)
Note: If you want to take weekends and holidays into account, that is more difficult and probably requires a user-defined function.
I have a php query that runs fairly often like this one:
$query = 'SELECT * FROM work_orders '
.'WHERE '
. "((end_time >= ?"
. "AND start_time <= ?) "
. "OR (start_time <= ? "
. "AND end_time >= ? ) "
. "OR (start_time >= ? "
. "AND end_time <= ? )) ";
And a table defined as:
CREATE TABLE IF NOT EXISTS `work_orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`work_order_number` varchar(32) COLLATE latin1_general_ci NOT NULL,
`start_time` datetime NOT NULL,
`end_time` datetime NOT NULL,
`client_name` varchar(128) COLLATE latin1_general_ci NOT NULL,
`location` varchar(128) COLLATE latin1_general_ci NOT NULL,
`note` varchar(255) COLLATE latin1_general_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `note_idx` (`note`),
KEY `client_idx` (`client_name`),
KEY `location_idx` (`location`),
KEY `start_time_idx` (`start_time`),
KEY `end_time_idx` (`end_time`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=1 ;
I'm often confused by how I should create indexes. This is a read heavy table with lots of searching on the data, which is why I have each column indexed, but by far the query most often run uses the 3 combinations of start and end date to determine if a work order falls in a particular calendar range. Should I have an index on start_time and end_time individually, as I currently do, or should I create a composite key out of the two? Is there a better way to set the indexes up in general? Should I even be using InnoDB? I'm not using transactions at all.
This answer is two-fold. First of all, I would make the index like:
KEY start_time_idx (start_time, end_time)
BUT (and this is the second issue): You query will not be able to use any of the indexes. Here's why:
SELECT *
FROM work_orders
WHERE (
(end_time >= <some-date> AND start_time <= <some-date>)
OR
(end_time >= <some-date> AND start_time <= <some-date>)
OR
(end_time >= <some-date> AND start_time <= <some-date>)
)
As soon as you are using the OR statement, you are effectively disabling the use of indexes on the fields involved in the OR statement. Since your WHERE statement has no other fields that is not involved in the OR statement, no index will be used.
I quess that this is executed in a PHP script. Do you have access to phpMyAdmin? Then try to run this query prepended by an EXPLAIN. It will give you some hints.
If the table contains a lot of data, you might want to change this query into 3 queries, each of which only queries for a single date range, then concatenating the result in PHP afterwards.
/Carsten
The following script allows you to create the database and populate it with necessary data for my question:
# HeidiSQL Dump
#
# --------------------------------------------------------
# Host: 127.0.0.1
# Database: blueskylearning
# Server version: 5.1.22-rc-community
# Server OS: Win32
# Target-Compatibility: MySQL 5.0
# Extended INSERTs: Y
# max_allowed_packet: 1048576
# HeidiSQL version: 3.0 Revision: 572
# --------------------------------------------------------
/*!40100 SET CHARACTER SET latin1*/;
#
# Database structure for database 'windowsLinuxProblem'
#
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `windowsLinuxProblem` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `windowsLinuxProblem`;
#
# Table structure for table 'organization'
#
CREATE TABLE /*!32312 IF NOT EXISTS*/ `organization` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
#
# Dumping data for table 'organization'
#
/*!40000 ALTER TABLE `organization` DISABLE KEYS*/;
LOCK TABLES `organization` WRITE;
REPLACE INTO `organization` (`id`, `name`) VALUES
(1,'Org1');
UNLOCK TABLES;
/*!40000 ALTER TABLE `organization` ENABLE KEYS*/;
#
# Table structure for table 'resource'
#
CREATE TABLE /*!32312 IF NOT EXISTS*/ `resource` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`resourcePublic` tinyint(4) NOT NULL,
`active` tinyint(4) NOT NULL,
`published` tinyint(4) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=534 DEFAULT CHARSET=utf8;
#
# Dumping data for table 'resource'
#
/*!40000 ALTER TABLE `resource` DISABLE KEYS*/;
LOCK TABLES `resource` WRITE;
REPLACE INTO `resource` (`id`, `title`, `resourcePublic`, `active`, `published`) VALUES
(1,'Title number 1',1,1,1),
(2,'Title number 2',1,1,1),
(3,'Title number 3',1,1,1),
(4,'Title number 4',1,1,1),
(5,'Title number 5',1,1,1),
(6,'Title number 6',1,1,1);
UNLOCK TABLES;
/*!40000 ALTER TABLE `resource` ENABLE KEYS*/;
#
# Table structure for table 'resourceorganization'
#
CREATE TABLE /*!32312 IF NOT EXISTS*/ `resourceorganization` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`resource_id` int(11) NOT NULL,
`organization_id` int(11) NOT NULL,
`forever` tinyint(4) NOT NULL,
`startDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`endDate` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY `resource_id` (`resource_id`),
KEY `organization_id` (`organization_id`),
CONSTRAINT `resourceorganization_ibfk_1` FOREIGN KEY (`resource_id`) REFERENCES `resource` (`id`),
CONSTRAINT `resourceorganization_ibfk_2` FOREIGN KEY (`organization_id`) REFERENCES `organization` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=115 DEFAULT CHARSET=utf8;
Could someone help me understand why the following query does not fetch all the resources with active=1, published=1, resourcePublic=1
Query:
select *
from
resource resource0_,
resourceOrganization resourceor1_
where
resource0_.active=1
and resource0_.published=1
and (
resource0_.resourcePublic=1
or resourceor1_.resource_id=resource0_.id
and resourceor1_.organization_id=2
and (
resourceor1_.resource_id=resource0_.id
and resourceor1_.forever=1
or resourceor1_.resource_id=resource0_.id
and (
current_date between resourceor1_.startDate and resourceor1_.endDate
)
)
)
As it stands, this query should simplify to:
select *
from
resource resource0_,
resourceOrganization resourceor1_
where
resource0_.active=1 and
resource0_.published=1 and
(resource0_.resourcePublic=1 or
(resourceor1_.resource_id=resource0_.id and
resourceor1_.organization_id=2 and
(resourceor1_.forever=1 or
current_date between resourceor1_.startDate and resourceor1_.endDate
)
)
)
I would expect this to produce a cartesian join to all records on the resourceOrganization table, where the resource table's active, published and resourcePublic values are all 1.
Where the resource table's active and published values are both 1, but resourcePublic is not 1, I would expect an inner join to records on the resourceOrganization table where the organization_id is 2, and either forever is 1 or the current date as at midnight is between the start and end datetime values. I note that the end datetime defaults to 0000-00-00 00:00:00.
Excluding records where either active or published values are not 1, the obvious reasons for the resource not being reported are therefore that the resourcePublic values are not 1, and either:
there is no corresponding resourceOrganization record for organization_id 2, or
the corresponding resourceOrganization record has a forever value that is not 1 and
the current date as at midnight is not between the start and end datetime values.
The date range appears the most likely source of incorrectly excluded records - records entered in the current day will have start datetime values after midnight and will therefore be excluded, while records with no end date specified will default to 0000-00-00 00:00:00 and will therefore also be excluded.
Therefore, I recommend rewriting the query to be something like:
select *
from resource r
left join resourceOrganization ro
on r.id = ro.resource_id and ro.organization_id = 2
where
r.active=1 and
r.published=1 and
(r.resourcePublic=1 or
ro.forever=1 or
now() between ro.startDate and
(case where ro.endDate = '0000-00-00 00:00:00'
then now()
else ro.endDate end
)
)
Mark, Tony, Leslie and all, thanks for your feedback but I managed to sidestep the problem!
I discovered that the where clause results in false if there are no records in resourceOrganization table. That means even if resource table has records where active, published and resourcePublic are all 1, if there are no data in resourceOrganization table, the where clause results in false. I really don't understand why this is the case but now that I discovered this, I added some logic in my Java code to build query string depending on whether are records in resourceOrganization table.
In any case, does anyone have any explanation for this behavior?
Here's a snippet of my PHP that is creating the table:
$sql = 'CREATE TABLE '.$table.' (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 55 ) NOT NULL ,
`venue` VARCHAR( 55 ) NOT NULL ,
`time` TIMESTAMP NOT NULL ,
`desc` TEXT NOT NULL
)';
This is making the time column become the current timestamp when I add or change a row. How can I prevent this?
That's the default behavior of the TIMESTAMP column type. I'd recommend changing it to a DATETIME column.
You can also alter the behavior by explicitly specifying how you want it to behave.
added TIMESTAMP DEFAULT CURRENT_TIMESTAMP
The above column specification will not have the ON UPDATE behavior. You can also specify a NULL value or 0 as the default.