-1

How do I store data about classes of licences?

Data subset is People who have Licences and Licences have Classes.

Thanks to @karmakaze for this drawing.

+------+    +-------+     +---------+
|person|---*|licence|---1*|lic_class|
+------+    +-------+     +---------+

The people table has person_id and various name fields.

The licences have unique numbers issued by a government agency with an expiry date, licences have multiple classes.

Originally I was going to structure the licenses table as a linking table between lut_training (a.k.a lic_class above) and people

CREATE TABLE `licences` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `person_id` int(10) unsigned NOT NULL,
  `training_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `person_id` (`person_id`),
  KEY `training_id` (`training_id`),
  CONSTRAINT `licences_ibfk_3` FOREIGN KEY (`person_id`) REFERENCES `people` (`person_id`) ON UPDATE CASCADE,
  CONSTRAINT `licences_ibfk_4` FOREIGN KEY (`training_id`) REFERENCES `lut_training` (`training_id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

The lut_training table was originally just a lookup table to handle short and long names for licence classes e.g. (id="6", short="1F", long="1F Armed Guard").

Then I needed to put the licence_number and expiry_date somewhere, so it can either go in licences (probably best place for it) or lut_training. I settled for lut_training, but this is probably poor DB design.

CREATE TABLE `lut_training` (
  `training_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `training_short` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL,
  `training_long` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
  `expiry_date` date DEFAULT NULL,
  `licence_number` int(10) unsigned NOT NULL,
  PRIMARY KEY (`training_id`),
  KEY `training_long` (`training_long`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

I then discovered that each licence class does not get its own number or expiry date unlike the 8 other government licences I have. Plus classes can be added and removed at will. So there is no point in putting expiry_date and licence_number fields in the lic_class table.

10 classes currently exist 1A,1B,1C,1D,1E,1F,2A,2B,2C,2D. Someone may start with 1A & 1E, add 1C and then drop the 1A and retain 1E & 1C until their licence expires.

So now it seems stupid and inefficient to create lut_training and put a row in for every licence class when lots of that data is duplicated because most people in the dataset are multi-class licensees.

So I could do this and use 1 field in licence to hold a licence's classes:

+------+    +-------+
|person|---*|licence|
+------+    +-------+

That's a bit like how WordPress.org wp_options table is a mess of key:value pairs.

As SQL

CREATE TABLE `licences` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `person_id` int(10) unsigned NOT NULL,
  `expiry_date` date DEFAULT NULL,
  `licence_number` int(10) unsigned NOT NULL,
  `licence_types` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  KEY `person_id` (`person_id`),
  CONSTRAINT `licences_ibfk_3` FOREIGN KEY (`person_id`) REFERENCES `people` (`person_id`) ON UPDATE CASCADE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

The bit I'm struggling with is licence_types varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL, or even licence_types smallint NOT NULL,

That seems to violate database normalization specifically 1NF first normal form.

Plus I would have to do string or integer manipulation in order to update licence classes if I use a string or integer field instead of a separate lic_class table. 10 licence classes means 2^10=1024 bits of data or some pseudo- or literal- array field to store all the licence class combinations a person holds. That's not a lot of space.

Directions on how to approach this are appreciated.

Hopefully that is clearer for the detractors.

paulzag
  • 116
  • 1
  • 6
  • 1
    That seems a bit confusing. `lut_training` are the actual licenses (in terms of classes) and `licences` is a linking table between licenses and people (the issued licences)? – sticky bit Dec 30 '19 at 04:19
  • Yes @stickybit in simplifying the example in the first case, the `licences` table lost a lot of functionality, but you are right. Licences was just a linking table. Now it is all the data and the lookup table lut_training is no longer required. – paulzag Dec 30 '19 at 04:47
  • This is not clear. What do these tables have to do with licence classes or the text of your post? What is "licence_types"? Why do you "have to do string or integer manipulation" & in what design? Please give a [mre]. Use enough words, sentences & references to parts of examples to clearly & fully say what you mean. PS "That seems to violate database normalization." Why? What do you mean by "normalization"? PS Please don't insert EDITs/UPDATEs, just make your post the best presentation as of right now. Adding to something unclear doesn't make it clear. Please clarify via edits, not comments. – philipxy Dec 30 '19 at 07:47
  • Parts are clearer but this is still not clear. Please act on all of my last comment. Eg Which ["1NF"](https://stackoverflow.com/a/40640962/3404097)? Eg Why does it "seem" to violate it & why aren't you sure? Eg Please use just 1 name for a thing. Eg [mre]. Eg Ask just 1 specific question re 1 place you are stuck. PS When clear this will very likely be a faq. Before considering posting please always google any error message or many clear, concise & precise phrasings of your question/problem/goal, with & without your particular strings/names & site:stackoverflow.com & tags, & read many answers. – philipxy Dec 30 '19 at 09:59
  • Re different types/kinds of things--DB/SQL subtyping/inheritance/polymorphism/classes: [How can you represent inheritance in a database?](https://stackoverflow.com/q/3579079/3404097) – philipxy Dec 30 '19 at 10:16

1 Answers1

1

As I see it, a person can have zero or more licences (from different governments) and each license can have one or more licensed classes.

+------+    +-------+     +---------+
|person|---*|licence|---1*|lic_class|
+------+    +-------+     +---------+

The expiration date will go with the license. You can choose to denormalize the licenced classes into license depending on how much/little data there is per licensed class.

The tricky bit is the constraints. There should be a unique constraint on license (person_id, government). There should also be a unique constraint on lic_class(licence_id, license_type) as you can't have the same type more than once.

karmakaze
  • 34,689
  • 1
  • 30
  • 32
  • Very little data per license class. 1A = unarmed guard, 1B = Bodyguard, 1C = Crowd controller, 1D = Dog, 1F = Armed guard, etc. The class IS the data that's why I'm thinking denormalizing is smart here. – paulzag Dec 30 '19 at 09:24
  • 1
    In this case, it would make sense to not have a separate table for a person's licence classes. You could create a lookup table for these codes with descriptions with rows unique on (government_issuer, licence_class). – karmakaze Dec 30 '19 at 16:02