Practices regarding Nullable Foreign Keys & Alternatives to it - database

I am currently working on designing and implementing a database.
We are running a conference, and there are two ways for attendees to register to the conference. The first is that the register and pay online - quite straightforward. The second is that they physically attend a "registration day" where they pay cash, and are added by one of our many admins. In this case, we would like to keep track of which admin added the attendee - for internal purposes.
We believe that it is best to store all attendees in the same table, and we would need a foreign key to track the admin that each attendee was added by. But for attendees registering online and paying themselves, we simply want to indicate that they were not added by any admin (so the foreign key would be Null).
Is the proposed idea of nullable foreign keys bad practice? Are there any benefits to storing attendees in different tables, where attendees registering online do not have a column for addedBy at all and cash-paying attendees have a non-nullable addedBy column?

I wouldn't store attendees in two (or more?) separate tables. They belong into the same table, and there should be a column which will tell you how someone got registered. Will it be "admin name" (or ID), checkbox, radio button item, whatever - just keep them in the same table.
Why? Because currently you think of two tables. It means that all code you write will have to be "duplicated" for those two tables - all inserts, updates, selects ... everything.
Now imagine that someone gets a super idea of the 3rd way of registration (snail mail, for example). What will you do then? Create the 3rd table, and triple code you wrote? That would be a really bad practice.
There's nothing "wrong" if a foreign key column doesn't contain any value, if you decide to do so - just document it. For example (Oracle syntax; would be similar in any other database):
SQL> create table admins
2 (id_admin number constraint pk_adm primary key,
3 name varchar2(20) not null
4 );
Table created.
SQL> create table attendees
2 (id_attendee number constraint pk_att primary key,
3 name varchar2(20) not null,
4 id_admin number constraint fk_att_adm references admins (id_admin)
5 );
Table created.
A few sample rows:
SQL> -- admin
SQL> insert into admins (id_admin, name)
2 select 1, 'Little' from dual;
1 row created.
SQL> -- attendees
SQL> -- registered online
SQL> insert into attendees (id_attendee, name) values (1, 'Mike');
1 row created.
SQL> -- registered on site
SQL> insert into attendees (id_attendee, name, id_admin) values (2, 'Scott', 1);
1 row created.
What do we know about them? It has to be outer join because - if it's not - you won't get info about people registered online:
SQL> select t.id_attendee, t.name attendee, a.name registered_by
2 from attendees t left join admins a on a.id_admin = t.id_admin;
ID_ATTENDEE ATTENDEE REGISTERED_BY
----------- -------------------- --------------------
1 Mike
2 Scott Little
SQL>
If you don't want to allow NULL values for a foreign key column, then a simple (and not bad at all) option is to create a "dummy" admin (let's call it "Online"):
SQL> insert into admins (id_admin, name) values (0, 'Online');
1 row created.
Set it for all users registered online (their id_admin column value is NULL):
SQL> update attendees set id_admin = 0 where id_admin is null;
1 row updated.
Disallow NULL values for a foreign key column:
SQL> alter table attendees modify id_admin not null;
Table altered.
Now you can use inner (instead of outer) join:
SQL> select t.id_attendee, t.name attendee, a.name registered_by
2 from attendees t join admins a on a.id_admin = t.id_admin;
ID_ATTENDEE ATTENDEE REGISTERED_BY
----------- -------------------- --------------------
1 Mike Online
2 Scott Little
SQL>
Snail mail? No problem, you don't have to modify any code you wrote so far:
SQL> insert into admins (id_admin, name) values (3, 'Snail mail');
1 row created.
SQL> insert into attendees (id_attendee, name, id_admin) values (3, 'King', 3);
1 row created.
SQL> select t.id_attendee, t.name attendee, a.name registered_by
2 from attendees t join admins a on a.id_admin = t.id_admin;
ID_ATTENDEE ATTENDEE REGISTERED_BY
----------- -------------------- --------------------
1 Mike Online
2 Scott Little
3 King Snail mail
SQL>

