Join Table vs Foreign Key/Ref - sql

Imagine I have two tables and they have a 1-to-Many relationship. Is it better to have a Join table storing the relationship, or issuing a foreign key in one of these tables? Take a look of these two situations:
Situation A:
Table 1: CreditCard
Table 2: Person
It seems to me quite making sense to put the creditCard_id as part of the Person table
Situation B:
Table 1: Order
Table 2: Person
This time I think I will put the order_id and person_id in a Join table?
Am I making a mistake in the above? Is there a standard/better way of determining this?

For 1 to Many relation, people usually put the foreign key into the heavier table or the "Many" table.
So from your example, both go CreditCard and Order tables, by doing so you will remove duplicate data.
Imagine you which one is better:
FK goes to the "Many" table
Table People:
ID NAME
1 A
2 B
Table CreditCard:
ID PEOPLE_ID
1 1
2 1
FK goes to "1" table:
Table People:
ID NAME CreditCard_ID
1 A 1
1 A 2
2 B 3
Table CreditCard:
ID
1
2
3
Note: See how the ID and Name are repeated(ID=1, NAME=A) in the second example, that happens if you put the FK in the wrong table.

I would make three tables; a person table with all their info( name, address, etc. ), a credit card table with all the info( expiration date, security number?, etc.. ) then another table connecting them with the PersonID and CreditCardID. But what do I know, I'm still in school lol so wait for someone else to answer you.

Related

SQL Table Relations

We have lots of tables in MS SQL that created without table relations many years ago. Now we are trying to create a relationship between these tables. The problem is in many of these developers used fake ids in the tables.
For example:
TABLE A, ID(primary key) -> TABLE B, AID needs to be relational. But developers used some fake ids like -1,-2 to solve some problems in their side. And now when I try to create a relation between TABLE A, ID(primary key) -> TABLE B, AID, I am getting errors.
TABLE A
ID | NAME
1 | name01
2 | name02
TABLE B
ID | NAME | AID
1 | name01 | 1
2 | name02 | -1
3 | name03 | -2
Is there way to solve this problem and is it meaning full what developers did, they didn't use any relations in sql, they are controlling everything in code-behind.
Thanks
You need to add those to your reference table. Something like this:
insert into a (id, name)
select distinct aid, 'Automatically Generated'
from b
where not exists (select 1 from a where b.aid = a.id) and
a.id is not null;
Then you can add the foreign key relationship:
alter table b add constraint fk_b_aid foreign key (aid) references a(id);
The general idea of referential integrity is exactly that you can't have invalid references.
So the best course of action here would be to suck it up and manually clean it up. Create the missing entries in the other table, or delete the records.
You can also ignore checks on existing data. If you are using sql server management studio to create relations there is option to do that just like in this screen shot
Hope it helps

Merge two versions of database tables with conflicting keys

