Feedback on database table structure for queue containing arbitrary tasks - sql-server

I want to introduce a queue functionality in an existing application built on Access VBA with an SQL Server 2012 backend. It should allow the application to store open tasks with 1:n parameters in a queue table and process them later on. It deserves mentioning that for some tasks, it might take several process steps until all information needed for their processing is available.
Some more information on my current situation:
The data needs to be persisted in the database for compliance reasons
No more than 1500 tasks will be processed each day
The application will be rebuild (except for the backend), the new application will make much more heavy use of this queue functionality
The total number of different tasks to be queued, as well as the no. of parameters they might need, is unknown
My currently best approach - however in EAV schema - would consist of three tables:
1. Table "tblQueueItemType"
It contains definitions for each type (or category) of task.
It contains an id, a name and an attribute count. This attribute count defines the number of attributes for this task. I want to use it later on to ensure data consistency for all tasks with status "READY".
Example for an entry in this table:
"1", "Generate Book Database Entry", "5"
2. Table "tblQueueItemHeader"
It which represents the instantiated tasks defined in the tblQeueItemType. They have a task id, their corresponding task type defined in tblQeueItemType, a status as well as a timestamp.
The status is either OPEN (not all information available), READY (all information available to process task), and DONE (when processed).
Example for an entry in this table:
"2", "1", "OPEN"
3. Table "tblQueueItemAttribute"
It contains all the information the tasks need to be processed. It contains an id, the id of the header, an attribute type and an attribute value.
Example entries for this table:
"1","2", "Author", "H.G. Wells"
"1","2", "No. Pages", "1234"
My table definitions so far:
CREATE TABLE [dbo].[tblQueueItemType](
id INT NOT NULL IDENTITY (1,1) PRIMARY KEY,
Name NVARCHAR(20) NOT NULL,
AttributeCount INT NOT NULL
)
CREATE TABLE [dbo].[tblQueueItemHeader](
id INT NOT NULL IDENTITY (1,1) PRIMARY KEY,
QueueItemTypeId INT NOT NULL,
Status NVARCHAR(5) NOT NULL,
Timestamp DATETIME NOT NULL
CONSTRAINT QueueTypeHeader
FOREIGN KEY (QueueItemTypeId)
REFERENCES tblQueueItemType (id)
)
CREATE TABLE [dbo].[tblQueueItemAttribute](
id INT NOT NULL IDENTITY (1,1) PRIMARY KEY,
QueueItemHeaderId INT NOT NULL,
Attribute NVARCHAR(5) NOT NULL,
Value NVARCHAR(50) NOT NULL,
Timestamp DATETIME NOT NULL
CONSTRAINT QueueHeaderAttribute
FOREIGN KEY (QueueItemHeaderId)
REFERENCES tblQueueItemHeader (id)
)
ALTER TABLE tblQueueItemHeader
ADD CONSTRAINT QueueItemHeaderStatus
CHECK (Status IN ('OPEN', 'READY', 'DONE'));
Obviously the current design is suboptimal. What would be best schema for this kind of use-case? How feasible is my current approach?
Thank you very much!

Related

Can someone explain this statement (foreign key) of a table

CREATE TABLE genres
(
genre_id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
genre VARCHAR(255) N[enter image description here][1]OT NULL,
parent_id INT NULL,
-- Will be thankful to you for explaining the 3 lines below
PRIMARY KEY (genre_id),
CONSTRAINT fk_parent
FOREIGN KEY(parent_id) REFERENCES genres(genre_id)
);
PRIMARY KEY(genre_id) - means that at most one row in the table GENRES can have a specific value. In other words, for every row in the table, the value in GENRE_ID column will be unique. Additionally the value in the column cannot be null, and that value serves to identify the row uniquely without needing any other value to identify the row. The DDL shows that the database will generate the value for the GENRE_ID column by default.
The "CONSTRAINT fk_parent FOREIGN KEY(parent_id) REFERENCES genres(genre_id) )" means that the database-manger
will enforce that if column PARENT_ID is not null then the value in this column must be an existing value in a row in the GENRES table. Another way of thinking about this is that the database-manager is asked to maintain a parent-child relationship, so a genre may have sub-genres (i.e. a genre may have child genres). So the database manager would not let you specify that a particular genre was a sub-genre of a non-existent parent genre.
The database manager might also enforce the relationship is valid over time, for example it might prevent a delete or update action if that delete would produce orphan rows (i.e. child rows with no parent row) , or it may set such parent_id values to null, depending on the DDL and Db2-version/platform.
Answer:Self Referencing Foriegn Key
https://www.red-gate.com/simple-talk/sql/t-sql-programming/questions-about-primary-and-foreign-keys-you-were-too-shy-to-ask/