Related

How to design schema many to many relations

I have table of customer and table of policies and table of coverage.
Each customer have 1 or more policies
Each policy have 1 customer
each policy have 1 or more coverage and each coverage appear in 1 or more policies
My problem is when I have policy with multiples coverage I don't know how to implement it in the schema so when I open policy I want the ability to add multiple coverage to my policy.
How the schema will look like?
Best regards to all
From my point of view, it is simpler to show than explain. This is Oracle syntax, but never mind that. I used only necessary primary and foreign key columns, just to illustrate the problem.
Customer is simple:
SQL> create table customer
2 (id_customer number primary key);
Table created.
Policy has a foreign key constraint that points to customer:
SQL> create table policy
2 (id_policy number primary key,
3 id_customer number references customer
4 );
Table created.
Coverage is also simple:
SQL> create table coverage
2 (id_coverage number primary key);
Table created.
This is what bothers you: how to store policies with multiple coverages - in a separate table! whose columns make foreign key constraints, pointing to appropriate tables, while its primary key is composite & made up of both columns:
SQL> create table policy_x_coverage
2 (id_policy number references policy,
3 id_coverage number references coverage,
4 --
5 constraint pk_pxc primary key (id_policy, id_coverage)
6 );
Table created.
SQL>

SQL Server Database Design - 1 Table vs 2 Tables

I have a table named Report and another named ReportRights. The purpose of these tables are to restrict access to a report based on a user or user group.
The Report Rights table is
ReportRightsId (int - Surrogate Key)
ReportId (int - Foreign Key to Report)
UserId (int - null FK to Users)
GroupId (int - null FK to Groups)
HasAccess (bit default 0)
The issue with this is that I want to add a Unique Constraint to ReportId+UserId as well as to ReportId+GroupId but in this table structure I cannot because ReportId 1, UserId 1, Group Id NULL, and trying to add a permission for report id 2 trips the reportid 1 groupid null unique constraint.
Is this bad design? Should I have 2 tables: ReportGroupRights and ReportUserRights instead? If i were to make a front-end UI they would both be managed by the same 'grid' so 1 table makes sense to me...but the inability to enforce only 1 user record or only 1 group record is problematic.
What is the best practice for this problem?
It isn't bad design.
Make GroupId nullable, so you can as well enter permissions for user only.
Also, unique constraint should be on three columns together: ReportId, UserId, GroupId.
So you can allow more then one report per user/group.

Access Relationship Design

I am fairly green when it comes to working with Access and databases in general.
I am asking for your help in figuring out how to set the correct relationships for three tables:
Table 1 contains:
(no unique ID)
SalesTripID
EmployeeName
StartDate
EndDate
*Each record on this table is related to 1 specific employee's 1 specific sales trip
Table 2 contains:
HotelName
HotelStart
HotelEnd
HotelTotal
*This table may contain multiple records that belong to only 1 record on table 1 (for instance, an employee would stay at 2 hotels during their sales trip)
Table 3 contains:
(no unique ID)
MealVendor
MealDate
MealTotal
*This table, similar to Table 2, may have multiple records in it that are tied to the 1 SalesTripID
How do I set something up to show me each SalesTripID, the multiple Table 2, and the multiple Table 3 records associated with it? Do I need to add a Primary Key anything other than Table 1? Is writing a query involved to display the information? Because I am so green, any and all feedback is welcome.
The following is my recommendation:
Add a SalesTripId field on tables 2,3. This is called a ForeignKey.
If SalesTripId in Table1 is not unique (i.e. each employee can have a trip with the same Id as another employee), add another field (Id) in Table1. You can use Access' AutoNumber type for that field.
I recommend always having a primary key in your tables. But you can skip the Id fields in tables 2,3.

Foreign Key fails to create

