One to one relationship with foreign key to mutiple tables - mysql

Please let me start by saying, I know this has been asked many times before and I've studied other questions (and answers) but after 2 days of reading questions and amending my database I can't get this to work as I want.
At the moment I have various tables, for example customer, supplier, product, banner, etc.
I have a table called custom_field which allows custom fields to be created and used against various other tables.
At the moment some of my tables look like this:
General Tables
==============
Customer
+-------------+---------------+
| customer_id | customer_name |
+-------------+---------------+
| 1 | Peter |
| 2 | Sally |
+-------------+---------------+
Banner
+-----------+-------------+--------------+
| banner_id | banner_name | banner_width |
+-----------+-------------+--------------+
| 1 | Easter | 100px |
| 2 | Xmas | 250px |
+-----------+-------------+--------------+
Tables for managing custom fields
=================================
Custom_Field
+----------+------------+----------------+-----------+
| field_id | field_name | field_label | item_type |
+----------+------------+----------------+-----------+
| 100 | fav_color | Favorite Color | customer |
| 101 | border | Border | banner |
+----------+------------+----------------+-----------+
Custom_Field_Value
+----------+----------+---------+-------------+
| value_id | field_id | item_id | field_value |
+----------+----------+---------+-------------+
| 1567 | 100 | 1 | Red |
| 1568 | 100 | 2 | Blue |
| 1569 | 101 | 1 | Solid |
| 1570 | 101 | 2 | Dotted |
+----------+----------+---------+-------------+
To clarify, item_id refers to a customer_id, a banner_id, or a supplier_id, etc. In the example above this means Peter has a "favorite color" custom field set to Red, and Sally has a "favorite color" custom field set to Blue.
The Easter Banner has a "border" custom field set to solid, and the Xmas Banner has a "border" custom field set to Dotted.
This all works fine, except there can be no foreign key or referential integrity set between Custom_field_value.item_id and Customer.customer_id (or Banner.banner_id) because item_id's context is described by the item_type field in the Custom_Field table.
I don't want to create multiple nullable foreign keys (not sure that would even work anyway) as it will become unmanageable.
I did try creating sub tables, for example customer_custom_field, and relate this between Customer and Custom_Field, but again it becomes unmanageable when you consider every table could potentially have custom fields.
A single field value would only ever apply to a single entity from another table.
As an aside I also want to create an Attachments table for managing uploaded attachments to a particular entity, and again that could apply to customers, suppliers, products and various other tables, so it's a similar issue.

Related

MySql add relationships without creating dupes

I created a table (t_subject) like this
| id | description | enabled |
|----|-------------|---------|
| 1 | a | 1 |
| 2 | b | 1 |
| 3 | c | 1 |
And another table (t_place) like this
| id | description | enabled |
|----|-------------|---------|
| 1 | d | 1 |
| 2 | e | 1 |
| 3 | f | 1 |
Right now data from t_subject is used for each of t_place records, to show HTML dropdowns, with all the results from t_subject.
So I simply do
SELECT * FROM t_subject WHERE enabled = 1
Now just for one of t_place records, one record from t_subject should be hidden.
I don't want to simply delete it with javascript, since I want to be able to customize all of the dropdowns if anything changes.
So the first thing I though was to add a place_id column to t_subject.
But this means I have to duplicate all of t_subject records, I would have 3 of each, except one that would have 2.
Is there any way to avoid this??
I thought adding an id_exclusion column to t_subject so I could duplicate records only whenever a record is excluded from another id from t_place.
How bad would that be?? This way I would have no duplicates, so far.
Hope all of this makes sense.
While you only need to exclude one course, I would still recommend setting up a full 'place-course' association. You essentially have a many-to-many relationship, despite not explicitly linking your tables.
I would recommend an additional 'bridging' or 'associative entity' table to represent which courses are offered at which places. This new table would have two columns - one foreign key for the ID of t_subject, and one for the ID of t_place.
For example (t_place_course):
| place_id | course_id |
|----------|-----------|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 3 | 1 |
| 3 | 3 |
As you can see in my example above, place 3 doesn't offer course 2.
From here, you can simply query all of the courses available for a place by querying the place_id:
SELECT * from t_place_course WHERE place_id = 3
The above will return both courses 1 and 3.
You can optionally use a JOIN to get the other information about the course or place, such as the description:
SELECT `t_course`.`description`
FROM `t_course`
INNER JOIN `t_place_course`
ON `t_course`.`id` = `t_place_course`.`course_id`
INNER JOIN `t_place`
ON `t_place`.`id` = `place_id`

Group results in Crystal Reports where linked column has multiple related results