Choice of a primary key in SQL table

I want to make an SQL table to keep track of notes that are added/edited/deleted. I want to be able to display the state of each NOTEID at this moment in a table, display log of changes of selected note and be able to delete all notes marked with a given NOTEID.
create table[dbo].[NOTES]{
NOTEID [varchar](128) NOT NULL,
CREATEDBY [varchar](128) NOT NULL, /*is this redundant?*/
TIMECREATED DATE NOT NULL, /*is this redundant?*/
MODIFIEDBY [varchar](128) NOT NULL,
TIMEMODIFIED DATE NOT NULL,
NOTE [VARCHAR}(2000) NULL,
PRIMARY KEY ( /* undecided */ ),
};
What is the natural way of making this table? Should I autogenerate the primary ID or should I use (NOTEID,TIMEMODIFIED) as the primary key? What kind of fool proof protection should be added?
I would like to be able to display all notes in a "Note history" window. So, I should store note from 3 days ago, when it was created, note from 2 days ago and from today, when it was modified.
However, the "Notes" table will show the final state for each NOTEID. That is
SELECT NOTE from NOTES where NOTEID = 'selected_note_id' and date = latest
The best way is create two tables.
NOTES (
NOTE_ID -- primary key and autogenerated / autonumeric
CREATEDBY -- only appear once
TIMECREATED -- only appear once
NOTE
)
NOTES_UPDATE (
NOTES_UPDATE_ID -- primary key and autogenerated / autonumeric
NOTE_ID -- Foreign Key to NOTES
MODIFIEDBY
TIMEMODIFIED
NOTE
)
You can get your notes updates
SELECT N.*, NU.*
FROM NOTES N
JOIN NOTES_UPDATE NU
ON N.NOTE_ID = NU.NOTE_ID
and to get the last update just add
ORDER BY NOTE_UPDATE_ID DESC
LIMIT 1 -- THIS is postgres sintaxis.
SIMPLE ANSWER:
The PRIMARY KEY should be the value that unique identifies each row in your table. In your particular case, NOTEID should be your id.
ELABORATING:
It is important to remember that a PRIMARY KEY creates an index by default, which means that whenever you do a query similar to:
SELECT * FROM table WHERE NOTEID = something
The query will execute a lot faster than without an index (which is mostly relevant for bigger tables). The PRIMARY KEY is also forced to be unique, hence no two rows can have the same PRIMARY KEY
A general rule is that you should have an INDEX for any value that will often be used within the WHERE ... part of the statement. If NOTEID is not the only value you will be using in the WHERE .... part of the query, consider creating more indexes
HOWEVER! TREAD WITH CAUTION. Indexes help speed up searches with SELECT however they make UPDATE and INSERT work slower.
I think your current table design is fine, though you might want to make the NOTEID the primary key and auto increment it. I don't see the point of making (NOTEID, TIMEMODIFIED) a composite primary key because a given note ID should ideally only appear once in the table. If the modified time changes, the ID should remain the same.
Assuming we treat notes as files on a computer, then there should be only one table (file system) which stores them. If a given note gets modified, then the timestamp changes to reflect this.

Jdbc table design which is better

Hi I'm creating a notification system which consists of only 3 data inputs
1. email - to which the notification should be sent
2. the notification message - varchar
3. status - sent or not (y or N)
--Note the final goal is club all notification to be sent to an email and send it as one email by a batch job
Help me choosing which design is better
Design -1
create table Notifications(
notification_id integer auto_increment primary key,
message varchar(100) not null
);
create table Emails(
email_id integer not null auto_increment primary key,
email varchar(40) not null
);
create table Email_notifications(
email_id integer not null,
notification_id integer ,
status varchar(5) not null,
foreign key(email_id) references Emails(email_id),
foreign key(notification_id) references Notifications(notification_id),
primary key(email_id, notification_id)
);
Design-2:
create table batchnotifications(
id integer not null auto_increment primary key,
email varchar(40) not null,
message varchar(100) not null,
status varchar(5) not null default 'N'
);
Since i'm going to use JDBC in it let me know in that perspective in terms of ease of api creation.
You should use Design 1 Its better to implement.
you can use Design-2 also but if you have to send status to multiple people with different emails and notifications,then it is possible only with Design-1
Let's assume one condition :
If you have to send email with id 2 and notification with id 4 then in that case, you need two different tables for email and notification. which you are doing in Design-1.
AND
Let's assume another condition :
If you have to send unique email and notification with same id then use Design-2
Design 1 is future proof.
Design 2 is still correct with respect of normalization rules, assuming that:
you will not add later content like "Name", "Reputation", etc to the email. In that moment, usage of Design 1 is mandatory.
the relative large key of "email" is not a space/performance problem, as compared to the integer key.
The driver you use to connect (JDBC, DAO, ODBC, OLEDB or native) is irrelevant with respect of the data structure.

