One-to-many Query in MySQL - 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.

Related

MySQL: Query Assistance Needed

I'm going to keep it brief here for convenience's sake. I'm new to SQL coding, so please excuse me if I say something weird.
I did not manage to find a solid solution to it (at least one that I would truly understand), which is precisely why I'm posting here as a last resort at this point.
The table code:
create table companies (
company_id mediumint not null auto_increment,
Name varchar(40) not null,
Address varchar(40),
FoundingDate date,
primary key (company_id)
);
create table employees (
Employee_id mediumint not null auto_increment,
Name varchar (40),
Surname varchar(40),
primary key (Employee_id)
);
create table accounts (
Account_id mediumint not null auto_increment,
Account_number varchar(10) not null,
CompanyID int(10),
Date_of_creation date,
NET_value int(30),
VAT int(3),
Total_value int(40),
EmployeeID int(10) not null,
Description varchar(40),
primary key (Account_number)
);
Table values are random strings and numbers until I figure this out.
My issue is that I'm stuck at forming correct SQL queries, namely:
Query all accounts with their designated companies. I need it to show 'NULL' value if an account has no associated company.
Query that can list all accounts whose date is less than 2018-03-16 or those without a date.
Query that will print the description of the 'Accounts' table in one column and the number of characters in that description in a different column.
Query that lists all employees whose names end with '-gh' and that have names greater than 5 characters in length.
Query that will list the top total sum amount.
Query that will list all accounts that have '02' in them (i.e. 3/02/05).
If you can answer at least one of these queries and if you can explain how you got to the solution in a simplistic manner, well... I'm afraid I have nothing to offer but honest gratitude! ^^'
Welcome to the community, but as Jerry commented, you should really try to show SOMETHING that you have tried just to show what you THINK is needed. Also, don't just add comments to respond, but edit your original post with additional details / data as people ask questions.
To try and advance you forward though, I will point out two specific links that should help you out. The first one is a link for the basics on querying explaining the
select [fields] from [what table] join [other tables] where [what is your criteria] -- etc. Some Basics on querying
The next give some very good clarification on JOIN conditions of (INNER) JOIN -- which means required record match in BOTH tables being joined, and FULL OUTER JOINS, LEFT JOINs, etc.
After reviewing those, if you STILL have questions, please edit your original question, post some samples of what you THINK is working and let us know (or comment back to a specific answer), and we in the forum can follow-up with you.
HINT, your first query wanting NULL you should get from the visual link via LEFT JOIN.
A visual representation and samples on querying

Replacing a specific char in a string

I have a table called Attendance and columns called attendanceStatus and CourseID. The attendanceStatus column is a text type because I wish to be able to change each 0 to a 1 at any given stage. The data is filled with 0's but I wish to change the last 0 in the string to a 1. The data contains 18 0's, so it would be the 18th character that I would need to change.
I feel like I have come close with this but I am willing to listen to changing it completely if I am not close.
UPDATE Attendance
SET attendanceStatus REPLACE = (attendanceStatus, '0', '1')
WHERE CourseID like '2%';
I realize that this code changes all of the 0's to 1's and I only wish to change the 18th 0.
You are storing attendance status using the wrong method. Although I can appreciate why you would store these in a string, it is really cumbersome. How are you going to answer: How many students attended for at least 10 days? How many students were around on day 6? How many students were absent for three days in a row?
The correct method is to have a separate row for each "day" (I'm not sure what the correct units are). It would be something like:
create table StudentAttendance (
StudentAttendanceId int not null primary key auto_increment,
StudentId int not null references Student(StudentId),
CourseId int not null references Courses(CourseId),
Date date,
status char(1),
constraint unq_scd unique (StudentId, CourseId, Date)
);
UPDATE Attendance
SET attendanceStatus = CONCAT( SUBSTRING(attendanceStatus,
1,
CHAR_LENGTH (attendanceStatus) - 1
),
'1' )
WHERE CourseID like '2%';

What changes shall we make in the table

I was asked below question in an interview which i could not answer.Could anyone please help.
A primary school teacher wants to store the first name,last name,date of birth,gender(0=female and 1=male) and home phone number of each of her pupils in a MySQL database.She came up with the following table definition:
CREATE TABLE pupil(
pupil_id INT NOT NULL AUTO_INCREMENT,
first_name CHAR(50),
last_name CHAR(50),
date_of_birth CHAR(50),
gender INT,
phone_number CHAR(50),
PRIMARY_KEY (pupil_id)
)ENGINE=MyISAM DEFAULT CHARSET=utf8;
she frequently runs the following queries
select * from pupil where pupil_id = 2;
select * from pupil where first_name = 'John';
select * from pupil where first_name = 'John' and last_name = 'Doe';
What changes will you make to this table? and why?
My answer would have been I would add two indexes. One for first_name and one for first_name, last_name.
If you frequently query according to the first name or the first and last names, it might be a good idea to index them:
CREATE INDEX pupil_names_ind ON pupil (first_name, last_name);
Having said that, you should really run a benchmark first. If the table has just a couple of hundreds of rows most of them will be caches anyway, and indexing it would be a wasted effort.
You just want one index
CREATE INDEX pupils_by_name ON pupil (first_name, last_name)
first query(by id) is optimised thanks to primary key
second query(by first name) is handled by pupils_by_name index, because it fits the index column orders starting from left hand side, if you would want to optimise a query by 'last_name' field, this index would't work because the order of columns in where clause must match an order of columns in and index starting from the left
thrid query fits the index pupils_by_name perfectly
in addition
date_of_birth CHAR(50) should be date
gender INT should be tinyint
I would create indexes on the first_name and last_name (in that order). it is important. more info here : http://dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html
Then i would change the date_of_birth datatype to date
You should add an index in the table in the columns first_name and last_name.
Syntax : CREATE INDEX `ind` ON `table`(`col`);
This will make the searches on the indexed columns fast. Go through this article => When to use indexes? and MySQL's docs. Basically you use indexes on frequently searched items to boost the performance. But remember one thing : Too many indexes slow insertions into the table. So, an effective usage of indexes will definitely speed up a query.

Mysql Multiple Unique Keys Across Tables

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.

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.