Build a field-lookup-query using the value of another field - ms-access

Creating a mini-database with access, i came across this problem:
For the background, i have two tables:
Table: Items and Table: Actions
ID(PK) Name ID(PK) Name
------------------ ----------------
1 Thing1 1 Move
2 Thing2 2 Delete
3 Thing3
I created a query that lists available actions for each item:
Query: AvailableActions
Item_ID Action_ID
------------------------
1 2 //Thing1 can be deleted
2 1 //Thing2 can be moved
2 2 //Thing1 can be deleted
(no more records)
Now i want to populate a third table that lists the history of objects
Table: History
ID(PK) Item_ID Action_ID
----------------------------------
1 1 2
2 1 2
3 2 1
4 2 2
So i'm trying to make a lookup-field for Action_ID, where i can only pick values that are allowed for the choosed item. However, be it in design mode or SQL mode, i can't get the value of that field.
Do you have any hints?

Before you sort out the UI (mop the floor...), ensure you have the required constraint on the table (...fix the leak) e.g. ANSI-92 Query Mode SQL DDL:
ALTER TABLE History ADD
CONSTRAINT fk__history__AvailableActions
FOREIGN KEY (Item_ID, Action_ID)
REFERENCES AvailableActions (Item_ID, Action_ID);
...assuming you already have the required unique constraint on AvailableActions (Item_ID, Action_ID).

If you want a list of actions that can be applied to Item X then you can generate this with:
SELECT Actions.ID, Actions.Name FROM Actions INNER JOIN AvailableActions
ON Actions.ID = AvailableActions.ActionID WHERE Actions.Item_ID = X
When you talk about deleting "Thing 1", do you actually intend to delete the record from the table or does that record proxy for something else (like a disk file). If you actually delete it, you will have trouble establishing a PK / FK relationship between Items and AvailableActions if that was your intent.

Well as i stated in comments, the only way i achieved this goal is by adding a sub-form to the Items form. (unable to specify this with the structure only, as those Available_actions are computed depending on that same History table)

Related

Database design: When not to use foreign keys?

I am unsure what is the rule of thumb of when to use foreign keys and when it's better to insert an "unreferenced" value in regard of disk space needed, performance etc.
Let's say I have three tables:
Table 1: itemGroup (for to populate a dropdown menu with items)
ID title
1 Active/Inactive Options
2 Car Brands
3 Ratings
4 Languages
Table 2: item (the itemID will be the actual value in the dropdown and used as a foreign key )
itemID listID title
1000 1 active
1001 1 inactive
1002 2 Porsche
1003 2 Audi
1004 3 1-Star Rating
1005 3 2-Star Rating
1006 4 en
1007 4 de
Table 3: exampleTable
ID car rating active language
So my question is whether I should insert foreign keys in table 3 using the itemID, or would it make more sense to use a 1/0 for active/inactive and to use let's say 1,2,3,4,5 as an integer for the rating? Guess for the car it's quite self explaining that the foreign keys are better but in some cases it's hard to decide as my "item" table can be very big and therefore the itemID has more digits than the actual "value" it might be referring to and in a big database this at some point will make a difference in space and I guess also performance wise because with foreign keys I need to make joints.
UPDATE:
I added the field "language" as maybe here the issue gets illustrated better. So if I'd store a language foreign key (e.g. "1006"):
I need to store over and over a 4-digit int in my exampleTable, instead of just a 2-character varchar
I can't do a an easy query like "SELECT * from exampleTable WHERE language=en"
Why would it be better to use a foreign key here?

many to many relationship bridge table dilemma

