My current schema looks like the following:
ID | DisplayVal
-- ----------
1 1-H-3
2 2-H-3
3 3-J-4
In the above, the ID field is an IDENTITY INT field which is also used as and end user account Id. The DisplayVal is what they see on the screen.
A new client has provided their own Account Id values, but they are alpha-numeric, so they can't just go into the IDENTITY field. Here are my scenarios: I am looking for a scenario that would offer the best maintainability, end user experience, magnitude and impact of changes and testing/QA impact.
My first scenario was to add an Account Number column that would be a VARCHAR(x) and accommodate all types of Account Numbers. It would look like this:
ID | DisplayVal | AccountNumber
-- ---------- -------------
1 1-H-3 1
2 2-H-3 2
3 3-J-4 3
4 h389 h389
5 h-400-x h400
In the above, in the case of the first client, the seeded Identity which is the Account Id would be copied into the Account Number, but for the other client, there would still be a seeded Identity created, but their Account Number would be different and it may or may not match the Display Value.
My second scenario was to not add any columns and for clients that provide an Account Number, I would turn off IDENTITY INSERT and insert the new Id's and then turn identity insert back on. If a client did not provide an Account Number, I would auto-generate one, obviously trying to avoid collisions.
The third scenario was basically to leave the new Account Number as a legacy Account Number and create new identity values for all new records. This would require the end-user to become familiar with a new Account Number. It is probably the easiest, but not sure if there are any downsides.
If there is another scenario you know that would work well in this case, let me know.
You should not use business keys, like account id, as identity. Create a new id column and populate it either using an autoincrement field or a guid. Your users or other systems that interact with your system should not know/depend on this value.
Related
I'm doing invoice table in my database. I need a integer column to store invoice number. This column has to forbid gap between number. Identity doesn't work because on rollback can produce a gap.
So what I want is:
InvoiceId(Primary key identity) InvoiceNumber(Unique, NOT NUll)
1 1
2 2
10 3
13 4
Is there a special way in sql to do this ?
If there is no solution in sql, how should I do it in c# + entity?
EDIT 1:
additionnal information: A row will be never deleted.
EDIT 2:
Why I need gapLess Column: it's a law requirement (french law). Invoice number as to be gapLess, to show that you didn't removed invoice.
There is no way to protect from gaps if you access your database in parallel.
Simple case:
Process A) creates an invoice. (#1)
Process B) creates an invoice. (#2)
Process A) rolls back it's transaction.
Gap.
You could either lock your whole table for the whole transaction. That means that only one of your processes can create an invoice at the same time. That might be acceptable for small companies where one person creates all invoices by hand.
Or you could leave the column empty and once per night you lock the whole table and write those numbers that are not set yet. That means you get the invoice number later in your invoice process, but it's without gaps.
Or you could read the requirements again. Germany had something equally stupid, but it was only meant to have those numbers on the form for the tax department. So you could use your normal invoice numbers with gaps and when sending them to this bureaucratic monstrosity, you would generate a unique, gap free number on export only.
Because there are multiple users you can't recalculate value at client side. Just create triggers in your database for insert/delete that will recalculate the InvoiceNumber for the entire table.
I have an Enum called RoleEnum with four values User (1), Supervisor (2), Admin (3), and ITOperator (4). I have an Announcement table that obviously stores Announcement data (title, text, start and end date). However, I'd like to be able to add a new column called Roles that defines which users in which roles can see the announcement.
Normally when storing Enums in SQL I just use the smallint data type, however that isn't going to work in this situation because an announcement can be seen by more than one role.
What I was considering doing was having the Roles column defined as a varchar(x) and comma delimiting them when they were stored. However, I don't think/know if that is the best route to take. I don't want to create a new table to hold a one-to-many relationship.
Any ideas?
If you care about maintainability, I'd stick with third normal form as much as possible.
Roles
RoleID RoleName
1 User
2 Supervisor
3 Admin
4 ITOperator
Announcements
AnnouncementID Title ...
1 Foo ...
2 Bar ...
AnnouncementsVisibility
AnnouncementID RoleID
1 1
1 2
2 2
2 3
2 4
This is one possible solution - not guaranteed to be the best but it doesn't require a new table.
You can add the [Flags] attribute on your enum - this makes the enum a bit field where individual enum values can be masked together. Your enum would then look like this:
[Flags]
public enum RoleEnum : long
{
User = 1,
Supervisor = 2,
Admin = 4,
ITOperator = 8
}
You can use the '|' (bitwise OR) operator to mask multiple roles together in a single 64-bit integer, which you can store in the database in an integer field (a bigint).
RoleEnum userRoles = RoleEnum.User | RoleEnum.Admin;
If you don't need 64 possible roles, you can drop down to using an int instead - that gives you 32 possible distinct roles.
Just do as Entity Framework would do and store them as ints.
Check this out to learn more... MSDN
If i have a field in my db which clarify the type of the application .
takes two values 0 or 1
0 for web app and 1 for win app
and now there is some requirement in my business:
There are some win applications available to all users and some of
them belong to specific users .
What 's the best solution to handle this case .
adding new field to state whether it's public or private
or just adding new value to the same field say 2 to state it's private win app
If you haven't already it would probably be best to slip in a user, role, permission based security model to the database/system, thereby giving you the ability to specify a group of users that have access to a particular application, whether it be web or windows based
I'd say add a new column next to your AppId called PublicIndicatior
Oh and be sure to have a lookup table so people can see what 0 or 1 means, and foreign key it to your data table
Lookup Table:
AppTypeId, AppTypeDescription
0, WebApp
1, WinApp
Data Table:
Id, AppTypeId, PublicIndicator
1,0,1
etc
As Pope suggested above (I +1 him), the best scenario is to add in a new user table (or tables for roles etc if possible) and then link to that through either a new foreign key, or using the appid (assuming it is on your table and unique). Then when the boss comes back 3 weeks later and say, "that's great, but now can we restrict App99 to just the Accounts Dept" you are not going back to the drawing board.
In a client-server accounting application in invoice form when a user saves an invoce it gets An invoice number like 90134 from server and saves the invoice with that number The invoice number is needed for the customer.
So in Offline mode (like when the network dropped) how provide a unique id?
Is it good to use String Id like this pattern: client + incremental number?
I don't want to use GUIDs.
If you know in advance how many invoice numbers you will generate per client during an offline period, would you be able to pre-allocate invoice numbers? e.g. if each client is likely only to generate 4 invoices per offline period, you could allocate a block of 4 numbers to each client. This may involve an extra column in your DB to store a value indicating whether the number is an invoice already created, or a preallocation of a number. Depending on the structure and constraints within your DB, you may also need to store some dummy data to enforce referential integrity.
The downsides would be that your block of numbers may not get used sequentially, or indeed at all, so your invoice numbers would not be in chronological order. Also, you would run into problems if the pool of available numbers is used up.
You can use Guid:
var myUniqueID = Guid.NewID();
In SQL server is corresponding type uniqueidentifier.
In general the Guid is 128-bit number.
More about Guid you can read:
http://en.wikipedia.org/wiki/Globally_unique_identifier
http://msdn.microsoft.com/en-us/library/system.guid.aspx
I suppose the invoice number (integer) is incremental: in this case, since you have no way of knowing the last invoice number, you could save the invoice in a local db/cache/xml without the invoice Number and wait for the network connection to insert the new records in the DB (the invoice number would be generated then)
You could start your numbers for each client at a different range... e.g.:
client 1: 1,000,000
client 2: 2,000,000
client 3: 3,000,000
Update them every now and then when there is a connection to avoid overlaps.
It's not 100% bulletproof, but at least it's better than nothing.
My favorite would still be a GUID for this, since they're always unique.
There is a workaround, but it is merely a "dirty hack", you should seriously reconsider accepting new data entries while offline, especially when dealing with unique IDs to be inserted in many tables.
Say you have an "orders" table and another "orderDetails" table in your local dataset:
1- add a tmpID of type integer in your "orders" table to temporarily identify each unique order.
2- use the tmpID of your newly created order in the rest of the process (say for adding products to the current order in the orderDetails table)
--> once you are connected to the server, in a single transaction do the following
1- insert the first order in the "orders" table
2- get its uniqueID generated on your SQL server
3- search for each line in "orderDetails" that have a tmpID of currentOrder.tmpID and insert them in the "orderDetails" table on your server
4- commit the transaction and continue to the following row.
Keep in mind that this is very bad coding and that it can get real dirty and hard to maintain.
it looks like impossible to create unique numbers with two different systems both offline when it must be chronological and without missing numbers.
imho there is no way if the last number (on the server) was 10, to know if i should return 11 or 12; i would have to know if 11 was already used by another person.
I can only imagine to use a temporary number and later on renumber those numbers, but if the invoices are printed and the number can not be changed, i don't know how you could accomplish such a solution.
We have a large database with enquiries, each enquirys is referenced using a Guid. The Guid isn't very customer friendly so we want to the additional 5 digit "human id" (ok as we'll very likely won't have more than 99999 enquirys active at any time, and it's ok if a humanuid reference multiple enquirys as they aren't used for anything important).
1) Is there any way to have a IDENTITY column reset to 1 after 99999?
My current workaround to this is to use a INT IDENTITY(1,1) NOT NULL column and when presenting a HumanId take HumanId % 100000.
2) Is there any way to automatically "randomly distribute" the ids over [0..99999] so that two enquirys created after each other don't get the adjacent ids? I guess I'm looking for a two-way one-to-one hash function??
... Ideally I'd like to create this using T-SQL automatically creating these id's when a enquiry is created.
If performance and concurrency isn't too much of an issue, you can use triggers and the MAX() function to calculate a 'next human ID' value. You probably would want to keep your IDENTITY column as is, and have the 'human ID' in a separate column.
EDIT: On a side note, this sounds like a 'presentation layer' issue, which shouldn't be in your database. Your presentation layer of your application should have the code to worry about presenting a record in a human readable manner. Just a thought...
If you absolutely need to do this in the database, then why not derive your human-friendly value directly from the GUID column?
-- human_id doesn't have to be calculated when you retrieve the data
-- you could create a computed column on the table itself if you prefer
SELECT (CAST(your_guid_column AS BINARY(3)) % 100000) AS human_id
FROM your_table
This will give you a random-ish value between 0 and 99999, derived from the first 3 bytes of the GUID. If you want a larger, or smaller, range then adjust the divisor accordingly.
I would strongly recommend relooking at your logic. Your approach has a few dangers, including:
It is always a bad idea to re-use ID's, even if the original record has become "obsolete" - do you lose anything by continuing to grow ID's beyond 99999? The problem here is more likely to be with long term maintenance, especially if there is any danger of the system developing over time. Another thing to consider - is there any chance a user will take this reference number, and use it to reference your system at some stage in the future?
With manually assigning a generated / random ID, you will need to ensure that multiple records are not assigned the same ID. There are a few options that you have to follow this (for example, using transactions), however you should ensure that the scope of the transactions is not going to leave you open to problems with concurrent transactions being blocked - this may cause a few problems eg. Performance. You may be best served by generating your ID externally (as SQL does not do random especially well), and then enforcing a unique constraint on your DB, perhaps in the way suggested by Firoz Ansari.
If you still want to reset the identity column, this can be done with the DBCC CHECKIDENT command.
An example of generating random seeds in SQL server can be found here:
http://weblogs.sqlteam.com/jeffs/archive/2004/11/22/2927.aspx
You can create composite primary key with two columns, say..BatchId and HumanId.
Records in these columns will look like this:
BatchId, HumanId
1, 1
1, 2
1, 3
.
.
1, 99998
1, 99999
2, 1
2, 2
3, 3
use MAX or ORDER BY DESC to get next available HumanId with condition with BachId
SELECT TOP 1 #NextHumanId=HumanId
FROM [THAT_TABLE]
ORDER BY BatchId DESC, HumanID DESC
IF #NextHumanId>=99999 THEN SET #NextHumanId=1
Hope this help.
You could have a table of available HUMANIDs, each time you add an enquiry you could randomly pull a HUMANID from the table (and DELETE it), and each time you delete the enquiry you could add it back (by INSERTing).