I have been asked to merge 2 Access databases. They are conflicting versions of the same file.
A database was emailed to somebody. (I know.) Somebody added records to the 'main' copy while somebody else added records to their copy. I want to add the new records from the 'unauthorised' copy into the main version, before utterly destroying all other copies.
Unfortunately, the database has several related tables. As would naturally happen when records are added, records in different versions have conflicting primary keys. These conflicting keys are also used as foreign keys in the new records. A foreign key reference to ID x means different things in the 2 versions.
Is there any hope? I thought of maybe importing it all into excel and using formulas to update the primary and foreign keys.
Is there any way to fix this programatically?
EDIT: Here is a picture showing the full relationships. Tables teachers, tests, and test_results have been changed; the others are the same in both.
In the main database, add a Long field named [oldID] to each table into which you need to append data. Then create Linked Tables pointing to the relevant tables in the "other" database. Since the table names are the same, the linked tables will have a '1' appended to them.
For this example, we have
[teachers]
ID teacher oldID
-- -------- -----
1 TeacherA
2 TeacherB
3 TeacherX
[teachers1]
ID teacher
-- --------
1 TeacherA
2 TeacherB
3 TeacherY
[tests]
ID test_name teacher oldID
-- -------------- ------- -----
1 TeacherA_Test1 1
2 TeacherA_Test2 1
3 TeacherB_Test1 2
4 TeacherX_Test1 3
[tests1]
ID test_name teacher
-- -------------- -------
1 TeacherA_Test1 1
2 TeacherA_Test2 1
3 TeacherB_Test1 2
4 TeacherY_Test1 3
5 TeacherY_Test2 3
Make a note of where the tables diverge. In this case the [teachers] tables diverge after ID=2. So, insert the new rows from [teachers1] into [teachers], putting [teachers1].[ID] into [teachers].[oldID] so we can map old IDs to new ones:
INSERT INTO [teachers] ([teacher], [oldID])
SELECT [teacher], [ID] FROM [teachers1] WHERE [ID]>2
So now we have
[teachers]
ID teacher oldID
-- -------- -----
1 TeacherA
2 TeacherB
3 TeacherX
4 TeacherY 3
Now when we append the new rows from [tests1] into [tests] we can use an INNER JOIN on [teachers].[oldID] to adjust the foreign key values that get inserted:
INSERT INTO [tests] ([test_name], [teacher], [oldID])
SELECT [tests1].[test_name], [teachers].[ID], [tests1].[ID]
FROM [tests1] INNER JOIN [teachers] ON [tests1].[teacher]=[teachers].[oldID]
giving us
[tests]
ID test_name teacher oldID
-- -------------- ------- -----
1 TeacherA_Test1 1
2 TeacherA_Test2 1
3 TeacherB_Test1 2
4 TeacherX_Test1 3
5 TeacherY_Test1 4 4
6 TeacherY_Test2 4 5
Notice how the [teacher] foreign key has been mapped from the value 3 in [tests1] to 4 in [tests], reflecting the new [teachers].[ID] value for 'TeacherY'.
You can then repeat the process for child tables of [tests].
(Once the cleanup is complete you can remove the table links and drop the [oldID] columns.)
Is there any way to fix this programatically?
No. This must be done by a human capable of reading and understanding the data and taking decisions.
Create a query with an inner join between table one and table two, another query with an outer join between table one and table two, and another query with an outer join between table two and table one.
Now you can study the differences and decide which version of similar records to be kept and which records are completely new and should be kept - some with a new Primary Key.

SQL Constraint/Check on Join tables