salesman
========
uId
salesGroupLinked
================
uId
groupId
//add performacesScore field here
group
======
groupId
I have 3 table above that formed many to many relationship, and I would add a field 'performaces' (INT) so that each salesman can have a score in each group. And I believe it should be located at salesGroupLinked table. But since uId and groupId is FK, I can't insert / edit the data (I'm using phpmyadmin). I can't make the performacesScore field unique since they can be same value for example a salesman get 10 and another get the same.
I got this msg :
This table does not contain a unique column. Grid edit, checkbox,
Edit, Copy and Delete features are not available.
describe salesGroupLinked
The tool is simply telling you that there can be several entries for a uId-groupId combination. Example:
uId groupId performacesScore
1 1 10
1 2 20
2 1 30
2 1 30
2 1 40
2 2 20
Now imagine this data is shown to you and you make the first 2/1/30 a 2/1/50. What update statement could the tool sent to the dbms?
update salesGroupLinked set performacesScore = 50
where uId = 2 and groupId = 1;
This would update three records instead of one.
update salesGroupLinked set performacesScore = 50
where uId = 2 and groupId = 1 and performacesScore = 30;
This would still update two records instead of one.
So in order to properly update and delete, you must tell the dbms what makes the records unique. There are four possibilities:
If you never want to update or delete single records, leave it as is.
If you want to be able to update and there can only be one entry for a uId-groupId combination, then tell the dbms so and make uId plus groupId the primary key of your table.
If you want to be able to update and there can be duplicates for a a uId-groupId combination, but a uId-groupId-performacesScore combination will always be unique, then make these three the table's primary key.
If you want to be able to update and there can be duplicates for any combination, then give the table another column for a technical id and make this the primary key.

Foreign key to multiple records

I have two tables like so:
Table1
| itemid (PK)|
| typeid (FK)|
| item count |
Table2
| typeid (PK)|
| type name |
Table2 have 4-5 records in it. ...And I'm stuck at this point... Basically the item can be of all types (it can have typeid (FK) = NULL or all 4-5). I just can't get my head round this..At the moment when I add new item to Table1 (using phpmyAdmin ui) it gives me a choice of one FK as a dropdown list.. But what if the item is of two or three types..? It's probably a piece of cake but my brain has stalled... What would be the best way to do it?
Your design support 1-to-many relationship. Perhaps, you need many-to-many relationship between Table1 and Table2. If yes, you need to add a new link table:
Table3:
itemid(FK)
typeid(FK)
You can define a composite key on itemid and typeid in Table1.
That allows combinations of 1-1, 1-2, 3-1, 3-4, etc on itemid and typeid values.
Even your question is not much clear, but what I am understanding that you are not able to insert records in child table (table1) as record does not exist in master table (table 2).
So first you need to add required typeid in master table (table2) then you can enter in its child table.
If your requirement is something different then elaborate, so that you can get better help from community.

Primary key on 2 tables

i have two tables, one 'master' and one 'child' table.
Each table has a field named 'ProductNo', which is defined as PRIMARY KEY and UNIQUE.
Is it possible to define the field 'ProductNo' in the table 'child' and the same field in table 'master' as PRIMARY + UNIQUE together?
master:
ID | ProductNo
child:
ID | MasterID (FK on master.ID) | ProductNo
Relation >> 1 (master) : n (child)
example data:
master:
1 | 1234
2 | 4567
child:
100 | 1 | 3333
101 | 1 | 4444
102 | 2 | 5555
103 | 1 | 1234 <----- NOT ALLOWED! PRODUCT NO ALREADY EXISTING IN TABLE `MASTER`
104 | 2 | 1234 <----- NOT ALLOWED! PRODUCT NO ALREADY EXISTING IN TABLE `MASTER`
It is needed to check on inserting/updating table 'child' if 'ProductNo' already exists in table 'master'.
How can I define it?
Or am I needed to create a trigger for this?
TIA Matt
no, there is no such thing as composite PKs among tables.
Just for data consistency, if the Ids are the same, you should add a FK from child to the master.
To solve your problem, a trigger with a check like this:
if exists (select 1 from master where prodcutId=new_productId)
would be a good idea
EDIT:
actually the best idea is to have only one table called product with a ID and a masterID field with a relation to itself. The way you have today Im pretty sure that you have a lot of duplicate data and you are stuck with 2 levels on hierarchy.
(Original answer) You can declare a foreign key from master to child, even if that foreign key points to the primary key of child. This would be a one to zero-or-one relationship, and is not that uncommon. A row cannot exist in child without a matching row in master already being inserted, but a row can exist in master without a matching child row. Your inserts therefore need to happen in the order master then child.
(Edited in light of question edit) HOWEVER, in your case, the column you are referring to looks like it should not actually be the primary key of either table, but rather you have a separate primary/foreign key, and the column in question needs to be unique across the two tables, which has become clear now you've edited some sample data into your question. In this case, you'd be best to use a trigger on both tables, to check existence in the other table and prevent the insert/update if the ProductNo already exists.
Just as #DavidM said, it can be done, but it seems you are with some modelling issues. First, if you have a natural primary key ProductNo, why do you define a surrogate ID? The other thing you might consider is to combine these two tables into a single one (as might make sense for most of 1-to-1 cases).
Are you sure you need the two tables?
Keep just one, having productID plus parentID.
Then productID can be a primary key and auto increment, while everything having a parentID other than null (f.keyed to the same table) would be a child item.
You can add a column named ProductNo in child table and add a foreign key reference to the parent table.

