Bad MYSQL design and how to improve it - mysql

I have the following problem tying to sort out a database which was when I started an unholy mix of Mysql, hand written back of envelope notes, Excel spreadsheet and completely missing records (don't ask) I have now reduced the problem down to the following.
I have 4 tables in greatly simplified form they are:-
customers
---------
id int not null primary key,
name varchar(50)
users
---------
id2 int not null primary key,
name varchar(50)
address
-------
id int,
id2 int,
country varchar(50)
product
-------
id int,
id2 int,
item varchar(50)
Sample data:
Examples select * from customers;
+----+------+
| id | name |
+----+------+
| 1 | Fred |
+----+------+
select * from users;
+----+---------+
|id2 | name |
+----+---------+
| 1 | Wilma |
| 2 | Pebbles |
+----+---------+
select * from address;
+----+-----+---------+
| id | id2 | country |
+----+-----+---------+
| 1 | 1 | Bedrock |
| 1 | 2 | Bedrock |
+----+-----+---------+
select * from product;
+----+-----+---------+
| id | id2 | item |
+----+-----+---------+
| 1 | 1 | Item1 |
| 1 | 2 | Item2 |
+----+-----+---------+
customers.id is a primary key and links to address.id and product.id
users.id2 is a primary key and links to address.id2 and product.id2
This arrangement fails where more than one user shares an address with a customer the only way to work around this at present is to duplicate the record in address and change the id2 number. At present this only effects one case in the database.
Where users don't share an address with a customer I am unable to workout a select statement that will find Customers name, Customers address, Users name and Users address.
This situation applies to approximately 30% of the records.
Any suggestions on how to sort this chaos would be most welcome.
Richard

To summarize what I think you're describing: An address can be shared by multiple users, but a user/customer can only have one address.
It sounds like you have your many-to-one relationship backwards.
Meaning, you have a foreign key in the address table pointing to customer/user, when what you really want is a foreign key in the customer/user table pointing to address. But that's kind of ugly... so a couple of ways you might clean it up:
Is a customer also a user? If so, have a single foreign key in Address that points to User, and a foreign key from User to Customer would be fairly clean.
If not, you might consider a table that bridges User/Customer and address. Something like this:
User_Cust_ID, Type, Address_ID
1 Customer 5
2 Customer 1
1 User 1
That will allow you to resolve a user and customer that share the same address.
HTH...

Related

MySQL relations with restrictions

I have this table structure and simple relationships:
and sample data in the table:
Company
Company names are unique and should not be repeated:
+------------+---------------+
| Company_ID | Company_name |
+------------+---------------+
| 1 | Company_name1 |
+------------+---------------+
| 2 | Company_name2 |
+------------+---------------+
Location
(Locations should be assigned to a specific company):
+-------------+------------+-------------------------+
| Location_ID | Company_ID | Location_name |
+-------------+------------+-------------------------+
| 1 | 1 | Company1_Location_name1 |
+-------------+------------+-------------------------+
| 2 | 1 | Company1_Location_name2 |
+-------------+------------+-------------------------+
| 3 | 2 | Company2_Location_name1 |
+-------------+------------+-------------------------+
| 4 | 2 | Company2_Location_name2 |
+-------------+------------+-------------------------+
Data
The data in the table should depend on the selected company, and the locations should be only those that occur in the company:
+---------+-------------+------------+------------+------+
| Data_ID | Location_ID | Company_ID | data_value | date |
+---------+-------------+------------+------------+------+
| 1 | 1 | 1 | 5 | date |
+---------+-------------+------------+------------+------+
| 2 | 2 | 1 | 2 | date |
+---------+-------------+------------+------------+------+
| 3 | 3 | 2 | 3 | date |
+---------+-------------+------------+------------+------+
| 4 | 2 | 1 | 1 | date |
+---------+-------------+------------+------------+------+
| 5 | 4 | 2 | 6 | date |
+---------+-------------+------------+------------+------+
| 6 | 4 | 2 | 7 | date |
+---------+-------------+------------+------------+------+
The main dependencies that should be met:
Company names should be unique and attempts to add the same company should be blocked
Location names should be assigned to a specific company, but they may repeat and a location may appear in several companies but have a different Location_ID
Adding values to the date table should depend on:
company (we choose a specific company for which we add values)
locations (locations must depend on company)
For example:
When adding values for a company with Company_ID = 1, I should only be able to add Location_ID that occur under that company.
If I want to add a value in the data table for Company_name1 then the only available values for the Location_ID column in the data table, should be: Company1_Location_name1 and Company1_Location_name2 and I can't have values there from another company (i.e. Company2_Location_name1 and Company2_Location_name2)
At the moment it works badly:
when adding values to the data table I can select a company, but then I have locations available and I can add values that do not make sense - for Company_name1 I can add a location from Company_name2 but it should be blocked.
How can I solve such a problem? Add some additional table which will be responsible for particular pairing?
Depends what database you use.
A simpler way would be to just create a unique constraint on the table field, this will also enforce it for updates too and remove the need for a trigger. Just do:
Example for MSSQL:
ALTER TABLE [dbo].[Company]
ADD CONSTRAINT [Company_name] UNIQUE NONCLUSTERED
(
[CompanyID], [Company_name]
)
and then you'll be in business. You will be not able to add 2 company with the same name.
You can find another examples here : Trigger to prevent Insertion for duplicate data of two columns
This is exacly what you are looking for :)
#EDIT 1
OK so if you want example for MARIADB here we go :
Create unique Contraint - Using a CREATE TABLE statement
The syntax for creating a unique constraint using a CREATE TABLE statement in MariaDB is:
CREATE TABLE table_name
(
column1 datatype [ NULL | NOT NULL ],
column2 datatype [ NULL | NOT NULL ],
...
CONSTRAINT constraint_name UNIQUE (uc_col1, uc_col2, ... uc_col_n)
);
table_name
The name of the table that you wish to create.
column1, column2
The columns that you wish to create in the table.
constraint_name
The name of the unique constraint.
uc_col1, uc_col2, ... uc_col_n
The columns that make up the unique constraint.
In your example :
CREATE TABLE Company
( Company_ID INT(11) PRIMARY KEY AUTO_INCREMENT,
Company_name VARCHAR(250) NOT NULL,
CONSTRAINT company_name_unique UNIQUE (Company_name)
);
In this example, we've created a unique constraint on the Company table called company_name_unique. It consists of only one field - the Company_name field.