I want a foreign key between 2 tables , so i try it like i always do. Now the issue i'm having is he fails to create , and by the looks of it it fails to create because there is already a key but there isnt.
- Unable to create relationship
'FK_tbl_Paramed_RegistratieBehandelingen_Users'.
The ALTER TABLE statement conflicted with the
FOREIGN KEY constraint "FK_tbl_Paramed_RegistratieBehandelingen_Users".
The conflict occurred in database "Nestor_Server",
table "dbo.Users", column 'UserID'.
Ive checked if they have the same type , they do(bigint) so don't get why he won't create it
It is possible that you have records in RegistratieBehandelingen(Not sure about the table name) which is not present in Users Table.
select * from RegistratieBehandelingen a where UserID IS NULL or
not exists (select 1 from Users b where b.UserID= a.UserID)
This means that you have child data with no matching parent ID.
Run the following to see if you get any results:
SELECT *
FROM tbl_Paramed_RegistratieBehandelingen r
LEFT JOIN Users u on r.UserID = u.UserID
WHERE u.UserID IS NULL
(changing table and column names where appropriate)
If you get any results then it should show which records contains UserIDs that don't match to Users.
After the above query, you may want to delete non existing UserId from table tbl_Paramed_RegistratieBehandelingen or insert them in table Users .

In which cases will Oracle create indexes automatically?

As far as I know (this page) Oracle automatically creates an index for each UNIQUE or PRIMARY KEY declaration. Is this a complete list of cases when indexes are created automatically in Oracle?
I'll try to consolidate given answers and make it community wiki.
So indexes are automatically created by Oracle for such cases:
APC: For primary key and unique key unless such indexes already exist.
APC: For LOB storage and XMLType.
Gary: For table with a nested table.
Jim Hudson: For materialized view.
Firstly, Oracle does not always create an index when we create a primary or unique key. If there is already an index on that column it will use it instead...
SQL> create table t23 (id number not null)
2 /
Table created.
SQL> create index my_manual_idx on t23 ( id )
2 /
Index created.
SQL> select index_name from user_indexes
2 where table_name = 'T23'
3 /
INDEX_NAME
------------------------------
MY_MANUAL_IDX
SQL>
... note that MY_MANUAL_IDX is not a unique index; it doesn't matter ...
SQL> alter table t23
2 add constraint t23_pk primary key (id) using index
3 /
Table altered.
SQL> select index_name from user_indexes
2 where table_name = 'T23'
3 /
INDEX_NAME
------------------------------
MY_MANUAL_IDX
SQL> drop index my_manual_idx
2 /
drop index my_manual_idx
*
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key
SQL>
There is another case when Oracle will automatically create an index: LOB storage....
SQL> alter table t23
2 add txt clob
3 lob (txt) store as basicfile t23_txt (tablespace users)
4 /
Table altered.
SQL> select index_name from user_indexes
2 where table_name = 'T23'
3 /
INDEX_NAME
------------------------------
MY_MANUAL_IDX
SYS_IL0000556081C00002$$
SQL>
edit
The database treats XMLType same as other LOBs...
SQL> alter table t23
2 add xmldoc xmltype
3 /
Table altered.
SQL> select index_name from user_indexes
2 where table_name = 'T23'
3 /
INDEX_NAME
------------------------------
MY_MANUAL_IDX
SYS_IL0000556081C00002$$
SYS_IL0000556081C00004$$
SQL>
No, we're getting closer but that's not quite a complete list yet.
There will also be an index automatically created when you create materialized view since Oracle needs to be able to quickly identify the rows when doing a fast refresh. For rowid based materialized views, it uses I_SNAP$_tablename. For primary key materialized views, it uses the original PK name, modified as necessary to make it unique.
create materialized view testmv
refresh force with rowid
as select * from dual;
select index_name from user_indexes where table_name = 'TESTMV';
Index Name
--------------
I_SNAP$_TESTMV
And another one, if you create a table with a nested table you get an index created automatically. Object based storage in general can do this as there can be hidden tables created.
I think schema-based XMLTypes will also do it.
Yes, that's the complete list. Oracle automatically creates an index for each UNIQUE or PRIMARY KEY declaration.

Resources