I have two tables that are linked. The first table is a list of prescribed medications ('medications' table) and the other is a list of actions that relate to the medication when it has been prescribed ('PMP' table)
For each prescribed medication, there can be multiple actions such as authorise, comment, stop etc.
What i am trying to do is to call all prescribed medications along with every other action for that drug.
I can do this using the code below.
select medications.oid, medications.drug, PMP.action_dte, PMP.actions
from medications
left join PMP on medications.oid = PMP.fk2_oid
This works fine and i get everything i need. But the medications that have multiple actions are returned once for each action.
My issue comes when i want to put it in to a Crystal Report.
I have a subreport called Drugs and i want to list all of the authorised drugs on the left and then all the actions for that drug on the right. But what i get is.
OID | MEDICATION | ACTION DTE | ACTION
| | |
1 | Paracetamol 200mg | 01.01.17 | Authorised
| | |
| | |
1 | Paracetamol 200mg | 03.01.17 | Comment
| | |
| | |
1 | Paracetamol 200mg | 10.01.17 | Stop
| | |
| | |
2 |Ibuprofen 100mg | 05.01.17 | Authorised
| | |
| | |
2 |Ibuprofen 100mg | 06.02.17 | Comment
Where as i would like
OID | MEDICATION | ACTION DTE | ACTION
| | |
1 | Paracetamol 200mg | 01.01.17 | Authorised
| | 03.01.17 | Comment
| | 10.01.17 | Stop
| | |
2 | Ibuprofen 100mg | 05.01.17 | Authorised
| | 06.02.17 | Comment
| | |
I have played around with grouping by oid and fk2_oid. As well as trying to link two sub reports on those fields but i am getting no where.
Is anyone able to suggest a formula or preferably a modification to the code which will allow the crystal report to display the drug on the left ONCE along with every action related to that drug on the right.
Thanks in advance!
EDIT ----------
I forgot to mention that there is a date field in the equation. Which is PMP.action_dte. Edited the original query and description.
Apologies for any confusion caused.
EDIT 2 ---------
Apparently my original post was misleading.....modified to hopefully clarify. I would like the drug grouped by OID with every action allocated to that drug listed along with the action date, as shown above.
Bring in your results
Under Report > Group Expert, group by Drug.
Then I deleted Drug in Details
In Group Header 1, Section Report, check on Underlay Following
Section
Remove Bold for Group #1 Medication
You can download the Crystal Report here

MYSQL select statement with non unique primary key

