I just started with stored procedures and I want to create a string primary key with a constant part and an auto-incremental part. For example "REP101, REP102" REP103 ...".
Other methods that I found were to have two columns,an auto incremental int column and a primary key column with REP as a prefix. But I think it will be more efficient if I used stored procedures.
I am looking for a way of retrieving the last row in a table and returning its key using stored procedure so I can use it in the C# code. Any other way that can achieve the same result will be appreciated.
It seems to me that if every primary key value is going to have 'REP' as the prefix, then you really don't need the prefix at all, and you can simply use an Identity column as your primary key. Then you don't need a stored procedure at all, and this will be much more efficient.
A stored procedure would be more efficient. You'd use a Sequence to create the numeric part and then return it concatenated with the prefix.
roughly:
CREATE PROCEDURE GetNextKey
#key VARCHAR(50) OUT
AS
BEGIN
SET #key = 'REP' + SELECT NEXT VALUE FOR keys_seq;
END;
However, why bother with the prefix in your primary key at all? Take a closer look at your design and htink it you really need to make the distinction. You can simply use a auto-increment ID as your primary key and add the prefix at point of display, if necessary.
Related
I want to add an object after the last row in table. But the object is randomly inserted anywhere in the table; sometimes at first, sometimes at middle; sometimes at last. I was sending an object from my frontend to web api.I generated the primary key by guid. It is GUID type. Before that it was string type. I am using code-first flow. Any help?
I generated the primary key by guid.
Records are always maintained in the backing data by the clustered index. If you want the inserts to generally append at the end of the clustered index, use an IDENTITY integer primary key instead. (For SQL Server that is. Other database engines can use different keywords to define an incrementing integer PK.)
Additionally, when querying the data there is never a guaranteed sort unless you explicitly provide one. Any time you query your data and want a specific ordering, use an ORDER BY clause. (.OrderBy() in LINQ application code.)
I would like to have a primary key column in a table that is formatted as FOO-BAR-[identity number], for example:
FOO-BAR-1
FOO-BAR-2
FOO-BAR-3
FOO-BAR-4
FOO-BAR-5
Can SQL Server do this? Or do I have to use C# to manage the sequence? If that's the case, how can I get the next [identity number] part using EntityFramwork?
Thanks
EDIT:
I needed to do this is because this column represents a unique identifier of a notice send out to customers.
FOO will be a constant string
BAR will be different depending on the type of the notice (either Detection, Warning or Enforcement)
So is it better to have just an int identity column and append the values in Business Logic Layer in C#?
If you want this 'composited' field in your reports, I propose you to:
Use INT IDENTITY field as PK in table
Create view for this table. In this view you can additionally generate the field that you want using your strings and types.
Use this view in your repoorts.
But I still think, that there is BIG problem with DB design. I hope you'll try to redesign using normalization.
You can set anything as the PK in a table. But in this instance I would set IDENTITY to just an auto-incrementing int and manually be appending FOO-BAR- to it in the SQL, BLL, or UI depending on why it's being used. If there is a business reason for FOO and BAR then you should also set these as values in your DB row. You can then create a key in the DB between the two three columns depending on why your actually using the values.
But IMO I really don't think there is ever a real reason to concatenate an ID in such a fashion and store it as such in the DB. But then again I really only use an int as my ID's.
Another option would be to use what an old team I used to be on called a codes and value table. We didn't use it for precisely this (we used it in lieu of auto-incrementing identities to prevent environment mismatches for some key tables), but what you could do is this:
Create a table that has a row for each of your categories. Two (or more) columns in the row - minimum of category name and next number.
When you insert a record in the other table, you'll run a stored proc to get the next available identity number for that category, increment the number in the codes and values table by 1, and concatenate the category and number together for your insert.
However, if you're main table is a high-volume table with lots of inserts, it's possible you could wind up with stuff out of sequence.
In any event, even if it's not high volume, I think you'd be better off to reexamine why you want to do this, and see if there's another, better way to do it (such as having the business layer or UI do it, as others have suggested).
It is quite possible by using computed column like this:
CREATE TABLE #test (
id INT IDENTITY UNIQUE CLUSTERED,
pk AS CONCAT('FOO-BAR-', id) PERSISTED PRIMARY KEY NONCLUSTERED,
name NVARCHAR(20)
)
INSERT INTO #test (name) VALUES (N'one'), (N'two'), (N'three')
SELECT id, pk, name FROM #test
DROP TABLE #test
Note that pk is set to NONCLUSTERED on purpose because it is of VARCHAR type, while the IDENTITY field, which will be unique anyway, is set to UNIQUE CLUSTERED.
I have an ASP.NET application that generates GUIDs in the code-behind via C#. These GUIDs are generated via the following:
Guid id = Guid.NewGuid();
This GUID is later stored in a SQL Server 2008 database. I also have a stored procedure that will update that record. I would like to generate a GUID in the stored procedure that is in the same format as the one generated in ASP.NET.
Can somebody please tell me how to do this?
Thank you!
Use NEWID() method
DECLARE #ID uniqueidentifier
SET #ID = NEWID()
If this is for a clustered index (most often a primary key), I highly recommend NEWSEQUENTIALID() (SQL Server 2005 on up) since, NEWID() will create a fragmented index in that case, being truly random.
This will generate a GUID for you: SELECT NEWID()
Examples may be found here: http://msdn.microsoft.com/en-us/library/ms190348.aspx
My guess from the way that you have worded your question is that you are storing the GUIDs in a text (e.g. VARCHAR) field in the database - if this is the case then you should instead be using the uniqueidentifier type in which case you can use the NEWID() SQL function to generate a new GUID.
See C# guid and SQL uniqueidentifier for more detail on how to store GUIDs in an SQL Server database.
You could use NEWID().
But, there are issues with indexing guids generated like this. Instead, you should use the comb algorithm:
CAST(CAST(NEWID() AS BINARY(10)) +
CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER)
Make sure you are storing these in a column of type UNIQUEIDENTIFIER and not converting them to NVARCHAR or anything of the sort.
You can use the NEWSEQUENTIALID for better indexing support, but the downside is that you can use this function only as a default value expression for your column.
You can use something like:
INSERT INTO MyTABLE (...) OUTPUT inserted.GUIDCOLUMN INTO #tableVar VALUES (...)
to access the newly generated sequential id.
The application I have completed has gone live and we are facing some very specific problems as far as response time is concerned in specific tables.
In short, response time in some of the tables that have 5k rows is very low. And these tables will grow in size.
Some of these tables (e.g. Order Header table) have a uniqueidentifier as the P.K. We figure that this may be the reason for the low response time.
On studying the situation we have decided the following options
Convert the index of the primary key in the table OrderHeader to a non-clustered one.
Use newsequentialid() as the default value for the PK instead of newid()
Convert the PK to a bigint
We feel that option number 2 is ideal since option number 3 will require big ticket changes.
But to implement that we need to move some of our processing in the insert stored procedures to triggers. This is because we need to trap the PK from the OrderHeader table and there is no way we can use
Select #OrderID = newsequentialid() within the insert stored procedure.
Whereas if we move the processing to a trigger we can use
select OrderID from inserted
Now for the questions?
Will converting the PK from newid() to newsequentialid() result in performance gain?
Will converting the index of the PK to a non-clustered one and retaining both uniqueidentifier as the data type for PK and newid() for generating the PK solve our problems?
If you faced a similar sort of situation please do let provide helpful advice
Thanks a tons in advance people
Romi
Convert the index of the primary key in the table OrderHeader to a non-clustered one.
Seems like a good option to do regardless of what you do. If your table is clustered using your pkey and the latter is a UUID, it means you're constantly writing somewhere in the middle of the table instead of appending new rows to the end of it. That alone will result in a performance hit.
Prefer to cluster your table using an index that's actually useful for sorting; ideally something on a date field, less ideally (but still very useful) a title/name, etc.
Move the clustered index off the GUID column and onto some other combination of columns (your most often run range search, for instance)
Please post your table structure and index definitions, and problem query(s)
Before you make any changes: you need to measure and determine where your actual bottleneck is.
One of the common reasons for a GUID Primary Key, is generating these ID's in a client layer, but you do not mention this.
Also, are your statistics up to date? Do you rebuild indexes regularly?
We currently define a list of constants (mostly these correspond to enumerations we have defined in the business layer) at the top of a stored procedure like so:
DECLARE #COLOR_RED INT = 1
DECLARE #COLOR_GREEN INT = 2
DECLARE #COLOR_BLUE INT = 3
But these often get repeated for many stored procedures so there is a lot of duplication.
Another technique I use if the procedure needs just one or two constants is to pass them in as parameters to the stored procedure. (using the same convention of upper case for constant values). This way I'm sure the values in the business layer and data layer are consistent. This method is not nice for lots of values.
What are my other options?
I'm using SQL Server 2008, and C# if it makes any difference.
Update Because I'm using .Net is there any way that user defined (CLR) types can help?
This might be controversial: my take is don't use enumerations in T-SQL. T-SQL isn't really designed in a way that makes enums useful, the way they are in other languages. To me, in T_SQL, they just add effort and complexity without the benefit seen elsewhere.
I can suggest two different approaches:
1) Define an Enumeration table with a tinyint identity column as the primary key and the enum value as a unique index; e.g.
CREATE TABLE [dbo].[Market](
[MarketId] [smallint] IDENTITY(1,1) NOT NULL,
[MarketName] [varchar](32) COLLATE Latin1_General_CS_AS NOT NULL,
CONSTRAINT [PK_Market] PRIMARY KEY CLUSTERED
(
[MarketId] ASC
) ON [PRIMARY]
) ON [PRIMARY]
Then either:
Have your application load the enumeration to primary key value mapping on start-up (assuming this will remain constant).
Define a function to translate enumeration values to primary key values. This function can then be used by stored procs inserting data into other tables in order to determine the foreign key to the enumeration table.
2) As per (1) but define each primary key value to be a power of 2. This allows another table to reference multiple enumeration values directly without the need for an additional association table. For example, suppose you define a Colour enumeration table with values: {1, 'Red'}, {2, 'Blue'}, {4, 'Green'}. Another table could reference Red and Green values by including the foreign key 5 (i.e. the bit-wise OR of 1 and 4).
Scalar user define function? Not perfect, but functional...
CREATE FUNCTION dbo.ufnRGB (
#Colour varchar(20)
)
RETURNS int
AS
BEGIN
DECLARE #key int
IF #Colour = 'BLue'
SET #key = 1
ELSE IF #Colour = 'Red'
SET #key = 2
ELSE IF #Colour = 'Green'
SET #key = 3
RETURN #KEy
END
I don't like the idea of defining what are effectively constants for stored procedures in multiple places - this seems like a maintenance nightmare and is easily susceptible to errors (typos etc). In fact, I can't really see many circumstances when you would need to do such a thing?
I would definitely keep all enumeration definitions in one place - in your C# classes. If that means having to pass them in to your procedures every time, so be it. At least that way they are only ever defined in one place.
To make this easier you could write some helper methods for calling your procedures that automatically pass the enum parameters in for you. So you call a helper method with just the procedure name and the "variable" parameters and then the helper method adds the rest of the enumeration parameters for you.
How about using a scalar function as a constant. A naming convention would make their usage close to enumerations:
CREATE FUNCTION COLOR_RED()
RETURNS INT
AS
BEGIN
RETURN 1
END
CREATE FUNCTION COLOR_GREEN()
RETURNS INT
AS
BEGIN
RETURN 2
END
...