DB design for alternative components - database

I'm trying to figure out the "best way" to design a database for a product configurator, where there are Items and Components. Now each Item can have multiple Components, where (potentially) any Component can have other Component(s) which can be its valid substitute(s) or incompatible with it.
P.S. Sorry for the noob question and for my poor English.

Items and Components relationship can be done by one to many approach. You can design your tables by connecting primary keys and foreign keys to each other.
My suggestion for your other problem is that to use "parent_id" approach. A component table can have a "parent_id" column, so you can set the actual component and its substitutes.
Here is the example Components table:
+----+---------+-----------+
| id | item_id | parent_id |
+----+---------+-----------+
| 1 | 1 | null |
| 2 | 1 | 1 |
| 3 | 1 | 1 |
+----+---------+-----------+
The table above, item_id = 1 has three components with ids 1,2 and 3. The component with id=1 has two substitutes which are 2 and 3.
UPDATE
+----+---------+-----------+
| id | item_id | parent_id |
+----+---------+-----------+
| 1 | 1 | null |
| 2 | 1 | 1 |
| 3 | 1 | 2 |
+----+---------+-----------+
The table above, component_id 3 is the substitute of component_id 2 (look at its parent_id), component_id 2 is the substitute of component_id 1.

Item-Component table like this:
+--+------------+---------+-----------+
|id|component_id|parent_id|parent_type|
+--+------------+---------+-----------+
| 1| 1| 1| i|
| 2| 2| 1| c|
+--+------------+---------+-----------+
where, the value of parent_type shows the parent is item column (with i) or component column (with c)
Item table like this:
+--+-------+
|id|content|
+--+-------+
| 1| xxx|
+--+-------+
Component table like this:
+--+-------+-------------+
|id|content|other_columns|
+--+-------+-------------+
| 1| xxx| ...|
| 2| xxx| ...|
+--+-------+-------------+

Related

Records that have not been accessed by a different table

I have a users table, videos table and actions table. When a user views a video, an actions record gets created with both their ids and some information. I want to get the Y videos not accessed by X users.
Users table
|---------------------|------------------|---------------------|
| ID | Name | account_id |
|---------------------|------------------|---------------------|
| 1 | user 1 | 1 |
|---------------------|------------------|---------------------|
| 2 | user 2 | 1 |
|---------------------|------------------|---------------------|
| 3 | user 3 | 1 |
|---------------------|------------------|---------------------|
Videos table
|---------------------|------------------|---------------------|
| ID | Name | account_id |
|---------------------|------------------|---------------------|
| 1 | video 1 | 1 |
|---------------------|------------------|---------------------|
| 2 | video 2 | 1 |
|---------------------|------------------|---------------------|
| 3 | video 3 | 1 |
|---------------------|------------------|---------------------|
Actions table
|---------------------|------------------|---------------------|
| user_id | video_id | account_id |
|---------------------|------------------|---------------------|
| 1 | 3 | 1 |
|---------------------|------------------|---------------------|
| 1 | 2 | 1 |
|---------------------|------------------|---------------------|
| 3 | 1 | 1 |
|---------------------|------------------|---------------------|
| 3 | 2 | 1 |
|---------------------|------------------|---------------------|
Desired Output:
|---------------------|------------------|
| user name | video name |
|---------------------|------------------|
| user 1 | video 1 |
|---------------------|------------------|
| user 2 | video 1 |
|---------------------|------------------|
| user 2 | video 2 |
|---------------------|------------------|
| user 2 | video 3 |
|---------------------|------------------|
| user 3 | video 3 |
|---------------------|------------------|
Edit: I think is worth mentioning that there is also a foreign key attached to all of the columns called account_id. for the example we'll assume they all have the same account_id.
demo:db<>fiddle
SELECT
u.name,
v.name
FROM
users u CROSS JOIN videos v
WHERE (u.id, v.id) NOT IN (
SELECT user_id, video_id FROM actions
)
Note: This expected result seems really unhandy. On bigger data sets you may get a really huge output!
First you have to create a CROSS JOIN which joins every user record agains every video record. Afterwards you can filter the existing combinations using the action table.

how to draw data from multiple tables?