Better way to design payment database with multiple payment methods

I am trying to make a payment/transaction database for a pretend online store (just trying to learn). 1 payment can purchase 1 to many items. 1 payment can only have 1 payment method.
To keep the example simple, there are 2 payment methods, PayPal and Bitcoin. Each payment method has different attributes, hence they must be different tables.
I have my payments table which tells me what transaction bought what item/s. However, you can see that if the paypal_idis NULL then the bitcoin_id column is not. This means there are a lot of NULL's which I think is not a good design. How can I have good design in a case like this?
paypal table
paypal_id | txn_id | buyer_email | amount
1 | 3sd7fgudf23sdf34 | john#mail.com | 50.00
2 | 45shfik45345fg2s | mike#gmail.com | 100.00
bitcoin table
bitcoin_id | txn_id | amount
1 | 34327yhujndreygdiusfsdf324 | 0.19203
2 | sdfgurjibdsfhubhsdfinjo332 | 0.04123
items table
item_id | item name | price
1 | ball | 50.00
2 | shirt | 50.00
payments
payment_id | item_id | paypal_id | bitcoin_id
1 | 1 | 1 | NULL
2 | 1 | 2 | NULL
3 | 2 | 2 | NULL
4 | 1 | NULL | 1
5 | 1 | NULL | 1
6 | 1 | NULL | 2
Your design is fine. But you might want to consider an alternative where you have a payment_transactions table and then related tables that use the same primary key:
create table payment_transactions (
payments_transactions_id int auto_increment primary key,
type varchar(255),
payment_datetime datetime, -- probably common to all payment methods
. . . other columns if you like,
unique (type, payments_transactions_id) -- this will be used for foreign key references
);
create table bitcoin_payments (
bitcoin_payments_transaction_id int primary key,
type varchar(255) generated always as ('bitcoin'),
. . . , -- columns specific to bitcoins
foreign key (type, bitcoin_payments_transaction_id) references payments (type, payments_transactions_id)
);
-- similar for paypal
Then your payments table can have a foreign key to payments.
This handles much of the data modeling issue;
You have proper declared foreign key relationships.
Only one column is needed in payments regardless of the number of types.
You can easily introduce new types.
This guarantees one type per payment (via the inclusion of type in the foreign key reference).
One downside is that you need to insert each transaction twice. First into the payment_transactions table and then into the proper table.
Payments are actually more complicated than you present. A more realistic data model would handle:
Transaction status.
Retries.
Partial payments.
Once you get the basic structure down, you might want to try adding in new capabilities.

