Multiple identity columns or a way to auto increment - c#

I have a table (tOrder) that has the following structure in SQL Server 2008
orderID (int) - this is currently the primary key and the identity field.
name(varchar)
address(varchar)
groupID (int) - now this field i need to also auto increment, but at the same time i want to be able to insert values into.
My data would look something like:
1 - john - address1 - 1
2 - mary - address2 - 1
3 - mary -address3 - 2
4 - jane - address4 - 3
where order IDs 1 and 2 share the same group , while 3 and 4 are in their own.
Many orders can have same groupID, but when I insert an order of a new group, I would like the groupID to be auto populated with the next sequence number automatically, while at the same time allowing me to insert duplicate groupID for different orders if I need to.
Hope this makes sense.
How do I go about doing this? (I'm using c# in the back end, if that makes any difference)

I would create a new "groups" table with an identity to ensure uniqueness as follows:
create table tOrders(
orderID int PRIMARY KEY IDENTITY,
name varchar(30),
address varchar(60),
fkGroup int
);
create table tGroups(
groupID int PRIMARY KEY IDENTITY,
description varchar(50)
);
ALTER TABLE tOrders
ADD FOREIGN KEY (fkGroup) REFERENCES tGroups(groupID);
You would, of course have to either supply a groupID for the IDENTITY of a newly inserted tGroup (groupID) value.
This SQL Fiddle Example demonstrates one way of populating the tables.

One option would be to create a trigger on your torder table (not a fan of triggers, but given your criteria, can't think of another option).
CREATE TRIGGER tOrder_trigger
ON tOrder
AFTER INSERT
AS
UPDATE tOrder
SET groupid = (SELECT COALESCE(MAX(groupid),0) + 1 FROM tOrder)
FROM INSERTED AS I
WHERE I.groupid IS NULL
AND tOrder.orderid = I.orderid;
SQL Fiddle Demo
This checks if the inserted record has a NULL groupid using INSERTED, and if so, updates the table to the MAX(groupid) + 1, using COALESCE to check for NULL.

Related

"Internal id" generation in Postgres (id that is only unique together with another column)

I have a database structure with schools and courses.
The navigation in the web UI is done with ids, so something like: /school/1/course/5.
Right now the id for course is the globally unique id for course.
So course 1 and 3 might belong to school 1, but course 2 belongs to school 2:
/school/1/course/1
/school/1/course/3
/school/5/course/2
This means that even though school 1 has two courses, the following URL is incorrect, since the course with global id 2 belongs to school 5:
/school/1/course/2
I would like to start using some internal ids, where instead of looking up courses by globally unique id, I look them up by both school id and internal id for the course.
So the course table would look like this for the example:
| id | school_id | internal_id |
| 1 1 1 |
| 2 5 1 |
| 3 1 2 |
When creating my internal ids, what is the best way to increment the id? Is there some way I can do this in the database, or must I find out how many courses there are for a school and use count + 1 for the next internal id?
I'm using a Postgres database and C#.
What you describe is a classic Many:Many association between School:Course; a school offers many courses and a course is offered by many schools. The classic solution is to introduce a resolution table which contains the key of both, and if needed attributes for the combination. So something similar too:
create table courses ( course_id integer generated always as identity primary key
, name text not null unique
, description text
-- other attributes related to a course and nothing else
) ;
create table schools ( school_id integer generated always as identity primary key
, name text not null unique
, location text
, latitude double precision -- perhaps
, langitued double precision -- perhaps
-- other attributes related to a school and nothing else
) ;
create table class_catalog( school_id integer not null references schools(school_id)
, course_id integer not null references courses(course_id)
, start_date date -- perhaps
, end_date date -- perhaps
, semester integer -- perhaps
-- other attributes related to a combination of school and course
, constraint class_catalog_pk
primary key (school_id, course_id)
) ;
This eliminates the need for your internal_id and your globally unique id as you need only the class)id and the course_id. It does not however preclude a the catalog from having its own id. You might want it if you plan to link other entities to it, say student_classes. Just make sure the combination of school_id and course_id id unique (or perhaps within year and semester. I'll leave that definition to you).
-- alternate with the insection (association tale) having its own PK
create table class_catalog_alt( internal_id integer generated always as identity primary key
, school_id integer not null references schools(school_id)
, course_id integer not null references courses(course_id)
, start_date date -- perhaps
, end_date date -- perhaps
, semester integer -- perhaps
-- other attributes related to the combination of school and course
, constraint class_catalog_bk
unique (school_id, course_id)
) ;

Migrate from NHibernate ids (hilo) to SQL auto ids in SQL Server

Is there a way to migrate existing database with all tables and relations to use SQL Server auto ids instead of Nhibernate (hilo) ids?
We have a .NET application which uses NHibernate. But the problem is, we are running out of int.
I know that this requires tables recreation with new ones which have ids set as auto incremented. Is there a easy way to migrate. For example some sort of query which will replicate tables, keep relations, but now with SQL Server ids instead of hilo ids. Biggest problem of hilo, it's using shared ids, which makes situation worse.
For example, we have a database of 3 tables:
dbo.Users
dbo.RegistrationResults
dbo.UserNotes
Tables:
dbo.Users
Id int (Primary)
Email nvarchar(255)
RegistrationResultFk int (Foreign Key)
dbo.RegistrationResults
Id int (Primary)
ValidationOutcome nvarchar(255)
dbo.UserNotes
Id int (Primary)
Message nvarchar(255)
RegistrationResultFk int (Foreign Key)
And data populated like this:
dbo.Users
Id
Email
RegistrationResultFk
1
test#gmail.com
2
4
test2#gmail.com
5
dbo.RegistrationResults
Id
ValidationOutcome
2
Awaiting confirmation
5
Confirmed
dbo.UserNotes
Id
Message
RegistrationResultFk
3
it's a test
2
6
it's a test 2
5
We want data after migration to look like:
dbo.Users
Id
Email
RegistrationResultFk
1
test#gmail.com
1
2
test2#gmail.com
2
dbo.RegistrationResults
Id
ValidationOutcome
1
Awaiting confirmation
2
Confirmed
dbo.UserNotes
Id
Message
RegistrationResultFk
1
it's a test
1
2
it's a test 2
2
I suggest you, to minimize impact, use Sequences that are equivalent of autoincrement fields but are stored outside the table.
Below simple example for a table
Create the sequence
This code creates a sequence.
With SSMS you can also navigate to Database -> Programmability -> Sequence ==>
right-click New Sequence.
Full Syntax.
CREATE SEQUENCE dbo.UserId
AS INT
START WITH 1234
INCREMENT BY 1
;
Read the syntax carefully to better set up your sequences, for example, you may want to set up the CACHE to increases performance minimizing the IOs required to generate sequence numbers
Get ID
For getting the ID you must issue a Raw Query with NHibernate.
NHibarnate Reference
NEXT VALUE
public int GetNextUserId(Session session)
{
var query = session.CreateSQLQuery("SELECT NEXT VALUE FOR dbo.UserId");
var result = query.UniqueResult();
return Convert.ToInt32(result);
}

How to Auto-increment non-integer primary key in sql-server? [duplicate]

Can I make a primary key like 'c0001, c0002' and for supplier 's0001, s0002' in one table?
The idea in database design, is to keep each data element separate. And each element has its own datatype, constraints and rules. That c0002 is not one field, but two. Same with XXXnnn or whatever. It is incorrect , and it will severely limit your ability to use the data, and use database features and facilities.
Break it up into two discrete data items:
column_1 CHAR(1)
column_2 INTEGER
Then set AUTOINCREMENT on column_2
And yes, your Primary Key can be (column_1, column_2), so you have not lost whatever meaning c0002 has for you.
Never place suppliers and customers (whatever "c" and "s" means) in the same table. If you do that, you will not have a database table, you will have a flat file. And various problems and limitations consequent to that.
That means, Normalise the data. You will end up with:
one table for Person or Organisation containing the common data (Name, Address...)
one table for Customer containing customer-specific data (CreditLimit...)
one table for Supplier containing supplier-specific data (PaymentTerms...)
no ambiguous or optional columns, therefore no Nulls
no limitations on use or SQL functions
.
And when you need to add columns, you do it only where it is required, without affecting all the other sues of the flat file. The scope of effect is limited to the scope of change.
My approach would be:
create an ID INT IDENTITY column and use that as your primary key (it's unique, narrow, static - perfect)
if you really need an ID with a letter or something, create a computed column based on that ID INT IDENTITY
Try something like this:
CREATE TABLE dbo.Demo(ID INT IDENTITY PRIMARY KEY,
IDwithChar AS 'C' + RIGHT('000000' + CAST(ID AS VARCHAR(10)), 6) PERSISTED
)
This table would contain ID values from 1, 2, 3, 4........ and the IDwithChar would be something like C000001, C000002, ....., C000042 and so forth.
With this, you have the best of both worlds:
a proper, perfectly suited primary key (and clustering key) on your table, ideally suited to be referenced from other tables
your character-based ID, properly defined, computed, always up to date.....
Yes, Actually these are two different questions,
1. Can we use varchar column as an auto increment column with unique values like roll numbers in a class
ANS: Yes, You can get it right by using below piece of code without specifying the value of ID and P_ID,
CREATE TABLE dbo.TestDemo
(ID INT IDENTITY(786,1) NOT NULL PRIMARY KEY CLUSTERED,
P_ID AS 'LFQ' + RIGHT('00000' + CAST(ID AS VARCHAR(5)), 5) PERSISTED,
Name varchar(50),
PhoneNumber varchar(50)
)
Two different increments in the same column,
ANS: No, you can't use this in one table.
I prefer artificial primary keys. Your requirements can also be implemented as unique index on a computed column:
CREATE TABLE [dbo].[AutoInc](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Range] [varchar](50) NOT NULL,
[Descriptor] AS ([range]+CONVERT([varchar],[id],(0))) PERSISTED,
CONSTRAINT [PK_AutoInc] PRIMARY KEY ([ID] ASC)
)
GO
CREATE UNIQUE INDEX [UK_AutoInc] ON [dbo].[AutoInc]
(
[Descriptor] ASC
)
GO
Assigning domain meaning to the primary key is a practice that goes way, way back to the time when Cobol programmers and dinosaurs walked the earth together. The practice survives to this day most often in legacy inventory systems. It is mainly a way of eliminating one or more columns of data and embedding the data from the eliminated column(s) in the PK value.
If you want to store customer and supplier in the same table, just do it, and use an autoincrementing integer PK and add a column called ContactType or something similar, which can contain the values 'S' and 'C' or whatever. You do not need a composite primary key.
You can always concatenate these columns (PK and ContactType) on reports, e.g. C12345, S20000, (casting the integer to string) if you want to eliminate the column in order to save space (i.e. on the printed or displayed page), and everyone in your organization understands the convention that the first character of the entity id stands for the ContactType code.
This approach will leverage autoincrementing capabilities that are built into the database engine, simplify your PK and related code in the data layer, and make your program and database more robust.
First let us state that you can't do directly. If you try
create table dbo.t1 (
id varchar(10) identity,
);
the error message tells you which data types are supported directly.
Msg 2749, Level 16, State 2, Line 1
Die 'id'-Identitätsspalte muss vom
Datentyp 'int', 'bigint', 'smallint',
'tinyint' oder 'decimal' bzw.
'numeric' mit 0 Dezimalstellen sein
und darf keine NULL-Werte zulassen.
BTW: I tried to find this information in BOL or on MSDN and failed.
Now knowing that you can't do it the direct way, it is a good choice to follow #marc_s proposal using computed columns.
Instead of doing 'c0001, c0002' for customers and 's0001, s0002' for suppliers in one table, proceed in the following way:
Create one Auto-Increment field "id" of Data Type "int (10) unsigned".
Create another field "type" of Data Type "enum ('c', 's')" (where c=Customer, s=Supplier).
As "#PerformanceDBA" pointed out, you can then make the Primary Key Index for two fields "id" & "type", so that your requirement gets fulfilled with the correct methodology.
INSERT INTO Yourtable (yourvarcharID)
values('yourvarcharPrefix'+(
SELECT CAST((SELECT CAST((
SELECT Substring((
SELECT MAX(yourvarcharID) FROM [Yourtable ]),3,6)) AS int)+1)
AS VARCHAR(20))))
Here varchar column is prefixed with 'RX' then followed by 001, So I selected substring after that prefix of it and incremented the that number alone.
We can add Default Constraint Function with table definition to achieve this.
First create table -
create table temp_so (prikey varchar(100) primary key, name varchar(100))
go
Second create new User Defined Function -
create function dbo.fn_AutoIncrementPriKey_so ()
returns varchar(100)
as
begin
declare #prikey varchar(100)
set #prikey = (select top (1) left(prikey,2) + cast(cast(stuff(prikey,1,2,'') as int)+1 as varchar(100)) from temp_so order by prikey desc)
return isnull(#prikey, 'SB3000')
end
go
Third alter table definition to add default constraint -
alter table temp_so
add constraint df_temp_prikey
default dbo.[fn_AutoIncrementPriKey_so]() for prikey
go
Fourth insert new row into table without specifying value for primary column-
insert into temp_so (name) values ('Rohit')
go 4
Check out data in table now -
select * from temp_so
OUTPUT -
prikey name
SB3000 Rohit
SB3001 Rohit
SB3002 Rohit
SB3003 Rohit
you may try below code:
SET #variable1 = SUBSTR((SELECT id FROM user WHERE id = (SELECT MAX(id) FROM user)), 5, 7)+1;
SET #variable2 = CONCAT("LHPL", #variable1);
INSERT INTO `user`(`id`, `name`) VALUES (#variable2,"Jeet");
1st line to get last inserted Id by removing four character than increase one value and set to a variable1
2nd line to make complete id with four character prefix and assign to variable2
insert new value with generated new primary key = variable2
you should have minimum one data in this table to work above SQL
No. If you really need this, you will have to generate ID manually.

View and Entity Framework data not right?

I have a view from some table when I select from the view in SQL Server Management Studio it works fine, but when I use Entity Framework to get the data from view it's different.
ReturnDbForTesEntities1 db = new ReturnDbForTesEntities1();
List<VJOBS2> list = new List<VJOBS2>();
list = db.VJOBS2.ToList();
Same number of records but last 2 rows are different.
I have table for job applicant applicant can apply for 2 jobs or more
ApplicantId ApplicantName JobId JobName
1 Mohamed 1 Developer
1 Mohamed 2 IT Supporter
but in list
ApplicantId ApplicantName JobId JobName
1 Mohamed 1 Developer
1 Mohamed 1 Developer
There is a subtle problem with views when used from Entity Framework.
If you have a table, do use it with EF, you need to have a primary key to uniquely identify each row. Typically, that's a single column, e.g. an ID or something like that.
With a view, you don't have the concept of a "primary key" - the view just contains some columns from some tables.
So when EF maps a view, it cannot find a primary key - and therefore, it will use all non-nullable columns from the view as "substitute" primary key.
I don't know what these are in your case - you should be able to tell from the .edmx model.
Let's assume that (ApplicantId, ApplicantName) are the two non-nullable columns that EF now uses as a "substitute" primary key. When EF goes to read the data, it will read the first line (1, Mohamed, 1, Developer) and create an object for that.
When EF reads the second line (1, Mohamed, 2, IT-Supporter), it notices that the "primary key" (1, Mohamed) is the same as before - so it doesn't bother creating a new object with those values read, but the primary key is the same, it hence must be the same object as it has already read before, so it uses that object instead.
So the problem really is that you can't have explicit primary keys on a view.
Either you can tweak your EF model to make it clear to EF that e.g. (ApplicantId, JobId) is really the primary key (you need to make sure those columns are both non-nullable) - or you need to add something like a "artificial" primary key to your view:
CREATE VIEW dbo.VJOBS2
AS
SELECT
ApplicantId, ApplicantName, JobId, JobName,
RowNum = ROW_NUMBER() OVER(ORDER BY JobId)
FROM
dbo.YourBaseTable
By adding this RowNum column to your view, which just numbers the rows 1, 2, ...., n, you get a new, non-nullable column which EF will include into the "substitute PK" and since those numbers are sequential, no two rows will have the same "PK" values and therefore none will erroneously be replaced by something that's been read from the database already.
FYI, I had to add ISNULL to get it to work for me, see the modification in the first line of this code example:
SELECT ISNULL(ROW_NUMBER() OVER(ORDER BY a.OrderItemID),0) as ident, a.*
FROM
(
SELECT e.AssignedMachineID, e.StartDate, e.OrderItemID, e2.OrderItemID AS doubleBookedEventID, e.StartTime, e.EndTime, e2.StartTime AS doubleBookedStartDateTime, e2.EndTime AS doubleBookedEndDateTime, DATEDIFF(MINUTE,e2.StartTime,e.EndTime) AS doubleBookedMinutes
FROM schedule e
INNER JOIN schedule e2
ON e.AssignedMachineID = e2.AssignedMachineID
and e.StartDate=e2.StartDate
AND e.schedID <> e2.schedID
AND e2.StartTime BETWEEN DATEADD(minute,1,e.StartTime) AND DateAdd(minute,-1,e.EndTime) where Coalesce(e.ManuallyOverrided,0)=0 and Coalesce(e.AssignedMachineID,0) > 0
) a

How to set Identity Specification YES in 2 columns in a table in SQL Server 2008

How to set Identity Specification YES in 2 columns in a table in SQL Server 2008 ?
Example:
id int,
catid int,
for "#id" i am using primary key and i set Is Identity YES. allow not null.
for "#catid" i want to set Identity YES also,
but when i set Identity YES for "#catid" column, "#id" column it becomes Identity No itself.
i want #catid also generate id itself like #id.
check picture attached plz. and suggest me
It seems you are having some table design issues. Why would you like to have two Identity columns? As stated in the comments, the values will always be either the same or be computable from the original identity specification:
create table MyTable (
Id int identity(1,1) not null primary key,
CatId as Id, -- computed column
MyField nvarchar(100) null
)
Yields:
Id CatId MyField
1 1 A
2 2 B
3 3 C
4 4 D
5 5 E
Now if we would like to have some different value for CatId:
create table MyTable (
Id int identity(1,1) not null primary key,
CatId as Id * 3, -- some different value
MyField nvarchar(100) null
)
We would get:
Id CatId MyField
1 3 A
2 6 B
3 9 C
4 12 D
5 15 E
Either way it never makes sense to have multiple identity columns.
It is not possible for two columns to set identity in the same table but i have a way around
set your id to primary and identity and write a trigger on after insert operation on this table and then update the catid column with id values
you will get id in two columns with one is primary key and other is normal column
It is not possible to have more than one identity column. Instead make one column as primary key without auto increment.
Only one identity column allowed per table.
Make use of primary key.

Categories