I have 2 different tables. I need to get a name from the TMK table in table 1 as below, and I need to bring the total number from my 2nd table. I can't write join. can u help me
TMK Table;
| tmkName |
| George |
| Jacob |
flowNewStatus Table;
|statusId|
| 1 |
| 2 |
if george has number 1 status i want this join
| tmkName | |statusId|
| George | | 1 |
Before getting to possible SQL queries... from the tables you show you'd need an additional table that associates the person to status, a join table. Essentially a TMK_status table:
TMK_status table
| personID | statusID |
|----------|----------|
| 1 | 1 |
| 2 | 3 |
| 3 | 1 |
Alternatively, the statusID could be stored as a column of TMK thus,
TMK table
| personID | tmkName | statusID |
|----------|----------|----------|
| 1 | George | 1 |
| 2 | Jacob | 3 |
If by "I can't write join", you mean you don't know how, check this answer: What is the difference between "INNER JOIN" and "OUTER JOIN"? - you will need an inner join.
If, on on the other hand, you mean you can't use join statements, then you could write a subselect statement. There could be other solutions but they depend on how you decide to join/relate the 2 tables.

How do I set 2 columns so each entry is unique against both columns?

I have a record that holds 2 license "keys" (actually GUIDs). When a request comes to our service it includes a key (GUID) in the request. I then do a query looking for a record that has this value in either the column Key1 or Key2.
The purpose of this is users will use Key1 for everything. Then they discover that Key1 has become public. So they switch to Key2 and then after 15 minutes, change the value of Key1. Now the old Key1 value is of no use.
By having the 2 keys, it allows the switch over with no downtime.
I need any key value to be unique. Not that any pair of values is unique. Not that a value in Key1 is unique in all rows for Key 1. But that a new value is unique in all rows.Key1 and rows.Key2.
Is there a way to force this in Sql Server. Or do I need to do this myself with a select before doing an insert or update?
-------------------------------------------------------------------------------------------
| LicenseId | ApiKey1 | APiKey2 |
| 1 | af53d192-7fa3-4be0-b3d4-7efe17a397b5 | 1a87cc4a-1941-4af7-aeaa-bf9690f47eef |
| 2 | 5bbc2d06-ed6f-4444-aa22-73820dd6f3f6 | c2bdd9d9-fd47-4727-83f8-02ed0e7537e1 |
| 3 | 8acfa8b4-aa4b-41a7-9d3d-b6ba1eac838e | 30c18f2d-5d89-4e5d-8e8e-2d2b647d6ab6 |
-------------------------------------------------------------------------------------------
I need to insure if I am going to create record LicenseId = 4, that if it has ApiKey2 = 'af53d192-7fa3-4be0-b3d4-7efe17a397b5', that the insert will fail because that guid is ApiKey1 for LicenseId = 1.
The most natural way to enforce this in the database is to put all keys in a single column. Eg
create table ApiKeys
(
LicenceId int,
KeyId int check (KeyId in (0,1)),
constraint pk_ApiKeys primary key (LicenceId,KeyId),
KeyGuid uniqueidentifier unique
)
Arguably having both the keys on the same row violates 1NF, and certainly your desire for uniqueness across the two column strongly suggests that they belong to a single domain.
So instead of storing ApiKey1 and ApiKey2 on the same row, you store them on two separate rows.
So instead of
---------------
| LicenseId | ApiKey1 | APiKey2 |
| 1 | af53d192-7fa3-4be0-b3d4-7efe17a397b5 | 1a87cc4a-1941-4af7-aeaa-bf9690f47eef |
| 2 | 5bbc2d06-ed6f-4444-aa22-73820dd6f3f6 | c2bdd9d9-fd47-4727-83f8-02ed0e7537e1 |
| 3 | 8acfa8b4-aa4b-41a7-9d3d-b6ba1eac838e | 30c18f2d-5d89-4e5d-8e8e-2d2b647d6ab6 |
-------------------------------------------------------------------------------------------
You would have:
----------------------------------------------------------
| LicenseId | KeyId | ApiKey |
| 1 | 0 | af53d192-7fa3-4be0-b3d4-7efe17a397b5|
| 1 | 1 | 1a87cc4a-1941-4af7-aeaa-bf9690f47ee4|
| 2 | 0 | 5bbc2d06-ed6f-4444-aa22-73820dd6f3f6|
| 2 | 1 | c2bdd9d9-fd47-4727-83f8-02ed0e7537e1|
| 3 | 0 | 8acfa8b4-aa4b-41a7-9d3d-b6ba1eac838e|
| 3 | 1 | 30c18f2d-5d89-4e5d-8e8e-2d2b647d6ab6|
----------------------------------------------------------