SQL Server automatic naming of indexes when creating tables

I'm rather new to SQL Server, (did learn SQL back in late 1980's, DB2 if I recall) Today I'm integrating my database layer into SQL, to begin with, SQL Server.
To begin with. As I do today, I will generate in runtime every databases objects, tables objects and indexes programmatically as I do with almost every visual and data object in my projects. That is, I use the visual designing tools very limited.
Every column in my project has a external description file's (every user has profile which contains these files), just as I do with database key's and for visual objects as for effect's as positioning, length, picture-mask, font size, etc. etc. i.e. dynamic forms. Almost every window, grids, filters is created in runtime just as far most of my database connections.
I did build a small test "machine" to create tables in this environment and did well, very easy to create tables within program (I use delphi and ADO)
The problem I encounter is when I flag a column as "autoincrement" or as Identity in SQL Server or if I describe a column as primary key, then SQL Server Management Studio creates automatically a index or key.
That would be ok if I could manage the name it gives this index or key.
Example of this situations:
AdoCommand.CommandText := Str_SQL;
TRY
AdoCommand.Execute;
FINALLY
NotiFy_TimeOut ('Table was created', wait_for_one_sec);
END;
My database engine creates this SQL script which I pass into the string Str_SQL above:
CREATE TABLE GENGITFL
(
NR INT NOT NULL IDENTITY,
GJM CHAR(3) NOT NULL,
HEITI VARCHAR(25) NOT NULL,
KAUPG REAL NULL,
SOLUG REAL NULL,
TOLLG REAL NULL,
DUMMY VARCHAR(20) NULL,
UNIQUE (GJM),
PRIMARY KEY (GJM)
)
SQL Server creates these two indexes automatically :
PK__GENGITFL__C51F17260A463F49
UQ__GENGITFL__C51F17277FA3E6E6
I don't want to use these names for these files, I would prefer names as:
IDX_GENGITFL_GJM
IDX_GENGITFL_NR
The reason should be obvious in light of my intro, the runtime engine can't create these names automatically and I consider it not a option to look for what index files are available within system database. If my external description say there should be index, I would like just to create names for the index automatically by using the prefix, IDX_ next the table name and last the field name or name's with underscore between, as IDX_GENGITFL_GJM etc.
Hope someone understand my poor english and presentation.. I'm rather rusty in english.
Thanks
Edit: After help from marc_s my SQL "script" is like this:
CREATE TABLE GENGITFL
(
NR INT NOT NULL IDENTITY,
GJM CHAR(3) NOT NULL,
HEITI VARCHAR(25) NOT NULL,
KAUPG REAL NULL,
SOLUG REAL NULL,
TOLLG REAL NULL,
DUMMY VARCHAR(20) NULL,
CONSTRAINT IDX_GENGITFL_NR UNIQUE (NR),
CONSTRAINT IDX_GENGITFL_GJM PRIMARY KEY (GJM),
)
CREATE INDEX IDX_GENGITFL_HEITI ON GENGITFL (HEITI)
Thanks again.
If you don't want the system default names - then just specify your own! :
CREATE TABLE GENGITFL
(
NR INT NOT NULL IDENTITY,
GJM CHAR(3) NOT NULL,
HEITI VARCHAR(25) NOT NULL,
KAUPG REAL NULL,
SOLUG REAL NULL,
TOLLG REAL NULL,
DUMMY VARCHAR(20) NULL,
CONSTRAINT IDX_GENGITFL_NR UNIQUE (GJM),
CONSTRAINT IDX_GENGITFL_GJM PRIMARY KEY (GJM)
)
See those CONSTRAINT (yourownnamehere) before the UNIQUE and the PRIMARY KEY ?
Now, your constraints are named as you defined.

SQL Server: how to constrain a table to contain a single row?

