Mysql Multiple Unique Keys Across Tables - mysql

I have a table like that:
CREATE TABLE `Appointment` (
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
doctor_slot_id INT NOT NULL,
date DATE NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(user_id) REFERENCES user(id),
FOREIGN KEY(doctor_slot_id) REFERENCES doctor_slot(id)
);
I want that a user can't arrange an appointment with a doctor more than once in a day. So I want to add a unique constraint between doctor_id and user_id but in this structure I can't. I tried those things which are not in SQL syntax:
UNIQUE(user_id, doctor_slot.doctor_id)
and
UNIQUE(user_id, doctor_slot(doctor_id))
and
UNIQUE(user_id, doctor_id(doctor_slot))
But as you know, they didn't work. Are there any suggestions you can make?

Based on your comment about the what the doctor_slot is, it would seem you have a bit on an issue with your schema design. There should be no reason for you to store both a slot_id and a date in the appointment table, in that the doctor_slot already has a date component, so storing the date in the appointment table is a redundant storage of data, and could become problematic to keep in sync.
Of course, without the date on this table it is impossible to force a unique constraint in the database for this table.
My recommendation for any type of calendar-based app like this, would be to first create a date table. I usually use a script like the one here: http://www.dwhworld.com/2010/08/date-dimension-sql-scripts-mysql/ to create this date table. Having such a table can allow you to use a simple date_id to reference all kinds of different information about a date (this is a technique commonly used in data warehouses). As long as you use this date_id in all the other tables where you need dates, it as extremely simple to look of dates in any fashion you desire (by day of week, month, week number, whether it is a weekday or not, etc.).
You could use a similar concept to build your timeslots. Maybe make a table that has 96 entries (24 hours * 15 minutes) to represent 15 minute intervals - obviously you can change this to whatever interval you like.
You could then build your appointment table like this:
appointment_id
user_id
doctor_id
date_id
time_start_id <= time slot for appointment start
time_end_id <= time slot for appointment end
Id don't see separate need for a doctor_slots table here. If you want to track open doctor slots, you could also do that in this table by having user_id simply = NULL until the slot is filled.
This would allow you to enforce unique index on user_id and date_id.

Related

Insert/Update on table with autoincrement and foreign key

I have a table as such:
id entity_id first_year last_year sessions_attended age
1 2020 1996 2008 3 34.7
2 2024 1993 2005 2 45.1
3 ... ... ...
id is auto-increment primary key, and entity_id is a foreign key that must be unique for the table.
I have a query that calculates first and last year of attendance, and I want to be able to update this table with fresh data each time it is run, only updating the first and last year columns:
This is my insert/update for "first year":
insert into my_table (entity_id, first_year)
( select contact_id, #sd:= year(start_date)
from
( select contact_id, event_id, start_date from participations
join events on participations.event_id = events.id where events.event_type_id = 7
group by contact_id order by event_id ASC) as starter)
ON DUPLICATE KEY UPDATE first_year_85 = #sd;
I have one similar that does "last year", identical except for the target column and the order by.
The queries alone return the desired values, but I am having issues with the insert/update queries. When I run them, I end up with the same values for both fields (the correct first_year value).
Does anything stand out as the cause for this?
Anecdotal Note: This seems to work on MySQL 5.5.54, but when run on my local MariaDB, it just exhibits the above behavior...
Update:
Not my table design to dictate. This is a CRM that allows custom fields to be defined by end-users, I am populating the data via external queries.
The participations table holds all event registrations for all entity_ids, but the start dates are held in a separate events table, hence the join.
The variable is there because the ON DUPLICATE UPDATE will not accept a reference to the column without it.
Age is actually slightly more involved: It is age by the start date of the next active event of a certain type.
Fields are being "hard" updated as the values in this table are being pulled by in-CRM reports and searches, they need to be present, can't be dynamically calculated.
Since you have a 'natural' PK (entity_id), why have the id?
age? Are you going to have to change that column daily, or at least monthly? Not a good design. It would be better to have the constant birth_date in the table, then compute the ages in SELECT.
"calculates first and last year of attendance" -- This implies you have a table that lists all years of attendance (yoa)? If so, MAX(yoa) and MIN(yoa) would probably a better way to compute things.
One rarely needs #variables in queries.
Munch on my comments; come back for more thoughts after you provide a new query, SHOW CREATE TABLE, EXPLAIN, and some sample data.

what is the best way to optimize join query with multiple AND's?

