views:

553

answers:

7

I have two tables in my SQL database:

Company: - ID (autoincrement) - name - address - ...

Employees: - ID (autoincrement) - Company_id - internal_id - name - lastname

The problem is that I would like to have a employee id (internal_id) that is relative to the company they belong to. I got this dilema since I'm really been searching what would be the cleanest way to implement it.

One option would be to just make a kind of SELECT MAX(internal_id) FROM employees WHERE company_id = X, but the problem would be that if I happen to delete the last employee the next one would be created with the ID of the next.

Any ideas or suggestions?

PD: The reason of why I want to do this is that i dont want a user from company X create an employee that is for example ID=2000, while the last employee created in his company was, say, 1532. this would normally happen in a system in wich Company Y and Z also create employees on the same system. I want this ID not to use as a foreign_key, but to have it for internal (even documents or reports) use.

PD2: In this case the employees will never have to change companies

UPDATE

Thanks to all, there is really good advice here... It was hard to choose which was the best answer to me.. I picked that one beacuse I think is the simplest.

Guillermo

A: 

You should never delete a row from a relational database. You should expire that employee (IE, Set Employee.ActiveFlag = 0)

So if you had an internal_id (int) and did a (SELECT MAX(internal_Id) +1 FROM Employees WHERE Company_Id = [parameterCompanyID]) then that would work fine.

Nathan Koop
no, you would have concurrency problems, this is a very poor technique.
HLGEM
Thanks Nathan,I didn't know this aproach.. even Rails or any framework just DELETE the rows after a delete actions. Even although sound interesting why you would do this? Do you have any more references about this point?
Guillermo
This is called "soft delete". I'm unaware or any Rails implementation, but on CakePHP there is a "SoftDeletableBehavior", which does this automagically.
dr Hannibal Lecter
The reason why we don't delete rows from a database is because there may be data associated with that employee. For instance, Employee_Pay, or Employee_Vacation, etc... you'd have to delete (if you have your database set up correctly with FK's) from the referencing tables as well, this would destroy data that could be valuable. If you don't have your FK's set up properly you would then lose referential integrity.
Nathan Koop
@Guillermo, if employeeID 123 enters in important data, with a fk to their employee row, and you delete the employee row 123, what happens to the important data they entered? do you cascade delete it, does it become worthless, do you not display a name by it? The systems I work on need to keep the employee to show historical work. You'll need to filter them out of select lists, etc. but I know I need them in my system forever.
KM
I would think long and hard in regards to HLGEM's comment before you implement my solution though. Perhaps you could explain your problem domain further and someone with more knowledge could present a better solution.
Nathan Koop
A: 

if you are going to use the employee id as a fk, would you really want to delete it and all of their work in the DB? Make a status field in employee and set it "A"ctive or "I"nactive. The EmployeeID is best if it stays unique across all companies so just let it be autoincrement. Never let the users see the IDs, let them see the names only.

also don't name the columns ID, CompanyID or EmployeeID, will be more readable and easier to search on.

KM
A: 

Why not use GUIDs for your internal_ids? That way, you can always just do you inserts without fear of ID collisions.

You will not be able to avoid the delete anomaly given your current structure, but you really shouldn't do it the way you are proposing anyway. In order to reliably get the "next number," you will have to lock the table and that will cause all kinds of bad. Perhaps you can implement some kind of soft delete for employees as they tend to go and come sometimes anyway. :)

JP Alioto
I think that this would be helpful for instance if you were creating an app for a payroll company. Each company would want to have their employee's numbered differently and don't care about your db's PK.
Nathan Koop
I never let users see internal IDs, they deal in Names, I deal in IDs
KM
GUIDs? you mean something like COMPANYID-EMPLOYEEID.. I thought about it but is not what I want.. I wan each company feel they own "their" part of the system.. imagine you creating your first employee and then having 1110-7343?? Is that what you mean BTW?
Guillermo
I mean exactly what I said, a globally unique identifier. That way regardless of what company an employee works for (can change), you have an unique id. Consider the case whee Employee X changes from Company A to Company B, unless you have a unique identifier for that employee, you will have an update anomaly at least.
JP Alioto
+2  A: 

There are lots of questions here about creating "Business" IDs or numbers that are unrelated to the primary keys.

In your case I would create a column on the Company table "NextEmployeeID" Then when creating a new employee simply retrieve the value and increment it.

Now I leave it up to you to figure out what happens if the employee changes companies. :-)

DJ
i'd recommend the same thing
Schnalle
makes sure you don't create concurrency issues
KM
if the employee changes companies, he gets the new internal id of the new company and all the rows depending on the internal_id break :) but: you could delete and re-insert the employee, losing all the old data. or you could "deactivate" the employee in the old company instead of deleting him while adding a reference to the old self in the new emp-record, so you can track him around different companies. wheooo!
Schnalle
thanks, I just edited the post to clarify that employees changing companies would *not* be an escenario.
Guillermo
+13  A: 

No, don't do that! This will create many many problems in your database (you have to worry about concurrency issues among other things) to solve something that is NOT a propblem and is, in fact, correctly designed. The id should be meaningless and gaps are unimportant. You would want a unique index on the employeeid/companyid combination to ensure no employee is assigned to multiple companies.

Your employee id should be something that never needs to be changed. If you make some sort of silly company based ID and company A buys out company B and becomes company C, you end up having to change all the ids and all the related tables. In your current design you only need to update the company code but not the related tables.

HLGEM
+1 HLGEM is right!
Bill Karwin
+1 User only see names and the Database only uses IDs
KM
Unfortunately "silly" company based IDs are sometimes a valid business requirement
DJ
I edited the post to clarify why I think I would need this, I know is ackward, but I dont wnat to use this internal_id for anything else!
Guillermo
@DJ exactly thats the point
Guillermo
@Guillermo, add a new field that is not a key or FK for the silly company number that they want, a display only field like name, you could still keep them in sequence. don't let silly company logic break your keys!
KM
@HLGEM how would you implement this requirement then taking into account concurrency issues?
Nathan Koop
@mike, yes but.. whats the simpler and best way to get this number generated in, say, mysql?
Guillermo
I would not implement the requirement. First it seems from the description to be a personal prefernce not a requirement. If it was given to me as a requirement, I would tell the person who set the requirement why it was a bad idea and how badly it could destroy their data integrity and how much extra it woul cost in terms of development time and performance. This is a seriously flawed idea.
HLGEM
If someone was foolish enough to try to do this, then they would have to use transactions and lock the tables. The best method is to use a table to pull the next id from for each client and then in the same transaction update that value to the next higher one, so the next person seeking an id has to wait until the table is updated.
HLGEM
+4  A: 

Create a separate table:

CREATE TABLE t_identity (company INT NOT NULL PRIMARY KEY, id INT NOT NULL)

and issue:

INSERT
INTO    t_identity (@company, 1)
ON DUPLICATE KEY UPDATE
SET     id = id + 1

before inserting a new employee.

Quassnoi
+1, @Guillermo, if you must have IDs based on company this is the best way to go. I think unique employee ids across all companies is easier, and shouldn't matter, don't let the user ever see the ID, it is "internal". If they want an employee number, give them another field that is not a key or fk (like a name field) that they can see/control.
KM
A: 

I would create a separate table, as Quassnoi suggested, with the company and the current max sequence number. But I would wrap it in a stored proc or UDF (does MySQL do UDFs?) that accepts the company as an input, increments the current value and returns that incremented value.

John M Gant