Is this a good design for a table?

Each user/person could know one or more languages.
All I can think is a table like
+----------+------+-----+------------+-----+-----+-----+-------+
| PersonID | Java | PHP | Javascript | C++ | C | CSS | HTML |
+----------+------+-----+------------+-----+-----+-----+-------+
| 1 | Yes | Yes | No | Yes | No | Yes | No |
| 2 | No | Yes | Yes | No | Yes | No | No |
| 3 | Yes | No | Yes | Yes | Yes | Yes | No |
+----------+------+-----+------------+-----+-----+-----+-------+
Considering I'm going to need at least 100 columns for all the languages, is it normal to have that many columns? Something tells me this is the wrong approach.
Thank you very much and sorry about my english!
I would suggest you to create three tables.
One table contains the information of the Person like his Name etc.
Second table contains two columns LanguageId and Language name.
+------------+-----------+
| LanguageID | Name |
+------------+-----------+
| 1 | Javascript|
| 2 | C |
| 3 | C++ |
+------------+-----------+
Third table contains the Id, PersonId, LanguageID. In this table you can join the above two tables record.
+---+----------+------------+
|ID | PersonID | LanguageID |
+---+----------+------------+
|1 | 1 | 1 |
|2 | 2 | 2 |
|3 | 3 | 3 |
+---+----------+------------+
Reasons to support my answer:
In future if you want to add any new language in your table then it
would be easier to add that in the main table.
You can join the two tables easily and get the result
A little improvement we can do over Rahul Tripathi response is to remove the "Known" column. You need only two tables for this case. One containing PersonId and LanguageId the person knows. The second table is for the languages only.
You know what languages one person knows by joining both tables. For example if you need to know the list of known languages you can do:
SELECT p.PersonId, l.Name
FROM Person p INNER JOIN Language l ON (p.LanguageId = l.LanguageId)
WHERE (p.PersonId = theIdYouNeedToKnow)

How to implement Auto_Increment per User, on the same table?

I would like to have multiple users that share the same tables in the database, but have one auto_increment value per user. I will use an embedded database, JavaDB and as what I know it doesn't support this functionality. How can I implement it?
Should I implement a trigger on inserts that lookup the users last inserted row, and then add one, or are there any better alternative? Or is it better to implement this in the application code?
Or is this just a bad idea? I think this is easier to maintain than creating new tables for every user.
Example:
table
+----+-------------+---------+------+
| ID | ID_PER_USER | USER_ID | DATA |
+----+-------------+---------+------+
| 1 | 1 | 2 | 3454 |
| 2 | 2 | 2 | 6567 |
| 3 | 1 | 3 | 6788 |
| 4 | 3 | 2 | 1133 |
| 5 | 4 | 2 | 4534 |
| 6 | 2 | 3 | 4366 |
| 7 | 3 | 3 | 7887 |
+----+-------------+---------+------+
SELECT * FROM table WHERE USER_ID = 3
+----+-------------+---------+------+
| ID | ID_PER_USER | USER_ID | DATA |
+----+-------------+---------+------+
| 3 | 1 | 3 | 6788 |
| 6 | 2 | 3 | 4366 |
| 7 | 3 | 3 | 7887 |
+----+-------------+---------+------+
SELECT * FROM table WHERE USER_ID = 2
+----+-------------+---------+------+
| ID | ID_PER_USER | USER_ID | DATA |
+----+-------------+---------+------+
| 1 | 1 | 2 | 3454 |
| 2 | 2 | 2 | 6567 |
| 4 | 3 | 2 | 1133 |
| 5 | 4 | 2 | 4534 |
+----+-------------+---------+------+
If you can guarantee that there will only be one session per user, then it would be pretty safe to do. If a user can have more than one session then whether you do this in a trigger or in the application code you will need to take an exclusive table lock to make sure that the session you are in is the only one to get that next number.
But don't go for a table per user. That would make your sql really ugly and prevent any sort of sql plan sharing.
You may be better served by using a timestamp instead of a serial number.

Resources