I'm developing an online reservation system where people can reserve items based on availability for a particular hour of the day. For that i'm using two tables 1.Item 2.Reservation
Item:(InnoDB)
-------------------------
id INT (PRIMARY)
category_id MEDIUMINT
name VARCHAR(20)
make VARCHAR(20)
availability ENUM('1','0')
Reservation:(InnoDB)
-------------------------
id INT (PRIMARY)
date DATE
Item_id INT
slot VARCHAR(50)
SELECT Item.id,Item.category,Item.make,Item.name,reservation.slot
FROM Item
INNER JOIN reservation ON Item.id=reservation.Item_id AND Item.category_id=2
AND Item.availability=1 AND reservation.date = DATE(NOW());
I'm using the above query to display all the items under a particular category with free timeslots which a user can reserve on a particular date.
slot field in reservation table contains string(ex:0:1:1:1:0:0:0:0:0:0:0:0:1:1:1:0:0:0:0:0:0:0:0:0) where 1 means that hour is reserved and 0 means available.
availability in Item table shows wether that item is available for reservation or not(may be down for servicing).
First of all is my table structure fine ?Secondly what is the best way to optimize my query(multi column indexing etc).
thanks,
ravi.
Put a foreign key constraint and index on your FK, this should speed things up a little. You appear to mix INT for the item ID and MEDIUMINT for the FK, not sure this is what nature intended.
Points to remember while choosing an Attribute on which an Index will be created
A column that is frequently used in a SELECT list and a WHERE clause.
A column in which data will be accessed in sequence by a range of values.
A column that will be used with the GROUP By or ORDER BY clause to sort data.
A column used in a join, such as the FOREIGN KEY column.
A column that is used as a PRIMERY KEY.
Try to create index on numeric values. can introduce a surrogate key if no numeric pK is there.
Why are you putting all of those conditions in the join clause? Why not:
SELECT Item.id,Item.category,Item.make,Item.name,reservation.slot
FROM Item INNER JOIN reservation ON Item.id=reservation.Item_id
WHERE Item.category_id=2 AND Item.availability=1 AND reservation.date = DATE(NOW());
I'm not enough of a SQL expert to say whether this will make it faster, but it looks more obvious to me.
I would suggest creating an index on Reservation.item_id. That should help you improve query performance.

MySql database formatting

I am currently developing a database storage solution for product inventory information for the company I work for. I am using MySql, and I am having a hard time coming up with an efficient, feasible format for the data storage.
As it works right now, we have ~25000 products to keep track of. For each product, there are about 20 different categories that we need to track information for(quantity available, price, etc..). This report is downloaded and updated every 3-4 days, and it is stored and updated in excel right now.
My problem is that the only solution I have come up with so far is to create separate tables for each one of the categories mentioned above, using foreign keys based off of the product skus, and cascading to update each respective table. However, this method would require that every table add 24000 rows each time the program is run, given that each product needs updated for the date it was run. The problem with this is that the data will be store for around a year, so the tables will grow an extensive amount. My research for other database formats has yielded some examples, but none on the scale of this. They are geared towards adding maybe 100 rows a day.
Does anybody know or have any ideas of a suitable way to set up this kind of database, or is the method I described above suitable and within the limitations of the MySql tables?
Thanks,
Mike
25,000 rows is nothing to MySQL or a flat file for that case. Do not initially worry about data volume. I've worked on many retail database schemas and products are usually defined by either a static or arbitrary-length set of attributes. Your data quantity ends of not being that far off either way.
Static:
create table products (
product_id integer primary key auto_increment
, product_name varchar(255) -- or whatever
, attribute1_id -- FK
, attribute2_id -- FK
, ...
, attributeX_id -- FK
);
create table attributes (
attribute_id integer primary key -- whatever
, attribute_type -- Category?
, attribute_value varchar(255)
);
Or, you obviously:
create table products (
product_id integer primary key auto_increment
, product_name varchar(255) -- or whatever
);
create table product_attributes (
product_id integer
, attribute_id integer
, -- other stuff you want like date of assignment
, primary key (product_id , attribute_id)
);
create table attributes (
attribute_id integer primary key -- whatever
, attribute_type -- Category?
, attribute_value varchar(255)
);
I would not hesitate to shove a few hundred million records into a basic structure like either.

One-to-many Query in MySQL

