Database design. One-to-many or Many-to-many? [closed] - mysql

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
In my application i need to assign multiple groups to my users. There are 1000+ users and 10-15 groups.
Which database design is better?
One-to-many:
USER_ID | GROUP_1 | GROUP_2 | ... | GROUP_15
--------------------------------------------
1 | true | false | ... | true
2 | false | true | ... | true
3 | true | true | ... | true
. | . | . | ... | .
. | . | . | ... | .
. | . | . | ... | .
or many-to-many:
USER_ID | GROUP_ID
------------------
1 | 1
1 | 15
2 | 2
2 | 15
3 | 1
3 | 2
3 | 15
. | .
. | .
. | .
?

The many-to-many is the better design without a doubt.
The first design makes writing queries difficult. Consider the following routine queries.
Is a specified user in a specified group? To do this you have to use a different query for each group. This is undesirable. Also if you are using column names for groups, then the list of groups is part of the database schema rather than being part of the data, where the users are data.
What groups is a specified user in? You could simply return the single row, though many applications would probably prefer (and are versed in) iterating through a result set. Iterating through a subset of columns is doable but unnatural.
What users does a specified group contain? Now you are back to the different queries for each group..
I'll leave the demonstration of these things as an exercise to the reader.
The relational model, which SQL databases approximate, was intended to deal with relations and keys (tables and primary/foreign keys). Information should exist in one (and ONLY ONE) place AS DATA (not metadata). The multi-column approach lacks normalization and will be a maintenance headache into the future.
Note: I edited this response to correct a misreading on my part of the original code. The thrust of the comments is the same however. The second (many-to-many) is the way to go.

If you want to follow the rules of an entity relationship model:
Many-to-many: users can belong to different groups & groups can have multiple users.
One-to-many: a user belongs to one group & groups can have multiple users.
Your second example is a many-to-many, your first isn't a one-to-many. A one-to-many would be:
USER_ID | GROUP_ID
------------------
1 | 1
2 | 15
3 | 2
4 | 15
5 | 1
6 | 2
7 | 15
Where user_id must be unique.

No 2 is standard, you can increase number of groups at any time, also you can handle easy sql join queries easily.

Related

Is it worth storing integer as string in table because it nice fits with business logic? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
In my application there are two types of users, anonymous and authenticated.
Authenticated users has integer for id, anonymous users has uuid as id. There is a table Item
which stores items that user has.
I have two ideas about how to store different id's in one table
First create column user_id as string and store both users ids in one column:
Item
+---------+----------------------------------------+----------+
| item_id | user_id | quantity |
+---------+----------------------------------------+----------+
| 1 | '1' | 2 |
| 2 | 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11' | 3 |
| 3 | '2' | 1 |
+---------+----------------------------------------+----------+
Second create two different columns user_id (integer) and anon_user_uuid (uuid),
Item
+---------+---------+----------------------------------------+----------+
| item_id | user_id | anon_user_uuid | quantity |
+---------+---------+----------------------------------------+----------+
| 1 | 1 | null | 2 |
| 2 | null | 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11' | 3 |
| 3 | 2 | null | 1 |
+---------+---------+----------------------------------------+----------+
In first case I need to write four queries for retrieving, updating, creating, deleting items.
In second I need to write eight queries for crud operations.
So my question, is it okey to have design as in first case (first table above)?
It looks like you've got two completely different types of values. In that case two columns is appropriate.
The reason you'd have them as one column is so you can set up foreign keys or other relations, but that's not possible if some of these can't be matched like that.
For consistency you might want to give every user an "anonymous" ID and just use that internally. Some of these might map to a registered user, others may not.
It's also possible to just create a user record for anonymous users and if the user registers after the fact, just populate the other fields and change that status to "registered".