MySQL link two tables together implicitly

Suppose we have two tables
A table called people with people linked to a bank account balances
| id | name | account_id |
--------------------------
| 1 | bob | 11 |
--------------------------
| 2 | sam | 22
A table called accounts with bank account balances
| id | value |
--------------
| 11 | 200 |
--------------
| 22 | 500 |
In order to link the two tables you can do
SELECT a.value as account_balance
FROM people p
WHERE p.name="bob"
LEFT JOIN accounts a ON p.account_id = a.id`
This would return
id => 1
name => bob
account_balance => 200
That's cool - but I am wondering if there is a more implicit way to do this via SQL linkage (foreign keys or otherwise). Can we in MySQL add links in some other way so that when we do a SELECT, it already knows to return value instead of **account_id **?
I'm asking this because I am creating a system where my users can create lookup tables and link them to other tables - but it must be do-able without any programming. The only other way I can think of is to set the name of account_id for example to accounts.value and treat that as a foreign key when doing a SELECT.
I would have to get the column structure and analyze and then determine that there is a foreign key and then return the appropriate foreign column by looking at the column name.

Two foreign keys reference one table and null able foreign key

I am new to Database tables and relationships .I need some help for the below requirements
Work flow
1. Hospital will have Male Patient
2. Hospital will have Female Patient
3. Hospital Will have Couple Patient but in RegTable it will stored as separate record for male and female.
For the above requirements i have designed the table structure below
Approach 1
RegTable
+-------+---------+---------+
| RegID | Name | Gender |
+-------+---------+---------+
| 1 | XXX | M |
| 2 | XXX | M |
| 3 | Husband | M |
| 4 | Wife | F |
+-------+---------+---------+
RegDetail
+----+------+-------+
| Id | FK_1 | FK_2 |
+----+------+-------+
| 1 | 1 | Null |
| 2 | 2 | Null |
| 3 | 3 | 4 |
+----+------+-------+
FK_1,FK_2 is RegId from Regtable
I have two questions
Is my current approach is correct or Not ?
Is alternative approach is there for the above work flow .
Kindly help me solve this . Thanks in Advance
You don't need 2 tables here.You can do it as shown below.
RegTables - this is the only table you need
Id int PK
Name string
Gender String
PatientType tinyint
Here you can maintain enum Type for separating Single and couple.
public enum PatientType : byte
{
Single=1,
Couple =2,
}
Update :
Treatments table
Id int PK
Name string
RegId int FK --> this is the foreign key referencing RegTables table
I would suggest the third table RegRecords with field
id, note, date. It will contain a registration data without link to RegTable. So you will store links to real people in RegDetail that will have only two fields: FK_KEY_RegRecords and FK_KEY_ RegTable.

Database table that has many-to-many and one-to-many relationship

In the interest of learning more about database design im was drawing up a database model, i choose to draw a simple database model for a social network website to keep a little more interesting than your average student/teacher/class models.
The question i have is about the relationships between the different tables.
Im not that good drawing these text database drawings like other peoples has on stack exchange ill try to just list the tables and explain the relationships, if its unclear i can try to draw a text drawing.
Database tables:
User
Friend
Group
Newsfeed
User has a one-to-many relationship to Friend and Group based on that one user can have many friends and a user can be a member of several groups and a group can have many users.
Friend has a many-to-many relationship with Group based on that one friend can be a part of many groups and a group can contain many friends. There is a one-to-many relationship to Newsfeed based on that one friend can have many newsfeeds.
Group has a many-to-many relationship with Friend based on that one group can contain many friends and one friend can be part of many groups. Group has a one-to-many relationships with Newsletter based on that one group can have many newsfeeds.
So now there is one many-to-many relationship and a one-to-many relationship in one table point to two other tables, is this correct ? Some part of this feels wrong, especially the Friend part but maybe im just misunderstanding something here. This might be a stupid database model but i need to ask stupid questions sometimes in order to get smarter at something. Ive read about and watch some videos about database relationships and to be they seem easy but when drawing this database model im getting confused since i suddently end up with a many-to-many and a one-to-many relationship in one table which seems weird.
This is how I'd start:
Let's say we have two groups, Group A and Group B.
groups
id unsigned int(P)
name varchar(30)
...
+----+---------+-----+
| id | name | ... |
+----+---------+-----+
| 1 | Group A | ... |
| 2 | Group B | ... |
| .. | ....... | ... |
+----+---------+-----+
Let's say Group A has two newsfeeds and Group B doesn't have any:
newsfeeds
id unsigned int(P)
group_id unsigned int(F groups.id)
name varchar(30)
...
+----+----------+--------------------+-----+
| id | group_id | name | ... |
+----+----------+--------------------+-----+
| 1 | 1 | Interesting Things | ... |
| 2 | 1 | Other Information | ... |
| .. | ........ | .................. | ... |
+----+----------+--------------------+-----+
Let's say we have three users: Bob, Mary and John:
users
id unsigned int(P)
name varchar(30)
...
+----+------+-----+
| id | name | ... |
+----+------+-----+
| 1 | Bob | ... |
| 2 | Mary | ... |
| 3 | John | ... |
| .. | .... | ... |
+----+------+-----+
A "Friend" is really just another user so let's create a table that allows many-to-many relationships between two users. My sample data shows that Bob is friends with Mary and John while Mary is only friends with John. (user_id and friend_id form the Primary Key)
users_friends
user_id unsigned int \_ (P) (F users.id)
friend_id unsigned int / (F users.id)
+---------+-----------+
| user_id | friend_id |
+---------+-----------+
| 1 | 2 |
| 1 | 3 |
| 2 | 3 |
| ....... | ......... |
+---------+-----------+
Users can belong to many groups and each group can have many users so we need to have a table that gives us that many-to-many relationship. In my example data we see that Bob is a member of Group A and Group B while Mary and John are only members of Group B. (user_id and group_id form the Primary Key)
users_groups
user_id unsigned int \_ (P)(F users.id)
group_id unsigned int / (F groups.id)
+---------+----------+
| user_id | group_id |
+---------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 2 |
| 3 | 2 |
| ....... | ........ |
+---------+----------+
Finally we need a table that shows the relationship between newsfeeds and users. I haven't entered any example data here but this table works exactly like the users_groups table. Tables like this are called many different things, you can read more about them at Wikipedia. (user_id and newsfeed_id form the Primary key)
users_newsfeeds
user_id unsigned int \_ (P) (F users.id)
newsfeed_id unsigned int / (F newsfeeds.id)
IMO, when thinking about relation modeling, one should remember about the 'direction' of the relation, otherwise it gets very confusing and also, one should remember every 'many to many' relation must have to be modeled using 'one to many'. Anyway, take a look here http://screencast.com/t/sJbPrvO53MS
even though it took a min to read the question...this was interesting problem...