I want to store a single row in a configuration table for my application. I would like to enforce that this table can contain only one row.
What is the simplest way to enforce the single row constraint ?
You make sure one of the columns can only contain one value, and then make that the primary key (or apply a uniqueness constraint).
CREATE TABLE T1(
Lock char(1) not null,
/* Other columns */,
constraint PK_T1 PRIMARY KEY (Lock),
constraint CK_T1_Locked CHECK (Lock='X')
)
I have a number of these tables in various databases, mostly for storing config. It's a lot nicer knowing that, if the config item should be an int, you'll only ever read an int from the DB.
I usually use Damien's approach, which has always worked great for me, but I also add one thing:
CREATE TABLE T1(
Lock char(1) not null DEFAULT 'X',
/* Other columns */,
constraint PK_T1 PRIMARY KEY (Lock),
constraint CK_T1_Locked CHECK (Lock='X')
)
Adding the "DEFAULT 'X'", you will never have to deal with the Lock column, and won't have to remember which was the lock value when loading the table for the first time.
You may want to rethink this strategy. In similar situations, I've often found it invaluable to leave the old configuration rows lying around for historical information.
To do that, you actually have an extra column creation_date_time (date/time of insertion or update) and an insert or insert/update trigger which will populate it correctly with the current date/time.
Then, in order to get your current configuration, you use something like:
select * from config_table order by creation_date_time desc fetch first row only
(depending on your DBMS flavour).
That way, you still get to maintain the history for recovery purposes (you can institute cleanup procedures if the table gets too big but this is unlikely) and you still get to work with the latest configuration.
You can implement an INSTEAD OF Trigger to enforce this type of business logic within the database.
The trigger can contain logic to check if a record already exists in the table and if so, ROLLBACK the Insert.
Now, taking a step back to look at the bigger picture, I wonder if perhaps there is an alternative and more suitable way for you to store this information, perhaps in a configuration file or environment variable for example?
I know this is very old but instead of thinking BIG sometimes better think small use an identity integer like this:
Create Table TableWhatever
(
keycol int primary key not null identity(1,1)
check(keycol =1),
Col2 varchar(7)
)
This way each time you try to insert another row the check constraint will raise preventing you from inserting any row since the identity p key won't accept any value but 1
Here's a solution I came up with for a lock-type table which can contain only one row, holding a Y or N (an application lock state, for example).
Create the table with one column. I put a check constraint on the one column so that only a Y or N can be put in it. (Or 1 or 0, or whatever)
Insert one row in the table, with the "normal" state (e.g. N means not locked)
Then create an INSERT trigger on the table that only has a SIGNAL (DB2) or RAISERROR (SQL Server) or RAISE_APPLICATION_ERROR (Oracle). This makes it so application code can update the table, but any INSERT fails.
DB2 example:
create table PRICE_LIST_LOCK
(
LOCKED_YN char(1) not null
constraint PRICE_LIST_LOCK_YN_CK check (LOCKED_YN in ('Y', 'N') )
);
--- do this insert when creating the table
insert into PRICE_LIST_LOCK
values ('N');
--- once there is one row in the table, create this trigger
CREATE TRIGGER ONLY_ONE_ROW_IN_PRICE_LIST_LOCK
NO CASCADE
BEFORE INSERT ON PRICE_LIST_LOCK
FOR EACH ROW
SIGNAL SQLSTATE '81000' -- arbitrary user-defined value
SET MESSAGE_TEXT='Only one row is allowed in this table';
Works for me.
I use a bit field for primary key with name IsActive.
So there can be 2 rows at most and and the sql to get the valid row is:
select * from Settings where IsActive = 1
if the table is named Settings.
The easiest way is to define the ID field as a computed column by value 1 (or any number ,....), then consider a unique index for the ID.
CREATE TABLE [dbo].[SingleRowTable](
[ID] AS ((1)),
[Title] [varchar](50) NOT NULL,
CONSTRAINT [IX_SingleRowTable] UNIQUE NONCLUSTERED
(
[ID] ASC
)
) ON [PRIMARY]
You can write a trigger on the insert action on the table. Whenever someone tries to insert a new row in the table, fire away the logic of removing the latest row in the insert trigger code.
Old question but how about using IDENTITY(MAX,1) of a small column type?
CREATE TABLE [dbo].[Config](
[ID] [tinyint] IDENTITY(255,1) NOT NULL,
[Config1] [nvarchar](max) NOT NULL,
[Config2] [nvarchar](max) NOT NULL
IF NOT EXISTS ( select * from table )
BEGIN
///Your insert statement
END
Here we can also make an invisible value which will be the same after first entry in the database.Example:
Student Table:
Id:int
firstname:char
Here in the entry box,we have to specify the same value for id column which will restrict as after first entry other than writing lock bla bla due to primary key constraint thus having only one row forever.
Hope this helps!

Resources