I have three tables: store, product, storeproduct.
It doesn't really matter what's in the store and the product table, just know there is a storeID in the store table, and a productID in the product table. However the storeproduct table keeps track of the different products each store has. So the storeproduct table has two columns. The storeID column, and the productID column, both foreign keys from the store and the product table.
Is there a way to put a constraint or check on any of the table to make sure that a store must have more than 0 products, and less than 50 products.
Note: I do not want a select statement to do this. I just want to know if there is a way to put a constraint or a check when creating the tables.
The point of this is so a user cannot insert into the storeproduct table if there are already 50 products(rows) with the same storeID, or delete from the storeproduct table if deleting a row will cause the last row with that storeID to be gone.
The storeproduct table might look like this
storeID productID
1 1
1 2
1 3
2 4
2 5
2 6
2 7
3 4
3 2
3 6
3 1
3 8
Actually, depending on your database you may be able to do this.
Oracle (and maybe others) provide materialized views which you can apply constraints to. So you could create the MV with a column PRODUCTS_IN_STORES (being something like select storeID, count(*) as PRODUCTS_IN_STORES from stores left outer join storeproduct on store.storeid=storeproduct.storeid group by store.storeid .Then put a constraint on it asserting that PRODUCTS_IN_STORES is between 0 and 50 or whatever.
http://www.sqlsnippets.com/en/topic-12896.html
and
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:21389386132607
Not a complete answer for you, but something to think about and hopefully set you on your way.

How to relate 3 tables depending on event

I have a table that have information about different types of events that can be done by persons in two categories civil and worker
so for each one of them I have their respective tables
civil{ civil_id, name, age,telephone...} the primary key is civil_id
worker{ worker_id, name, duty, department...} the primary key is worker_id
then the event table has a list of all possible events
event {type_of_event} the primary key is type_of_event
then I am planing to store information in other table
with eventype, the person that did the job (worker or civil)
id event_type date person
-----------------------------------
1 type1 12-12-12 x
2 type1 05-12-10 y
3 type2 02-12-12 y
Now in this design I do not know how to relate whose person did the job if, I would had only a kind of person (aka civil) i would only store the civil_id in person field in this last table....but how to know if it was civil or worker, do I need other intermediate table?
Generally, there are to ways to model this type of situation...
Using Exclusive Foreign Keys
In the event, both civil_id and worker_id are NULL-able, but there is also a constraint ensuring exactly one of them is non-NULL at any given time:
CHECK (
(civil_id IS NOT NULL AND worker_id IS NULL)
OR (civil_id IS NULL AND worker_id IS NOT NULL)
)
Using Inheritance1
For more on inheritance, take a look at "Subtype Relationships" chapter in the ERwin Methods Guide and at this post.
1 Aka. category, subtyping, subclassing, generalization hierarchy...
In this can, you cannot set up a foreign key because you have multiple parent. In order to do fast search or to avoid full table scan define an index on column person on table event and join the table using LEFT JOIN. eg,
SELECT ....,
COALESCE(b.name, c.name) AS personname
FROM event a
LEFT JOIN civil b
ON a.person = b.civil_id
LEFT JOIN worker c
ON a.person = c.worker_ID
Adding INDEX
ALTER TABLE event ADD INDEX (person)

SQL field with multiple id's of other table

Could someone give me an idea how to create this database structure.
Here is an example:
Table "countries":
id, countryname
1, "US"
2, "DE"
3, "FR"
4, "IT"
Now I have another table "products" and in there I would like to store all countries where this product is available:
Table "products":
id,productname,countries
1,"product1",(1,2,4) // available in countries US, DE, IT.
2,"product2",(2,3,4) // available in countries DE, FR, IT.
My question:
How do I design the table structure in "products" to be able to store multiple countries?
My best idea is to put a comma-separated string in there (i.e. "1,2,4"), then split that string to look up each entry. But I doubt that this the best way to do this?
EDIT: Thank you all for your help, amazing! It was difficult to choose the right answer,
I finally chose Gregs because he pointed me to a JOIN explanation and gave an example how to use it.
You need an intersection table for that many-to-many relationship.
Table Country
CountryID, CountryName
Table CountryProduct
CountryID, ProductID
Table Product
ProductID, ProductName
You then Inner Join all 3 tables to get your list of Countries & Products.
Select * From Country
Inner Join CountryProduct On Country.CountryID = CountryProduct.CountryID
Inner Join Product On CountryProduct.ProductID = Product.ProductID
Without denormalizing, you'll need to add an extra table
Table Product countries
ProductID CountryID
1 1
1 2
1 4...
What you're talking about is normalisation. You have a many-to-many structure, so you should create another table to link the two. You should never (ok, pretty much never) use delimited strings to store a list of values in a relational database.
Here's an example of the setup:
product_countries table
productid | countryid
----------+-----------
1 | 1
1 | 2
1 | 4
2 | 2
2 | 3
2 | 4
You can use a foreign key to each other table, then make them both into a composite primary key.
You can then get a list of supported products for a country ID like this:
SELECT * FROM products, product_countries
WHERE products.id = product_countries.productid
AND product_countries.countryid = $cid
You could also make a third table countries_products with fields country_id and product_id.
the best approach for relational databases is the following :
One table for coutries, let's say
country_id, country_desc (country_id is primary)
one table for products, let's say
product_id, product_desc and as many columns as you want (product_id is primary)
if you had only one country for sure, it'd be enough to have a foreign key pointing to country_id in each product row. Having a foreign key asserts that there is an actual country behing a country_id referring to country table.
In your case you have several countries for a product, so add a separate association table
product_id, country_id
both keys primary and both foreign as well.