What's the best way to query one-to-many in MySQL? This is a simplified version of the database I am working on (if anything doesn't look right tell me):
CREATE TABLE Tenant(
tenant_id int NOT NULL,
first_name varchar(20),
last_name varchar(20),
PRIMARY KEY (tenant_id)
);
CREATE TABLE Rent(
tenant_id int NOT NULL,
month enum('JAN', 'FEB', ...),
date_paid date NOT NULL,
amount_paid int NOT NULL,
FOREIGN KEY (tenant_id) REFERENCES Tenant(tenant_id)
);
(The reason that there is month and date_paid in the Rent table is because the tenant does not necessarily pay the rent all at once). What I want the tenant's name to appear once which would just be a Left Join, but I want all the amount paid in a particular month listed as columns for each tenant, I am not sure how to go about that. I am not really sure how to do that since your are dealing with an unknown amount of columns, haven't touched that yet in MySQL. Or is there a better strategy? Also, how would I go about creating my own variable like MONTH-YEAR (I don't think that exists as a native variable in MySQL). Thank you!
Edit:
Just to simplify it further I am using this format:
create table rent(
tenant_id int not null,
year year,
amount_paid int,
foreign key (tenant_id) references tenant(tenant_id)
);
If I understand what duffymo said below I should use group by (I know I am misunderstanding somewhere because it only shows the first example for each year):
SELECT Tenant.first_name, Rent.year, Rent.amount_paid
FROM Tenant
LEFT JOIN Rent
ON Tenant.tenant_id = Rent.tenant_id
GROUP BY year;
This is what I want the query to look like, the number under each year is the amount paid (I actually just realized it's a little bit more complex than what I how explained):
first_name 2009 2008 2007
John 500 500 NULL
Ann 1000 NULL NULL
Bob NULL 700 700
If you have MONTH and YEAR columns, you can do a GROUP BY to select amount paid broken out as you'd wish. If you have a PAID_DATE column, one way to do this would be to have a BEFORE INSERT trigger that runs when the PAID_DATE is set. That way users don't have to enter values, and data integrity can be guaranteed.

Best way to store event times in (My)SQL database

I'm trying to decide on the best way to store event times in a MySQL database. These should be as flexible as possible and be able to represent "single events" (starts at a certain time, does not necessarily need an end time), "all day" and "multi day" events, repeating events, repeating all day events, possibly "3rd Saturday of the month" type events etc.
Please suggest some tried and proven database schemes.
Table: Events
StartTime (dateTime)
EndTime (dateTime) null for no end time
RepeatUnit (int) null = noRepeat, 1 = hour, 2 = day, 3 = week, 4 = dayOfMonth, 5 = month, 6 = year
NthDayOfMonth (int)
RepeatMultiple (int) eg, set RepeatUnit to 3, and this to 2 for every fortnight
Id - if required, StartTime might be suitable for you to uniquely identify an event.
Name (string) - name given to the event, if required
This might help. It would require a decent amount of code to interpret when the repeats are. Parts of the time fields that are at lower resolutions than the repeat unit would have to be ignored. Doing the 3rd saturday of the month woudln't be easy either... the NthDayOfMonth info would be required just for doing this kind of functionality.
The database schema required for this is simple in comparison with the code required to work out where repeats fall.
I worked on a planner application which loosely follows the iCalendar standard (to record events). You may want to read RFC 2445 or this schema published by Apple Inc. icalendar schema to see if they are relevant to the problem.
My database schema (recurring/whole-day event was not considered at the time)
event (event_id, # primary key
dtstart,
dtend,
summary,
categories,
class,
priority,
summary,
transp,
created,
calendar_id, # foreign key
status,
organizer_id, # foreign key
comment,
last_modified,
location,
uid);
the foreign key calendar_id in the previous table refers this
calendar(calendar_id, # primary key
name);
while organizer_id refers this (with other properties like common name etc. missing)
organizer(organizer_id, # primary key
name);
Another documentation that you may find more readable is located here
hope this helps
You need two tables. One for storing the repeating events (table repeatevent) and one for storing the events (table event). Simple entries are only stored in the event table. Repeating entries are stored in the repeatevent table and all single entries for the repeating event are also stored in the event table. This means that everytime you enter a repeating entry, you have to enter all the single resulting entries. You can do this by using triggers, or as part of your business logic.
The advantage of this approach is, that querying events is simple. They are all in the event table. Without the storage of repeating events in the event table, you would have complex SQL or business logic that would make your system slow.
create table repeatevent (
id int not null auto_increment,
type int, // 0: daily, 1:weekly, 2: monthly, ....
starttime datetime not null, // starttime of the first event of the repetition
endtime datetime, // endtime of the first event of the repetition
allday int, // 0: no, 1: yes
until datetime, // endtime of the last event of the repetition
description varchar(30)
)
create table event (
id int not null auto_increment,
repeatevent null references repeatevent, // filled if created as part of a repeating event
starttime datetime not null,
endtime datetime,
allday int,
description varchar(30)
)
Same way the cron does it? Recording both start and end time that way.
Use datetime and mysql's built in NOW() function. Create the record when the process starts, update your column that tracks the end time when it the process ends.