The new forums will be named Coin Return (based on the most recent vote)! You can check on the status and timeline of the transition to the new forums here.
The Guiding Principles and New Rules document is now in effect.

SQL total overlapping specialization

RichyRichy Registered User regular
I've been racking my brains over this for a while: say I've got a general class that specializes in several specific subclasses. Specialization is mandatory for the general class, but the specialized classes are not mutually exclusive and an instance can belong to multiple classes at once. How do you code that in SQL?

For example: Say you have a database for a company with a general table "Employee" that contains the attributes "employeeID" (primary key) and general attributes (name, address, etc.). And you have specialized tables for the jobs in the company like "Engineer", "Secretary", "Manager", etc., each inheriting "employeeID" as a foreign key and with their own specific attributes unique to their job. An employee must have at least one job in the company, but can wear many hats and thus hold several jobs at once.

I can make each individual table easily enough, but where I get stumped is with the constraints. I just can't figure out how to make it so that an Employee tuple must exist in one or more specialized tables - every solution I come up with using triggers, procedures, constraints, etc., either allows an Employee tuple with no specialized tuples or forces a single specialized tuple. I've googled the problem in every phrasing I can think of, but come up empty.

Anyone here have any ideas?

sig.gif

Posts

  • bowenbowen Sup? Registered User regular
    edited July 18
    You'd want a junction table, because this is a "one to many"/"many to many". So you'd have your table with employee details (employeeID, name, address), a table with all the jobs and an ID ("roleID") for those jobs, then you'd have a third table with the employeeID and "roleID" from the jobs table, you make a composite key based off those two fields. So the employeeID can show as many times as you have jobs.

    bowen on
    not a doctor, not a lawyer, examples I use may not be fully researched so don't take out of context plz, don't @ me
  • RichyRichy Registered User regular
    bowen wrote: »
    You'd want a junction table, because this is a "one to many"/"many to many". So you'd have your table with employee details (employeeID, name, address), a table with all the jobs and an ID ("roleID") for those jobs, then you'd have a third table with the employeeID and "roleID" from the jobs table, you make a composite key based off those two fields. So the employeeID can show as many times as you have jobs.

    But how do you enforce the requirement that an employee must have at least one job in this setup?

    sig.gif
  • bowenbowen Sup? Registered User regular
    edited July 18
    Richy wrote: »
    bowen wrote: »
    You'd want a junction table, because this is a "one to many"/"many to many". So you'd have your table with employee details (employeeID, name, address), a table with all the jobs and an ID ("roleID") for those jobs, then you'd have a third table with the employeeID and "roleID" from the jobs table, you make a composite key based off those two fields. So the employeeID can show as many times as you have jobs.

    But how do you enforce the requirement that an employee must have at least one job in this setup?

    I don't think there's any real in built syntax-y way of doing this simply, that's the realm of business logic where you start getting into triggers/functions/stored procedures like you were talking about.

    You could have a trigger autoinsert when an employee is added that gives them a job role of unspecified, or you could just have a function that requires at least one job role ID (or many) and errors out if one isn't provided.

    I could be wrong about that though. I usually put this kind of enforcement into my code itself, I hate business logic in my databases.

    bowen on
    not a doctor, not a lawyer, examples I use may not be fully researched so don't take out of context plz, don't @ me
  • FeralFeral MEMETICHARIZARD interior crocodile alligator ⇔ ǝɹʇɐǝɥʇ ǝᴉʌoɯ ʇǝloɹʌǝɥɔ ɐ ǝʌᴉɹp ᴉRegistered User regular
    edited July 19
    Richy wrote: »
    I've been racking my brains over this for a while: say I've got a general class that specializes in several specific subclasses. Specialization is mandatory for the general class, but the specialized classes are not mutually exclusive and an instance can belong to multiple classes at once. How do you code that in SQL?

    For example: Say you have a database for a company with a general table "Employee" that contains the attributes "employeeID" (primary key) and general attributes (name, address, etc.). And you have specialized tables for the jobs in the company like "Engineer", "Secretary", "Manager", etc., each inheriting "employeeID" as a foreign key and with their own specific attributes unique to their job. An employee must have at least one job in the company, but can wear many hats and thus hold several jobs at once.

    I can make each individual table easily enough, but where I get stumped is with the constraints. I just can't figure out how to make it so that an Employee tuple must exist in one or more specialized tables - every solution I come up with using triggers, procedures, constraints, etc., either allows an Employee tuple with no specialized tuples or forces a single specialized tuple. I've googled the problem in every phrasing I can think of, but come up empty.

    Anyone here have any ideas?

    I broadly agree with bowen but some commentary:

    First off, I agree it would be better for this kind of data validation to be done at a higher level than the database. Database-level validation (SQL constraints, DRI, etc) are good and IMO necessary for simpler logic but more complicated logic can get finicky (as you're discovering).

    Secondly...

    "And you have specialized tables for the jobs in the company like "Engineer", "Secretary", "Manager", etc., each inheriting "employeeID" as a foreign key and with their own specific attributes unique to their job."

    This is kind of a weird way to do it. Is there a specific reason why you need each job to have a separate table? Here's how I'd consider doing it. (This will also conform better to common data normalization & database design principles.)

    Imagine you have three relevant tables.

    Table 1: Employees
    Table 2: Roles
    Table 3: role Assignments

    The employee table might look like this:
    employeeID givenName surname
    00000001   Jean-Luc  Picard
    00000002   William   Riker
    00000003   Beverly   Crusher
    

    Roles:
    roleID   roleName
    00000001 Captain
    00000002 First Officer
    00000003 Doctor
    00000004 Chief Medical Officer
    00000005 Saxophonist
    00000006 Archaeologist
    00000007 Dance Instructor
    

    Assignments:
    assignmentID employeeID roleID
    00000001     00000001   00000001
    00000002     00000001   00000006
    00000003     00000002   00000002
    00000004     00000002   00000005
    00000005     00000003   00000003
    00000005     00000003   00000004
    00000005     00000003   00000007
    
    (The roleID could then be defined as foreign keys.)

    If you want to answer "what roles does Jean-Luc Picard have?" then you'd do something like "SELECT FROM [Assignments] WHERE [employeeID]='00000001'"
    "Who has the role of Doctor?" then it's "SELECT FROM [Assignments] WHERE [roleID]='00000003'"

    Feral on
    every person who doesn't like an acquired taste always seems to think everyone who likes it is faking it. it should be an official fallacy.

    the "no true scotch man" fallacy.
  • SeñorAmorSeñorAmor !!! Registered User regular
    I'm curious why an employee must have a job. I agree that, logically, it makes sense, but it could cause issues from a data storage perspective. In your scenario, if a person changes their job, you'll have to ensure the INSERT of the new job comes before the DELETE of the previous job or you'll run into problems.

    I'm actually creating a web app that has a hierarchy similar to yours. Users have specific roles within groups they're assigned to, but it occurred to me that a person could have their roles temporarily rescinded but I'd still want them to have an account to access the app. I don't want to have to re-add a user every time they come back from a hiatus, so I didn't implement a requirement for them to have a role (or even a group).

    You could always run a report of users that don't have jobs assigned to them and figure out why that's the case.

  • bowenbowen Sup? Registered User regular
    Maybe an issue trying to avoid certain joins hiding rows when there's nulls?

    not a doctor, not a lawyer, examples I use may not be fully researched so don't take out of context plz, don't @ me
  • RichyRichy Registered User regular
    SeñorAmor wrote: »
    I'm curious why an employee must have a job.

    To be honest, it's a "prove it can be done" kind of thing. Otherwise, as several people suggested, I could have gone with a business logic solution and saved myself some headaches.

    I figured out one solution I think. I added a vector of bits in the Employees table with one bit per job, with a table constraint stating that at least one bit must be 1 (or more precisely, that the sum of bits must be greater than 0). Then I added triggers so that after each time a bit changes to 1 (or 0) a tuple is added to (or deleted from) the corresponding job table, and triggers that prevent the deletion of tuples from jobs tables if the corresponding bit is 1. I think that solves it.

    sig.gif
  • bowenbowen Sup? Registered User regular
    Richy wrote: »
    SeñorAmor wrote: »
    I'm curious why an employee must have a job.

    To be honest, it's a "prove it can be done" kind of thing. Otherwise, as several people suggested, I could have gone with a business logic solution and saved myself some headaches.

    I figured out one solution I think. I added a vector of bits in the Employees table with one bit per job, with a table constraint stating that at least one bit must be 1 (or more precisely, that the sum of bits must be greater than 0). Then I added triggers so that after each time a bit changes to 1 (or 0) a tuple is added to (or deleted from) the corresponding job table, and triggers that prevent the deletion of tuples from jobs tables if the corresponding bit is 1. I think that solves it.


    Essentially bit masking like you'd see with enums? Definitely an idea that could work.

    not a doctor, not a lawyer, examples I use may not be fully researched so don't take out of context plz, don't @ me
  • discriderdiscrider Registered User regular
    edited July 26
    Linked list the role tables to the employee table like some sort of maniac was my thought.
    Employee table would/must point to a role, and any other roles would then get pointed to from other role tables.
    And then someone deletes the middle record of the list and it doesn't work anymore.

    I like the previously mentioned solutions much better.

    discrider on
Sign In or Register to comment.