Allow/require only one record with common FK to have "primary" flag

Firstly, I apologise if this is a dupe - I suspect it may be but I can't find it.
Say I have a table of companies:
id | company_name
----+--------------
1 | Someone
2 | Someone else
...and a table of contacts:
id | company_id | contact_name | is_primary
----+------------+--------------+------------
1 | 1 | Tom | 1
2 | 2 | Dick | 1
3 | 1 | Harry | 0
4 | 1 | Bob | 0
Is it possible to set up the contacts table in such a way that it requires that one and only one record has the is_primary flag set for each common company_id?
So if I tried to do:
UPDATE contacts
SET is_primary = 1
WHERE id = 4
...the query would fail, because Tom (id = 1) is already flagged as the primary contact for company_id = 1. Or even better, would it be possible to construct a trigger so that the query would succeed, but Tom's is_primary flag would be cleared by the same operation?
I am not too bothered about checking whether company_id exists in the companies table, my PHP code would already have performed this check before I got to this stage (although if there is a way to do this in the same operation it would be nice, I suppose).
When I initially thought about this I thought "that will be easy, I'll just add a unique index across the company_id and is_primary columns" but obviously that won't work as it would restrict me to one primary and one non-primary contact - any attempt to add a third contact would fail. But I can't help feeling there would be a way to configure a unique index that gives me the minimum functionality I require - to reject an attempt to add a second primary contact, or reject an attempt to leave a company with no primary contact.
I am aware that I could just add a primary_contact field to the companies table with an FK to the contacts table but it feels messy. I don't like the idea of both tables having an FK to the other - it seems to me that the one table should rely on the other, not both tables relying on each other. I guess I just think that over time there is more chance of something going wrong.
To sum up:
How can I restrict the contacts table so that one and only one record with a given company_id has the is_primary flag set?
Anyone have any thoughts on whether two tables having FKs to each other is a good/bad idea?
Circular refenences between tables are indeed messy. See this (decade old) article: SQL By Design: The Circular Reference
The cleanest way to make such a constraint is to add another table:
Company_PrimaryContact
----------------------
company_id
contact_id
PRIMARY KEY (company_id)
FOREIGN KEY (company_id, contact_id)
REFERENCES Contact (company_id, id)
This will also require a UNIQUE constraint in table Contact on (company_id, id)
You could just do a query before that one setting
UPDATE contacts SET is_primary = 0 WHERE company_id = .....
or even
UPDATE contacts
SET is_primary = IF(id=[USERID],1,0)
WHERE company_id = (
SELECT company_id FROM contacts WHERE id = [USERID]
);
Just putting an alternative out there - personally I'd probably look to the FK approach though instead of this type of workaround i.e. have a field in the companies table with a primary_user_id field.
EDIT method w/o relying on a contact.is_primary field
Alternative method, first of all remove is_primary from contacts. Secondly add a "primary_contact_id" INT field into companies. Thirdly, when changing the primary contact, just change that primary_contact_id thus preventing any possibility of there being more than 1 primary contact at any time and all without the need for triggers etc in the background.
This option would work fine in any engine as it's simply updating an INT field, any reliance on FK's etc could be added/removed as required but at it's simplest it's just changing an INT fields value
This option is viable as long as you need one and precisely one link from companies to contacts flagging a primary