-1

I am trying to design an SQL lite database. I have made a quick design on paper of what I want.

So far, I have one table with the following columns:

  • user_id (which is unique and the primary key of the table)
  • number_of_items_allowed (can be any number and does not have to be unique)
  • list_of_items (a list of any size as long as it is less than or equal to number_of_items_allowed and this list stores item IDs)

The column I am struggling the most with is list_of_items. I know that a relational database does not have a column which allows lists and you must create a second table with that information to normalise the database. I have looked at a few stack overflow answers including this one, which says that you can't have lists stored in a column, but I was not able to apply the accepted answer to my case.

I have thought about having a secondary table which would have a row for each item ID belonging to a user_id and the primary key in that case would have been the combination of the item ID and the user_id, however, I was not sure if that would be the ideal way of going about it.

forpas
  • 160,666
  • 10
  • 38
  • 76
knowledge_seeker
  • 811
  • 1
  • 8
  • 18
  • 1
    I would not have a column `list_of_items`, I would definitely prefer the other table with a composite unique index of userID and itemID. No primary key would be needed, though you could have one, as long as the other two columns are a unique composite index to avoid duplicates. – Paul T. Aug 07 '21 at 23:58

1 Answers1

1

Consider the following schema with 3 tables:

CREATE TABLE users (
  user_id INTEGER PRIMARY KEY, 
  user TEXT NOT NULL,
  number_of_items_allowed INTEGER NOT NULL CHECK(number_of_items_allowed >= 0)
);

CREATE TABLE items (
  item_id INTEGER PRIMARY KEY, 
  item TEXT NOT NULL
);

CREATE TABLE users_items (
  user_id INTEGER NOT NULL REFERENCES users(user_id) ON UPDATE CASCADE ON DELETE CASCADE, 
  item_id INTEGER NOT NULL REFERENCES items (item_id) ON UPDATE CASCADE ON DELETE CASCADE, 
  PRIMARY KEY(user_id, item_id)
);

For this schema, you need a BEFORE INSERT trigger on users_items which checks if a new item can be inserted for a user by comparing the user's number_of_items_allowed to the current number of items that the user has:

CREATE TRIGGER check_number_before_insert_users_items 
BEFORE INSERT ON users_items
BEGIN
   SELECT
     CASE
       WHEN (SELECT COUNT(*) FROM users_items WHERE user_id = NEW.user_id) >=
            (SELECT number_of_items_allowed FROM users WHERE user_id = NEW.user_id)
         THEN RAISE (ABORT, 'No more items allowed')
     END;
END;

You will need another trigger that will check when number_of_items_allowed is updated if the new value is less than the current number of the items of this user:

CREATE TRIGGER check_number_before_update_users 
BEFORE UPDATE ON users
BEGIN
   SELECT
     CASE
       WHEN (SELECT COUNT(*) FROM users_items WHERE user_id = NEW.user_id) > NEW.number_of_items_allowed
         THEN RAISE (ABORT, 'There are already more items for this user than the value inserted')
     END;
END;

See the demo.

forpas
  • 160,666
  • 10
  • 38
  • 76
  • Thanks. I have used your database and updated it to allow for expiration of credits, which can be seen on [this](https://www.db-fiddle.com/f/kNcpTN1neadgPJsAmh4pzL/1) fiddle. Do you know why I am getting an error on it? – knowledge_seeker Aug 08 '21 at 16:04
  • 1
    @knowledge_seeker this probably a bug from the fiddle. Here: https://dbfiddle.uk/?rdbms=sqlite_3.27&fiddle=d056c79d4a841122167d3628ed94b9d9 there is no error. – forpas Aug 08 '21 at 16:11