What are the different ways of handling 'Enumerations' in SQL server? - c#

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
...

Related

Getting the Average in ROWS c#

i have a sql server database with table. These are
1stAP_TB, 2ndAP_TB, 3rdAP_TB, 4thAP_TB, 1steng_TB, 2ndeng_TB, 3rdeng_TB,
4theng_TB
all in them are in row. The numbers will be solve individually on specific column. Now, i need to know how am i going to get the average of 1stAP_TB, 2ndAP_TB, 3rdAP_TB and 4thAP_TB while there are in rows.
Also, there are multiple data that will be save inside the database. I am using C# programming language.
Try below method
create table aveexample
(a1stAP_TB int,
a2ndAP_TB int,
a3rdAP_TB int,
a4thAP_TB int,
a1steng_TB int,
a2ndeng_TB int,
a3rdeng_TB int,
a4theng_TB int
)
Sample data
insert into aveexample values(1,2,3,4,5,6,7,8)
insert into aveexample values(11,22,33,44,55,66,77,78)
insert into aveexample values(2,3,1,4,10,10,45,5)
Method 1
select *, (select AVG(totaldata)
from (values(a1stAP_TB),
(a2ndAP_TB),(a3rdAP_TB),(a4thAP_TB),(a1steng_TB),
(a2ndeng_TB),(a3rdeng_TB),(a4theng_TB)) total(totaldata))as average
from aveexample
Method 2
select ((a1stAP_TB)+
(a2ndAP_TB)+(a3rdAP_TB)+(a4thAP_TB)+(a1steng_TB)+
(a2ndeng_TB)+(a3rdeng_TB)+(a4theng_TB))/8 as Average
from aveexample
It is difficult to give concrete advice given the very limited description in the question, but from the description and comments so far, it seems to me like the database needs to be redesigned to better fit your requirements. First, you have no ID field, so there is no way to differentiate one row from the next. Then, what you are left with is a series of repeated values. The clue here is that you have "1st", "2nd", "3rd" in the column names. That's probably a sign that those columns need to be moved into rows of a related table. It may not instantly seem to be the best approach, but this is called "First Normal Form" and is a typical best practice with SQL databases. See also Database Normalization Basics.
It seems to me that what you have here is some entity (which you haven't mentioned in your question) that has a number of values associated with it. The 'entity' here should be given a unique ID and then all of the values for that entity stored with its ID.
You might have a table with the following columns:
CREATE TABLE MyItems (
ID int NOT NULL,
Sequence int NOT NULL,
Value int NOT NULL,
CONSTRAINT PK_MyValues_ID_Sequence PRIMARY KEY
(ID,Sequence)
)
Note: ID + sequence forms the unique primary key for the table and makes every row unique. This also lets you keep track of what order the items were added in. This may or may not be important to you but every table should probably have a unique primary key.
Your data table would then look something like this (the example represents two different entities, the first having 4 values and the second having 3 values):
It's difficult to show a sensible example without knowing more about the application and what it does... but with this table design you have a basis from which to add values one at a time, as you said you needed, and a way to query them back. You can use grouping to produce things like totals and averages, or you can do that in code by iterating over the results of a query or in a LINQ statement.
You can then compute the average for an entity of a given ID using a LINQ query along the lines of:
var average = MyItems.Where(p=>p.ID == 1).Average(q=>q.Value);
As an example of the flexibility of this sort of approach, you could just as easily compute the average of every second value entered across the entire database:
var averageOfSecondItems = MyItems.Where(p => p.Sequence == 2).Average(q => q.Value);
The example I've shown deals with one type of value. In your question it appears that you might have two different types of value. There are several ways you could handle that - for example you could add another column to the table if the values are always entered in pairs, or you could create a second table to hold the separate values. Again, it's hard to make a recommendation based on the limited information given.
If putting your data into First Normal Form seems like a lot of work, then your application might be a better fit for a document database ("NoSQL" database), but that is really a different question. In the question, a SQL database was specified so I've concentrated on that.

Stored procedure to generate auto incremented string primary key

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.

How can I load a .Net DataTAble schema from a UDT Table declared in our DB?

I've searched every way I can come up with, but can't find an technique for initializing a DataTable to match a UDT Table declared in our DB. I could manually go through and add columns, but I don't want to duplicate the structure in both places. For a normal table, one option would be to simply issue a "select * where ..." that returns no results. But can something like this be done for a UDT Table?
And here is the background problem.
This DB has a sproc that accepts a Table Valued Parameter that is an instance of the indicated UDT Table declared in the same DB. Most of the UD fields are nullable, and the logic to load the TVP is quite involved. What I hoped to do is initialize the DT, then insert rows as needed and set required column/field values as I go until I'm ready to toss the result to SS for final processing.
I can certainly add the dozen or more fields in code, but the details are still in flux (and may continue to be so for some time), which is one reason I don't really want to have to load all the columns in code.
So, is there a reasonable solution, or am I barking up the wrong tree? I've already spent more time looking for the solution I expected to exist than it would have taken to write the column loading code 100 times over, but now I just want to know if it's possible.
Ok, I was discussing with a friend who is MUCH more SQL savvy than I am (doesn't take much), and he suggested the following SQL query:
"DECLARE #TVP as MyUDTTable; SELECT * FROM #TVP"
This appears to give me exactly what I want, so I'm updating here should some other poor sap want something similar in the future. Perhaps others may offer different or better answers.
Here is an example of how I did this. This style of input/output is something me and a co-worker put together to allow quick and effective use of entity framework on his side and keeps my options open to use all sql toys. If that is the same use as you have you might also like the OUTPUT use I did here. It spits the newly created ids right back at whatever method calls the proc allowing the program to go right on to the next activity withouth pestering my database for the numbers.
My Udt
CREATE TYPE [dbo].[udtOrderLineBatch] AS TABLE
(
[BrandId] [bigint] NULL,
[ProductClassId] [bigint] NULL,
[ProductStatus] [bigint] NULL,
[Quantity] [bigint] NULL
)
and the procedure that takes is as an input
create procedure [ops].[uspBackOrderlineMultipleCreate]
#parmBackOrderId int
,#UserGuid uniqueidentifier
null
,#parmOrderLineBatch as udtOrderLineBatch readonly
as
begin
insert ops.OrderLine
(
BrandId
,ProductClassId
,ProductStatusId
,BackOrderId
,OrderId
,DeliveryId
,CreatedDate
,CreatedBy)
output cast(inserted.OrderLineId as bigint) OrderLineId
select line.BrandId
,line.ProductClassId
,line.ProductStatus
,#parmBackOrderId
,null
,null
,getdate()
,#UserGuid
from #parmOrderLineBatch line
join NumberSequence seq on line.Quantity >= seq.Number
end

How to solve bad database design with Entity Framework?

I apologize for the strange question; it is hard to put into words. I am forced to work with a database of questionable design and I would like to solve data access issues with the Entity Framework. I am at a loss how to treat this type of design in an object oriented way.
The Item table is the problem. It has fields that may contain different types of data, ranging from Size to Lot Numbers to SO numbers, etc. The name of the field is determined by the ItemDef table, which links to a ItemDefValue table with the actual field names. The tables have been simplified for demonstration purposes.
Create Table Item
(
ItemKey int Primary Key not null,
ItemID1 varchar(100) null,
ItemID2 varchar(100) null,
ItemID3 varchar(100) null,
ItemID4 varchar(100) null,
ItemDefKey int not null --foreign key to ItemDef table
);
Create Table ItemDef
(
ItemDefKey int Primary Key not null,
CustomerKey int not null , -- foreign key to cusotmer table
);
Create Table ItemDefValue
(
FieldCode small not null,
Title varchar(50) not null
ItemDefKey int not null - foreign key to ItemDef table
);
I have solved this problem with DataSets and DataTables by renaming columns based on the ItemDefValue, so I am not looking for a table-based solution. I would like to avoid this type of table-based logic, especially since I am not fond of DataSets and would rather accomplish data access using the Entity Framework.
I would appreciate advise from anyone that has dealt with this kind of problem before. I would specifically like any suggestions on how to treat this kind of database design in an object oriented way, preferably using the Entity Framework.
And if you think there is no other solution than to re-design the database than I will take that advise as well.
Thanks.
Messy! A restructure would definitely be best.
But, how about creating views that represent the way you'd like the tables to be organised at an object level - and then with EF use those views rather than the tables directly. You'd need to function map the insert/update/delete to stored procedures for dealing with the real tables, but at least from EF side of things you'd be dealing with a decently organised set of entities rather than those tables ...

SQL Server - formatted identity column

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.

Categories

Resources