SQL - Table structure for read/unread Notifications per user - sql

I have a requirement to create a notification module on a web application and I would like to know which user has read the notification so I won't have to display the Red Badge that indicates that a notification is not yet read.
I have thought of two solutions.
The first one is a bit naive but I find it easier to implement and maintain:
Solution #1:
CREATE TABLE [Notifications1]
(
Id integer NOT NULL,
Title varchar(255) NOT NULL,
Description varchar(255) NOT NULL,
DateInserted datetime NOT NULL,
ReadByUsers varchar(MAX) NOT NULL
)
So in this case, when a user opens an unread notification, I will append the User ID on the ReadByUsers column, so it will look like this 10,12,15,20 ...
Then when I need to check whether the user has read the notification, I will simply query the column to see whether the user id is included.
The second solution looks more of 'best practice' approach but it gets bit more complicated and a bit more heavy I guess..
Solution #2:
CREATE TABLE [Notifications]
(
Id integer NOT NULL,
Title varchar(255) NOT NULL,
Description varchar(255) NOT NULL,
DateInserted datetime NOT NULL,
CONSTRAINT [PK_NOTIFICATIONS]
PRIMARY KEY CLUSTERED
)
CREATE TABLE [Notification_Users]
(
NotificationId integer NOT NULL,
UserId integer NOT NULL
)
In this case I store the user that has seen the notification in a different table with a relationship (One to Many) on the Notification Id on the main table. And then I can check the Notification_Users table to check if the user has seen the notification.
Also note that :
I don't need to know the time the user has seen the notification.
The application is not expected to reach more than 10000 users any time soon.
Notifications will be deleted from database after few months. So max 10 notifications at a time.
Do you have any suggestions for better implementation?
Thanks

Solution #2 would be the preferred solution.
Storing a list of Ids is not a normalized solution. In my opinion, the return on investment of breaking this out into two tables out weights the perceived complexity.
Some good info below, and honestly all over the net.
A Database "Best" Practice
https://softwareengineering.stackexchange.com/questions/358913/is-storing-a-list-of-strings-in-single-database-field-a-bad-idea-why

Related

How should I construct my tic-tac-toe server using SQL

This is my first post on StackOverflow. I apologize that my question is almost open-ended. But I'm working on a full-stack app for Tic-Tac-Toe.
I want to make it to where two players can create a room to play with one another. To do this I want to make a small database using SQL so that data can be stored, this is also for a project. My goal, for now, is to make this app playable through Postman. I want the client to be responsible for creating a game instance, which would end up being a URL with a unique 'roomId' at the end of it. I'm having trouble creating the tables which would store that data. I'm still at square one essentially.
What I'm having trouble with currently is creating the row which would store the 3 x 3 board. I've tried making a separate table that would store what is basically just an array. The entries are squareOne, squareTwo, etc. I don't know if this is correct because I'm not sure how I would set it up to where the client knows which specific square it's going to be updating. I know that the client would likely be responding with an index and a player id, so it would look something like
...com/game/:roomId/:playerId/:index
Here are my tables :
CREATE TABLE board
(
id INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
squareOne INTEGER NOT NULL,
squareTwo INTEGER NOT NULL,
squareThree INTEGER NOT NULL,
squareFour INTEGER NOT NULL,
squareFive INTEGER NOT NULL,
squareSix INTEGER NOT NULL,
squareSeven INTEGER NOT NULL,
squareEight INTEGER NOT NULL,
squareNine INTEGER NOT NULL,
);
CREATE TABLE game
(
id INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
date_created TIMESTAMP DEFAULT now() NOT NULL,
date_ended TIMESTAMP,
player_started_id INTEGER,
player_joined_id INTEGER,
board ??? I don't know what should go here
);
What I want this server to do is ultimately be able to handle multiple games running at once. So each time a game is created a new entry is made on the game table and the URL that responds with that unique ID is referencing the id of the game table id.
Please let me know where I can improve, and how to properly set these tables.
Maybe you'll prefer to store the player choices instead of the all board. Your board table represent your full business object. You could try a table like : GAME_ID, PLAYER_ID, SQUARE_X, SQUARE_Y (X and Y represent the position on the board). You can make a composite Primary key with GAME_ID, SQUARE_X, SQUARE_Y to ensure that only one player can be on a square. With this rows you can rebuild your "BOARD" game business object.
Even your actual model could handle multiple games, but you never talk about the technology used to serve API. (ASP.net ? node.js ? )

Automatical creation of indentity(1,1) but with GUIDs instead of integers

A neat thingy is to put identity(2,3) on a column to get the counter and an unique value to index. I wonder if, and in such case how, it's possible to do the same with a guid.
So, instead of inserting a row with newid(), I'd have one created for me.
If there's no way to do that, I'd like to know the technical rationale behind it. If there is any, of course.
create table Records (
Id int identity(3,14) primary key,
Occasion datetime not null,
Amount float not null,
Mileage int not null,
Information varchar(999) default ' '
)
As the lazy efficient #MartinSmith commented (but didn't bother to formulate a full-size reply, forcing me to do that for him, hehe), we can use default keyword to achieve the requested behavior as shown below.
create table Records (
Id uniqueidentifier primary key default newid(),
Occasion datetime not null,
Amount float not null,
Mileage int not null,
Information varchar(999) default ' '
)
And if the said lazy Martin wishes, he might just copy over the contents of this answer and post it as his own reply, since it was really his suggestions and I'm merely a typing machine for him. Given that he's got rep like an immortal God, I doubt he will, but still - I want to make this perfectly clear.