Eloquent laravel : how count data from json column [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
Hi this is Products table
id | name | price
22 | product_1 | 10.00
33 | product_2 | 10.00
44 | product_3 | 10.00
and this Orders table.
in cartproducts column i have use json.
id| cartproducts | summ
1 | [{"productid":22,"quantity":1,"options":"[]"}] | 10.00
2 | [{"productid":33,"quantity":1,"options":"[]"},{"productid":44,"quantity":2,"options":"[]"}] | 30.00
3 | [{"productid":22,"quantity":3,"options":"[]"}] | 30.00
Using eloquent laravel how can i count how much order i have for each productid in Orders table using groupby method or other methods.
this is a simple imagine
$productorders = \App\Orders::where('productid',$productid)->groupBy('??')->orderby('count', 'desc')->get();
Desired output ( i will use my output in table sorted by Orders count )
product name | orders count | total earning | total quantity
product_1 | 2 | 40 | 4
product_2 | 1 | 10 | 1
product_3 | 1 | 20 | 2
thanks
You are making it hard for joining, and this table (Orders) as I can see it's only useful for logs. You can't join any other table to this table. Because you need to extract the corresponding values (ProductId and quantity) from the JSON field.
But even if you extract those fields, you can't handle the second record in Orders table, since you'll have two different productID, so you need to have an array field to extract the productId's into it, but then what you want to do with the array field, You can't really join it, and even if it's possible it'll make too way hard. for such small things.
I'll suggest stopping storing data which they eventually needs to be used as a joining key to other tables. You can store each item in OrderTable separately without using JSON.
There is a second solution which it's obvious, using PHP to handle the process in the application layer. You can json_deocde the contents of the orders and then the rest of the story which you know it.

How to structure database for Tinder functionality [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I'm building a functionality similar to Tinder. People can 'like' or 'skip' photo's of someone else, if both people 'like' each other then there is a match.
What is the best approach of a database structure for this functionality? I want to be able to get a list of all matches and all matches per Person.
Approach 1:
Person | JudgedPerson | Like
------ | ------------ | ----
1 | 2 | yes
2 | 1 | yes
1 | 3 | yes
3 | 1 | no
2 | 3 | yes
This looks like a logical approach, but it is difficult to create a MySql query to discover matches. Or is there a simple way to discover it?
Approach 2
Person1 | Person2 | P1LikesP2 | P2LikesP1
------- | ------- | --------- | ---------
1 | 2 | yes | yes
1 | 3 | yes | no
2 | 3 | yes | null
It's easy to create queries to get matches, but the datamodel might be not the best.
What is the best approach?
If approach 1 is the best approach, what mysql queries can I use to discover the matches?
I don't have a formal reason for why I prefer the first option, but it is clear that the second option is not completely normalized.
To query the first table and find pairs of people who like each other, you can try the following self join:
SELECT DISTINCT LEAST(t1.Person, t1.JudgedPerson) AS Person1,
GREATEST(t1.Person, t1.JudgedPerson) AS Person2
FROM yourTable t1
INNER JOIN yourTable t2
ON t1.JudgedPerson = t2.Person AND
t1.Person = t2.JudgedPerson
WHERE t1.Like = 'yes' AND
t2.Like = 'yes'
Note: I added DISTINCT along with LEAST/GREATEST to the SELECT clause because each match will actually come in the form of a duplicate. The reason for this is that, e.g. 1 -> 2, 2 -> 1 would be one matching record, but also 2 -> 1, 1 -> 2 would also be a second record.
Personally, I would consider adding another option to the presented ones: having 2 tables - likes and matches:
Matches
Person1 | Person2
------ | --------
1 | 2
1 | 3
2 | 1
3 | 1
Likes
Who | Whom | Likes
--- | -----|---------
2 | 3 | 'no'
Getting matches would be a simple query:
SELECT p.*
FROM Persons p
INNER JOIN Matches m ON p.Id = m.Person2
WHERE m.Person1 = #judgedPersonId
The idea is to precompute matches instead of resolving them on each query (either in background process or during Like operation - to remove two-way likes and add records to matches tables).
This way one gets faster and easier queries when selecting matches, but the approach involves additional complexity computing "matches" and doing related queries (e.g. finding people who are not yet matched and not disliked).

Find 'friend of a friend' relations in one Select (SUBSELECT and/or JOIN) [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have a "relationship" matrix like:
+---------+------------+------------+------------+------------+------------+
| name | Albert | Bob | Charles | Dale | Ethan |
+---------+------------+------------+------------+------------+------------+
| Albert | | 0 | 1 | 1 | -1 |
| Bob | | | 1 | -1 | 1 |
| Charles | | | | 0 | 1 |
| Dale | | | | | 0 |
| Ethan | | | | | |
+---------+------------+------------+------------+------------+------------+
0 means they don't know each other
1 means they like each other
-1 means they don't like each other
Now, I want to input two names and get the number of mutual known people and 'speculate' on their relationship by adding up the 'likes' (preferable in one single SELECT).
For example take the pair of Charles and Dale:
Charles knows Albert and Bob, who also know Dale. The relationship
between Charles and Dale would probably be friendly since Charles
likes Albert (+1) who likes Dale (+1) and Charles likes Bob (+1)
though Bob does not like Dale (-1).
So, the output would be 2 mutual known people and a 'speculation' of +3.
I can't get my head around a functional subselect query, plus the fact that the matrix is only half-filled seems to make it more complicated (sometimes a name is the first index, sometimes it is the second).
Could someone help me formulate a useful query, please?
As per a comment above you should modfify your table structure to something more sensible.
So we assume tables like:
Person - Columns: (PersonId, Name)
PersonRelationships - Columns: (Person1Id, Person2Id, Relationship)
Then a query might look like:
DECLARE #Person1Id INT;
DECLARE #Person2Id INT;
SET #Person1Id = 1;
SET #Person2Id = 2;
SELECT SUM(r1.Relationship + r2.Relationship)
(
SELECT
Person2Id AS CommonRelatedPersonId, Relationship
FROM PersonRelationships
WHERE Person1Id = #Person1Id
UNION
SELECT
Person1Id AS CommonRelatedPersonId, Relationship
FROM PersonRelationships
WHERE Person2Id = #Person1Id
) r1
JOIN
(
SELECT
Person2Id AS CommonRelatedPersonId, Relationship
FROM PersonRelationships
WHERE Person1Id = #Person2Id
UNION
SELECT
Person1Id AS CommonRelatedPersonId, Relationship
FROM PersonRelationships
WHERE Person2Id = #Person2Id
) r2 ON r1.CommonRelatedPersonId = r2.CommonRelatedPersonId;
Please excuse any syntax errors - I'm more used to MS SQL Server syntax.
Still you should be able to see the concept - you need a relationship table, linking people, and you need to assume the link could be in either direction (hence the unions above)
Join 2 unioned (A-> B + B -> A) copies together on the common related person and sum the total and you're there.

Database modeling for international and multilingual purposes

I need to create a large scale DB Model for a web application that will be multilingual.
One doubt that I've every time I think on how to do it is how I can resolve having multiple translations for a field. A case example.
The table for language levels, that administrators can edit from the backend, can have multiple items like: basic, advance, fluent, mattern... In the near future probably it will be one more type. The admin goes to the backend and add a new level, it will sort it in the right position.. but how I handle all the translations for the final users?
Another problem with internationalization of a database is that probably for user studies can differ from USA to UK to DE... in every country they will have their levels (that probably it will be equivalent to another but finally, different). And what about billing?
How you model this in a big scale?
Here is the way I would design the database:
Visualization by DB Designer Fork
The i18n table only contains a PK, so that any table just has to reference this PK to internationalize a field. The table translation is then in charge of linking this generic ID with the correct list of translations.
locale.id_locale is a VARCHAR(5) to manage both of en and en_US ISO syntaxes.
currency.id_currency is a CHAR(3) to manage the ISO 4217 syntax.
You can find two examples: page and newsletter. Both of these admin-managed entites need to internationalize their fields, respectively title/description and subject/content.
Here is an example query:
select
t_subject.tx_translation as subject,
t_content.tx_translation as content
from newsletter n
-- join for subject
inner join translation t_subject
on t_subject.id_i18n = n.i18n_subject
-- join for content
inner join translation t_content
on t_content.id_i18n = n.i18n_content
inner join locale l
-- condition for subject
on l.id_locale = t_subject.id_locale
-- condition for content
and l.id_locale = t_content.id_locale
-- locale condition
where l.id_locale = 'en_GB'
-- other conditions
and n.id_newsletter = 1
Note that this is a normalized data model. If you have a huge dataset, maybe you could think about denormalizing it to optimize your queries. You can also play with indexes to improve the queries performance (in some DB, foreign keys are automatically indexed, e.g. MySQL/InnoDB).
Some previous StackOverflow questions on this topic:
What are best practices for multi-language database design?
What's the best database structure to keep multilingual data?
Schema for a multilanguage database
How to use multilanguage database schema with ORM?
Some useful external resources:
Creating multilingual websites: Database Design
Multilanguage database design approach
Propel Gets I18n Behavior, And Why It Matters
The best approach often is, for every existing table, create a new table into which text items are moved; the PK of the new table is the PK of the old table together with the language.
In your case:
The table for language levels, that administrators can edit from the backend, can have multiple items like: basic, advance, fluent, mattern... In the near future probably it will be one more type. The admin goes to the backend and add a new level, it will sort it in the right position.. but how I handle all the translations for the final users?
Your existing table probably looks something like this:
+----+-------+---------+
| id | price | type |
+----+-------+---------+
| 1 | 299 | basic |
| 2 | 299 | advance |
| 3 | 399 | fluent |
| 4 | 0 | mattern |
+----+-------+---------+
It then becomes two tables:
+----+-------+ +----+------+-------------+
| id | price | | id | lang | type |
+----+-------+ +----+------+-------------+
| 1 | 299 | | 1 | en | basic |
| 2 | 299 | | 2 | en | advance |
| 3 | 399 | | 3 | en | fluent |
| 4 | 0 | | 4 | en | mattern |
+----+-------+ | 1 | fr | élémentaire |
| 2 | fr | avance |
| 3 | fr | couramment |
: : : :
+----+------+-------------+
Another problem with internationalitzation of a database is that probably for user studies can differ from USA to UK to DE... in every country they will have their levels (that probably it will be equivalent to another but finally, different). And what about billing?
All localisation can occur through a similar approach. Instead of just moving text fields to the new table, you could move any localisable fields - only those which are common to all locales will remain in the original table.