Cars
|Vin |Make |Year|
| 1 |Honda|2009|
| 2 |Honda|2010|
| 3 |Honda|2009|
| 4 |Toyota|2009|
Colors
|Color|Make |Vin|
| Red |Honda|1|
|Blue |Toyota|4|
|Pink |Honda|2|
Financer
|Bank|Make |
| BOA |Honda|
| Cha |Toyota|
| PNC |Jeep|
A query
SELECT
car.Vin,
colors.Color,
financer.Bank
from Car
JOIN Colors ON car.vin = colors.vin
AND car.make = colors.make
JOIN financer ON car.make = financer.make
WHERE car.make = 'Honda'
Trying to join these tables but getting more rows than expected. My financer table above doesn't have a column that's unique.
Is there a way that I can do an ALTER TABLE to make a primary key unique by combining 2 columns?
Let's take a step back from the RDBMS details, and consider what it means that your financer table has no unique column. What does that mean in the world of your application?
It might mean that a particular Make has more than one Bank that gives loans, in which case there's some kind of choice, or alternative, embedded in that table.
It might mean there are completely duplicated rows.
Here are examples of both circumstances.
|Bank |Make |
| BOA |Honda |
| TD |Honda | 1. more than one bank finances Honda
| Cha |Toyota |
| PNC |Jeep |
| PNC |Jeep | 2. entirely duplicate row
This version of the financer table generates this result set (http://sqlfiddle.com/#!9/27705/2/0). It has, as you mention plenty of duplicates
| Vin | Color | Bank |
|-----|-------|------|
| 1 | Red | BOA |
| 2 | Pink | BOA |
| 1 | Red | TD |
| 2 | Pink | TD |
| 4 | Blue | Cha |
You can prevent the completely duplicated rows by creating a composite primary key, consisting of both columns. That probably makes sense in your application's world.
If you want a 1::1 relationship Make::Bank, you can put a primary key on Make. But I suspect that won't model your application's world correctly. (http://sqlfiddle.com/#!9/27705/5/0)
Maybe you want this query:
SELECT cars.Vin, cars.Make,
GROUP_CONCAT(DISTINCT colors.Color) Colors,
GROUP_CONCAT(DISTINCT financer.Bank) Banks
from Cars
JOIN Colors ON cars.vin = colors.vin
AND cars.make = colors.make
JOIN financer ON cars.make = financer.make
GROUP BY cars.Make, cars.vin
This combination of GROUP_CONCAT() and GROUP BY yields a list of matching colors, and a list of matching banks, for each car.
| Vin | Make | Colors | Banks |
|-----|--------|--------|--------|
| 1 | Honda | Red | TD,BOA |
| 2 | Honda | Pink | TD,BOA |
| 4 | Toyota | Blue | Cha |
As you can see, each of the Honda cars has a list of two banks.

Mysql: Change set column on update condition?

I'm trying to figure out the best way to update one of two fields in a table. I have a personrelationship table and it links two people and I would like to have each person be able to set the relationship type on their end.
PersonRelationship Table
id int
user1_id int
user2_id int
user1_reltype /* boss, manager, etc */
user2_reltype
Depending on whether the current user is either user1_id or user2_id in the table, I need to update the user_reltype accordingly. So basically if current userid is in the user1_id field then update user1_reltype otherwise update the user2_reltype.
Since you want each user to be able to independently manage their half of the relationship, you can simplify your table structure
--------------------------------------
| initiator_id | reltype | target_id |
When a person with ID 5 (the 'initiator') marks person with ID 9 (the 'target') as a friend, the table will contain:
---------------------------------------
| initiator_id | reltype | target_id |
+--------------+----------+-----------+
| 5 | 'friend' | 9 |
If person 9 later initiates a 'boss' connection with person 5, the entry can be created without interfering with the row previously created by person 5:
--------------------------------------
| initiator_id | reltype | target_id |
+--------------+---------+_----------+
| 9 | 'boss' | 5 |
This approach will make your table easy to read and your queries easy to write.
Extra:
If you do not already have it, consider creating another table to track relationship types ('reltype'):
-----------------
| id | type |
+----+----------+
| 1 | 'friend' |
| 2 | 'boss' |
and replace the string reltype's in the relationship table with foreign keys.
---------------------------------------
| initiator_id | reltype | target_id |
+--------------+----------+-----------+
| 5 | 1 | 9 |
| 9 | 2 | 5 |

Database structure for a classifieds website

I am developing a classifieds website similar to Quickr.com.
The main problem is that each category requires a different set of properties. For example, for a mobile phone the attributes might be Manufacturer, Operating System, Is Touch Screen, Is 3G enabled etc... Whereas for an apartment the attributes are Number of bedrooms, Is furnished, Which floor, total area etc. Since the attributes and the number of attributes varies for each category, I am keeping the attributes and their values in separate tables.
My current database structure is
Table classifieds_ads
This table stores all the ads. One record per ad.
ad_id
ad_title
ad_desc
ad_created_on
cat_id
Sample data
-----------------------------------------------------------------------------------------------
|ad_id | ad_title | ad_desc | ad_created_on | cat_id |
-----------------------------------------------------------------------------------------------
|1 | Nokia Phone | Nokia n97 phone for sale. Excellent condition | <timestamp> | 2 |
-----------------------------------------------------------------------------------------------
Table classifieds_cat
This table stores all the available category. cat_id in classifieds_ads table relates to cat_id in this table.
cat_id
category
parent_cid
Sample data
-------------------------------------------
|cat_id| category | parent_cid |
-------------------------------------------
|1 | Electronics | NULL |
|2 | Mobile Phone | 1 |
|3 | Apartments | NULL |
|4 | Apartments - Sale | 3 |
-------------------------------------------
Table classifieds_attribute
This table contains all the available attributes for a particular category. Relates to classifieds_cat table.
attr_id
cat_id
input_type
attr_label
attr_name
Sample data
-----------------------------------------------------------
|attr_id | cat_id | attr_label | attr_name |
-----------------------------------------------------------
|1 | 2 | Operating System | Operating_System |
|2 | 2 | Is Touch Screen | Touch_Screen |
|3 | 2 | Manufacturer | Manufacturer |
|4 | 3 | Bedrooms | Bedrooms |
|5 | 3 | Total Area | Area |
|6 | 3 | Posted By | Posted_By |
-----------------------------------------------------------
Table classifieds_attr_value
This table stores the attribute value for each ad in classifieds_ads table.
attr_val_id
attr_id
ad_id
attr_val
Sample data
---------------------------------------------
|attr_val_id | attr_id | ad_id | attr_val |
---------------------------------------------
|1 | 1 | 1 | Symbian OS |
|2 | 2 | 1 | 1 |
|3 | 3 | 1 | Nokia |
---------------------------------------------
========
Is this design okay?
Is it possible to index this data with solr?
How can I perform a faceted search on this data?
Does MySQL support field collapsing like solr?
My suggestion is to remove cat_id from the classifieds_attribute table, then create a new table.
The new table would look like:
cat_attr | id | cat_id | attr_id
This should help you decrease redundancy.
Your design is fine, although I question why you are using hierarchical categories. I understand that you want to organize categories from an end-user standpoint. The hierarchy helps them drill down to the category that they are looking for. However, your schema allows for attribute values at every level. I would suggest that you only need (or possibly want) attributes at the leaf level.
It is certainly possible that you could come up with attributes that would be applicable at higher levels, but this would drastically complicate your management of the data since you'd have to spend a lot of time thinking about exactly how high up the chain a certain attribute belongs and whether or not there is some reason why a lower level might be an exception to the parent rule and so forth.
It also certainly over complicates your retrieveal as well - which is part of the reason for your question, I think.
I would suggest creating an additional table that will be used to manage the hierarchy of categories above the leaf level. It would look exactly like your classifieds_cat table except the involuted relationship will obviously be to the new table. Then classifieds_cat.parent_cid becomes an FK to the new table rather than an involuted FK to classifieds_cat.
I think this schema change will reduce your application and data management complexity.