SQL Server Vocabulary table problems

I have a project that I'm working on, it's kind of... say it's a quiz.
Each registered user can sign in to this system (ASP.NET MVC project) and answer easy questions that only requires one word as an answer. Each word starts at level 1, max is level 5, if you fail answering a question, you get -1 and +1 if you give the correct answer, no big deal.
My problem here is that I don't know how to create this SQL model to store the -1 and +1 (range from 1-5) data in the same table for all registered users. Sure I can store everything in one or two cells separated by commas, but that's way to ugly. I want to focus on using foreign keys for this approach (or?). So far I've created the basic structure since I'm not sure how to create the above described approach.
As I've said, each correct answer (one word answer), raises +1 (range 1-5) for that word. Each word/answer should be connected to all users and to which level each user accessed. Please help me connect the dots here, thank you.
This is the basic structure so far.
create table Users
(
Id int primary key not null identity,
Username nvarchar(256) not null,
Password nvarchar(256) not null
);
go
create table Words
(
Id int primary key not null identity,
Words nvarchar(256) not null,
Answers nvarchar(256) not null,
TimeStamp DateTime not null
);
go

SQL Zero to One, or Boolean

I'm currently migrating a tonne of user data from CSV spreadsheets to an SQL database, designing the schema's and all that jazz.
At this time, the Users table looks like this:
CREATE TABLE Users
(
id INTEGER NOT NULL AUTO_INCREMENT,
name VARCHAR(48) NOT NULL,
alias VARCHAR(24) NOT NULL UNIQUE,
email VARCHAR(128) NOT NULL,
date_of_birth DATE,
location VARCHAR(24) NOT NULL,
date_joined DATE,
...
PRIMARY KEY (id)
);
We (the group) are looking to soon test an email subscription service for certain events and write-ups; which will be managed by looking at who is subscribed in the database.
My initial thoughts were to just add a boolean value to the Users table, representing whether they are subscribed or not, but editing the schema like this seems like a bad practice.
My second thoughts were something like:
CREATE TABLE Subscribers
(
uid INTEGER NOT NULL,
PRIMARY KEY (uid)
FOREIGN KEY (uid) REFERENCES Users(id)
);
What are the pro-cons of the two approaches? Which would be most suitable for my situation, given the test may be experimental.
Which course of action is suitable, depends largely on the expected use cases. If you can subscribe only to one newsletter then the boolean flag version should be preferred, since it is much faster to find the subscribers with a
select * from User where subscribed = 1
than doing a join with the possibly large Subscribers table. However, if you can subscribe to more than one newsletter, this would mean adding one column per newsletter to your users table (e.g subscribedToProductAnnouncements, subscribedToHolidayNewsletter, etc.). If you got a real lot of users (several millions) that might still be an option if you run into performance issues, but unless you really get performance issues using the Subscribers table would be preferable as you can add more newsletters without having to change the database schema all the time. That would require you to extend the Subscribers table with one field, though:
CREATE TABLE Subscribers
(
id LONG PRIMARY KEY AUTO_INCREMENT,
newsletterId INTEGER NOT NULL -- the ID of the newsletter that the user is subscribed to
uid INTEGER NOT NULL,
FOREIGN KEY (uid) REFERENCES Users(uid)
);
I also would go for a LONG in the id column of Subscribers as subscriptions may come and go and you might eventually hit the 2 billion limit of INTEGER. And probably renaming that table to Subscriptions is also a good idea as it actually contains subscriptions and not subscribers.
Advantages of the Subscribers table approach include:
it is a higher -- actually, the highest -- normal form, being 6NF;
it is more portable e.g. not all SQL products have a Boolean type;
There are further reasons in this article by Joe Celko.
The Subscribers table as posted has a few flaws, though. The uid columns should have a unique constraint. The auto-increment column is redundant. The foreign key should probably have the referential action ON DELETE CASCADE (and ON UPDATE CASCADE could prove useful in the future).
well you would be better off using a Boolean... with your second thought, if a user wanted to unsubscribe you would have to delete them from the database which is a very bad idea
thats where the boolean would be better to determine if that user will allow you to send them an email
(Side note: Users doesn't have a uid column.)
Subscribers doesn't need a surrogate key. It can have just one column, which is a primary key and a foreign key on Users(id).

Data Base design turnkey records

I have some questions regarding data base design for simple CMS.
Every Page in Table "PAGES" could be "AssignedTo" and "LockedBy" by a specific User.
To manage Users I am using SQL specific table "aspnet_Users", every User is identified using a GUI uniqueIdentifier column.
If a page is "Unlock" and can be edited by Users, its value in Table "Pages" would be NULL, same mechanism for "AssignedTo".
Here my script when I create Table "PAGES":
LockedBy uniqueidentifier NULL FOREIGN KEY REFERENCES aspnet_users(UserId),
AssignedTo uniqueidentifier NULL FOREIGN KEY REFERENCES aspnet_users(UserId)
My question:
This design will generate many NULL values in "LockedBy" since Pages will be locked only in the moment of editing, because I heard have many NULL values is not a good thing in Data Base design,, I would like to know if my design is in good practice, or if you could suggest a better way.
Thanks guys
Normally I think it's a good idea to have this in one table, ignoring the fact that there's many NULL values, but here's another solution:
You could split this into two other tables:
PageLocks and PageAssignedTo
PageLocks:
PageID, UserID <- unique key
PageAssignedTo:
PageID, UserID <- unique key