I have 2 tables and was wondering what the best relationship between them was. I know there is a relationship between them but I get so confused with one to many, many to one, many to many, unidirectional, bidirectional, multidirectional etc.
So this is the basic, displayed, structure:
Traveler Table:
+------------------------------------------+
| Name | Family Name | National ID No. |
+------------------------------------------+
| Dianne | Herbert | 579643 |
| Francine | Jackson | 183432 |
| Oprah | Dingle | 269537 |
+------------------------------------------+
Journeys Table
+------------------------------------------------------------------------------------------------------+
| Start Station | End Station | Start Time | End Time | Travelers |
+------------------------------------------------------------------------------------------------------+
| Hull | Leeds | 13:50 | 14:50 | Francine Jackson, Oprah Dingle |
| Newcastle | Manchester | 16:30 | 19:00 | Dianne Herbert, Francine Jackson |
| Hull | Manchester | 10:00 | 13:00 | Dianne Herbert, Francine Jackson, Oprah Dingle |
+------------------------------------------------------------------------------------------------------+
The travelers table is okay, it makes sense:
CREATE TABLE Travelers (
Name VARCHAR(50) NOT NULL,
Family_Name VARCHAR(50) NOT NULL,
National_ID_Number INT(6) NOT NULL PRIMARY KEY
)
But I am unsure about how to do the journeys table. Especially with Travelers:
CREATE TABLE Journeys (
Start_Station VARCHAR(50) NOT NULL,
End_Station VARCHAR(50) NOT NULL,
Start_Time VARCHAR(50) NOT NULL,
End_Time VARCHAR(50) NOT NULL,
Travelers ???????
)
Obviously I have "Travelers" as a column inside my 2nd table. So there is a relationship there with the first table. But what is it? I think I need to make a Foreign Key somehow?
You are looking for a junction/association table. The tables should look like this:
create table Journeys (
Journey_Id int auto_increment primary key,
Start_Station VARCHAR(50) NOT NULL,
End_Station VARCHAR(50) NOT NULL,
Start_Time VARCHAR(50) NOT NULL,
End_Time VARCHAR(50) NOT NULL
)
create table TravelerJourneys (
traveler_journey_id int auto_increment primary key,
traveler_id int(6),
journey_id int,
foreign key (traveler_id) references travelers(National_ID_Number),
foreign key (journey_id) references Journeys (journey_id)
);
I Relational • Pre-Requisite Explanation
There is an awful lot of misinformation; disinformation in the "literature" produced by the "theoreticians" and all the authors that follow them. Of course that is very confusing and leads to primitive, pre-relational Record Filing Systems with none of the Integrity; Power; and Speed of Relational Systems. Second, while newbies try hard to answer questions here, due to the above, they are also badly confused.
I can't provide a tutorial, this is not-so-short explanation of the issues that you need to understand before diving in to the Question.
1 Relationship
I get so confused with one to many, many to one, many to many, unidirectional, bidirectional, multidirectional etc.
unidirectional, bidirectional, multidirectional
Please delete those from your mind, they are not Relational terms (the "theoreticians" and newbies love to invent new things, they have no value other than to add confusion).
There is no direction in a relationship. It always consists of:
a Primary Key: thing that is referenced, the parent end, and
a Foreign Key: the child end, thing that is referencing the parent PK
At the SQL code level, DML, you could perceive a "direction", parent-to-child, or child-to-parent. It is a matter of perception (not storage) and relevant only to the requirement of the code, the "way to get from this data to that data".
At the physical level, SQL DDL, there is only one type of relationship Parent::Child, and that is all we have ever needed. No Cardinality yet, because that is controlled by other means. As with the natural world, the parent is the thing that is referenced, the child is the thing that references the parent.
At the bare bones level, that is not a Relational database, but a 1960's Record Filing System, the relationship is Referenced:: Referencing, and God only knows what each thing is.
The child can have only one parent, and the parent can have many children, therefore the one-and-only relationship at the physical level is:
one [parent] to 0-to-many [children]
A Relational database is made up of things (rows, the main symbol, with either square or round corners)) and relationships between things (the lines, either Identifying or Non-Identifying). A thing is a Fact, each row is a Fact, the relationships are relationships between Facts.
In the Relational Model, each thing must be uniquely Identified, each logical row (not record!) must be unique. That is the Primary Key, which must be made up from the data (INT; GUID; UUID; etc are not data, they are additions, in the system, the user does not see them).
Of course, IDENTITY or AUTOINCREMENT are fine for prototypes and trials, they are not permitted in Production.
There are many differences between Relational databases and the pre-relation, 1960's Record Filing Systems that the "theoreticians" use. Such primitive systems use physical pointers, such as Record ID (INT; GUID; UUID; etc). If I had to declare just one, the fundamental difference is:
whereas the RFS is physical, the Relational Model is Logical
therefore, whereas in the RFS physical records are referenced by their physical pointer, in the RDb logical rows (nor records!) are referenced by their logical Key
The relationship is established as follows:
ALTER TABLE child_table
ADD CONSTRAINT constraint_name
FOREIGN KEY ( foreign_key_column_list )
REFERENCES parent_table ( primary_key_column_list )
Beware, some "theoreticians", and some newbies, do not understand SQL. If I tell you that Sally is Fred's daughter, from the single Fact you will know that Fred is Sally's father. There is no need for the second statement, it is obviously the first statement in reverse. Likewise in SQL, it is not stupid. There is only one relationship definition. But those darlings add a second "relationship", the above in reverse. That is
(a) totally redundant, and
(b) interferes with administration of the tables. Probably, those types are the ones that use weird and wonderful directional terms.
2 Cardinality
That is controlled firstly by implementing an Index, and secondly by additional by other means. The additional is not relevant here.
one [parent]
Each row is unique, by virtue of the Primary Key, expressed as:
ALTER TABLE table
ADD CONSTRAINT constraint_name
PRIMARY KEY ( column_list )
one [parent] to many [children]
Because each parent row is unique, we know that the reference [to the parent] in the child will reference just one row
ALTER TABLE child_table
ADD CONSTRAINT constraint_name
FOREIGN KEY ( foreign_key_column_list ) -- local child
REFERENCES parent_table ( primary_key_column_list ) -- referenced parent
Example
All my data models are rendered in IDEF1X, the Standard for modelling Relational databases since 1993. Refer to IDEF1X Introduction,.
ALTER TABLE Customer
ADD CONSTRAINT Customer_pk
PRIMARY KEY ( CustomerCode )
ALTER TABLE OrderSale
ADD CONSTRAINT OrderSale_pk
PRIMARY KEY ( CustomerCode, OrderSaleNo )
ALTER TABLE Order
ADD CONSTRAINT Customer_Issues_Orders_fk
FOREIGN KEY ( CustomerCode ) -- local child
REFERENCES Customer ( CustomerCode ) -- referenced parent
many to one
There is no such thing. It is simply reading a one-to-many relationship in reverse, and doing so without understanding. In the example, reading the data model explicitly, or translating it to text:
Each Customer issues 0-to-n OrderSales
the reverse is (refer again to the one-to-many):
Each OrderSale is issued by 1 Customer
Again, beware, newbies may implement a duplicate relationship, that will (a) confuse you, and (b) stuff things up royally.
many to many
We have been using diagrammatic modelling tools since the early 1980's. Even IDEF1X was available for modelling long before it was elevated to a NIST Standard. Modelling is an iterative process: whereas redrawing is very cheap, re-implementing SQL is expensive. We start at the Logical level with no concern for the physical (tables, platform specifics), with only entities, progress to logical Keys, Normalising as we go. Finally, still at the logical level, we would finalise each table, and check that the datatypes are correctly set.
If and when the logical model is (a) stable, and (b) signed off, then we progress to the Physical: creating the datatypes; tables; foreign keys; etc. It is a simple matter of translating the data model to SQL DDL. If you use a modelling tool, that is one click, and the tool does it for you.
The point is, there is progression, and a distinction between the Logical and Physical levels.
At the physical level, as can be understood from the fact that there is one and only one type of relationship in SQL, there is no such thing as a many-to-many relationship. Notice that it can't be expressed even in text form, in a single statement, we need two statements.
Such a relationship exists only at the logical modelling level: when we determine that there is such a relationship between two Facts (rows in a table), we draw it.
At the point when the data model is stable, and we move from teh Logical to the Physical, the n-to-n relationship is translated into an Associative Table and a relationship to each parent.
Refer to this unrelated document for an Example
Notice the many-to-many relationship Favours in the Logical Requirement
Notice the translation to an Associative table and tw relationships in Implementation (Right side only)
Each User favours 0-to-n ProductPreferences
Each Product is favoured in 0-to-n ProductPreferences
Now notice this sagely: that Implementation model can be read Logically:
Each User favours 0-to-n Products (via ProductPreference)
Each Product is favoured by 0-to-n Users (via ProductPreference)
Additionally, you might find this document helpful (section 1 Implementation: Relationship only).
II Your Question
Now we can deal with your question.
1 The Obstacle
Your quandary is due to:
not progressing through the formal stages, due to lack of education in the subject matter (hopefully mitigated by the above explanations)
having an idea at the Logical level ... but not formally
of the views required in the app, as opposed to the perceiving the data independent of the app
diving into the Physical tables ... with nothing in-between
not asking specific questions, due either to shyness or inability to identify the particular point that you do not understand
and thus you are stuck, as per your original post.
2 The Quandary
Your quandary is:
you have this at the logical level (Data model, Entity-Relationship level):
and of course, your CREATE TABLE commands at the physical level.
I hope my explanations above are enough to understand the great gap in what you have:
the logical vs the physical
that the physical is far too premature
that we need at least some data modelling (not formal, not possible in this medium) to work things out.
The Logical data model is simply not progressed enough, let alone resolved, in order to create stable tables, let alone correct ones.
3 Journey Progressed
Let's take your Journey thingamajig first. What is a Journey ?
It is definitely not an Independent thing. We do not go walking in the heath and heather after the dew; nor the quietened beach at sunset, and suddenly, out of nowhere ... find a Journey, sitting there, all by itself. No. It can't stand up.
A Journey is Dependent (at least) on a starting and finishing point.
And what are those points exactly ? Railway stations.
Railway stations are Independent, they do stand alone.
And then a Journey is Dependent on a Railway station. In two separate relationships: start; end.
Predicate
I have given some of the Predicates, those relevant right now, so that they are explicit, so that you can check them carefully.
All the Predicates can be read directly from the model.
In the normal case, you have to read them from the diagram (it is rich with specific detail), and check that it is correct
that provides a valuable feedback loop:
modelling --> Predicate --> check --> more modelling.
4 Traveller Progressed
Now for your Traveller thingee. What is a Traveller ?
A Traveller is a person who has travelled on at least 1 journey
Therefore Journey is Dependent on Person
Person is Independent, it can stand alone
5 Journey Resolved
Now we can finalise Journey.
5 Requirement
Now we have a decent chance of answering your Question.
I have chosen Relational Keys that throw themselves at us, no thinking necessary.
What makes a Journey unique is ( NationalID, StationStart, DateTimeStart )
not ( NationalID, StationStart ). Anything more would be superfluous.
Person needs an additional Key, called an Alternate Key, on ( NameFamily, Name ). This prevents dupes on those values.
RoleName
In the first instance, the column name for a PK in used unchanged wherever it is an FK
Except:
to make it even more meaningful, eg.TravellerID, or
to differentiate, when there is more than one FK to the same parent, eg. StationStart, StationEnd.
6 Traveller ???
So what exactly is Traveller??? (the concept in your mind, it is not in the Requirement) ?
One possibility is:
a Person who travels on a Journey is a Traveller.
That is already available above, in the single Person sense.
But there is more. I get the idea that it is a group of people who took a journey together. But that too, is available from the above:
SELECT *
FROM Journey
WHERE (condition...)
GROUP BY StationStart, DateTimeStart, StationEnd
But that will give you the whole train, not a group of people who have an intended common purpose.
What I can figure out is, that you mean a group of people who have some common purpose, such as taking a trip together. That marks an intent, before the fact of the Journey. It could be a loose Group, or and Excursion, etc. Something smaller than a train-load.
I will give you two options. It is for you to contemplate them, and to specify (if it is long, edit your Question; if it is short, post a Comment).
7 Group Option
This is a simple structure, for groups that travel together. This assumes that (because it is group travel) tickets for the Journey are purchased in a block, for all the Members of the Group, and we don't track individual Person purchases.
8 Excursion Option
An excursion is one outing for the group, with different members each outing. This assumes that the Journey for each Person is tracked (booked personally, at different times).
The Fact that each Member has reserved their Journey (or not) is simply a matter of joining Excursion::Member::Journey.
Which is eminently possible due to the Relational Keys (impossible in an RFS). Refer to this Example. Please ask if you need code.
The Identifier for a Group (above) and an Excursion (below) is quite different:
I have set up Group to be a somewhat permanent affair, with a home, and an assumption that they go on several outings together. The groups you have given (in your Journeys.Travellers) would be three different groups, due to the membership.
Excursion is a single event, the group is the list of Passengers.
MemberID and PassengerID are RoleNames for NationalID, that is, the Role the Person plays in the subject table.
It also allows Journeys that a Person takes alone (without an Excursion) to be tracked.
Please feel free to ask specific questions. Or else update your original post.
Firstly understand what each relationships are, I am explaining very few basics which are widely used.
One to One
A One-to-One relationship means that you have two tables that have a relationship, but that relationship only exists in such a way that any given row from Table A can have at most one matching row in Table B.
Ex: A Student has unique rollnumber to unique student which means one student can have only one rollnumber
Many to Many
A good design for a Many-to-Many relationship makes use of something called a join table. The term join table is just a fancy way of describing a third SQL table that only holds primary keys.
Ex- Many Students can have many subjects.
One to Many
a one-to-many relationship is a type of cardinality that refers to the relationship between two entities A and B in which an element of A may be linked to many elements of B, but a member of B is linked to only one element of A.
For instance, think of A as books, and B as pages. A book can have many pages, but a page can only be in one book.
While in your case Travelers column make it as foreign key,the primary key of Traveler table.
Reason: One Traveller can have many journeys. So here relationship is One to Many
As you have a n To n relations. You need to create an intermediate table.
In this case you will have To create a unique id to the journey table to identify the row easily.
CREATE TABLE TRAVELERS_IN_JOURNEY (
National_of,
Journey_id
)
As a column cannot contains multiple keys, you ca also remove the Travelers column from you Journey table.
CREATE TABLE Journeys (
Journey_id INT AUTO_INCREMENT PRIMARY KEY,
Start_Station VARCHAR(50) NOT NULL,
End_Station VARCHAR(50) NOT NULL,
Start_Time VARCHAR(50) NOT NULL,
End_Time VARCHAR(50) NOT NULL
)
This question has been already asked but I've not found a "1 voice answer".
Is it better to do :
1 big table with :
user_id | attribute_1 | attribute_2 | attribute_3 | attribute_4
or 4 smal tables with :
user_id | attribute_1
user_id | attribute_2
user_id | attribute_3
user_id | attribute_4
1 big table or many small tables ? Each user can only have 1 value for attribute_X. We have a lot of data to save (100 millions users). We are using innoDB. Performance are really important for us (10 000 queries / s).
Thanks !
François
If you adhere to the Zero, One or Many principle, whereby there is either no such thing, one of them, or an unlimited number, you would always build properly normalized tables to track things like this.
For instance, a possible schema:
CREATE TABLE user_attributes (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
attribute_name VARCHAR(255) NOT NULL,
attribute_value VARCHAR(255),
UNIQUE INDEX index_user_attributes_name(user_id, attribute_name)
);
This is the basic key-value store pattern where you can have many attributes per user.
Although the storage requirements for this is higher than a fixed-columns arrangement with the perpetually frustrating names like attribute1, the cost is small enough in the age of terabyte-sized hard-drives that it's rarely an issue.
Generally you'd create a single table for this data until insertion time becomes a problem. So long as your inserts are fast, I wouldn't worry about it. At that point you would want to consider a sharding strategy to divide this data into multiple tables with an identical schema, but only if it's required.
I would imagine that would be at the ~10-50 million rows stage, but could be higher if the amount of insert activity in this table is relatively low.
Don't forget that the best way to optimize for read activity is to use a cache: The fastest database query is the one you don't make. For that sort of thing you usually employ something like memcached to store the results of previous fetches, and you would invalidate this on a write.
As always, benchmark any proposed schema at production scale.
1 big table with :
user_id | attribute_1 | attribute_2 | attribute_3 | attribute_4
will make your management easier. Too many individual lookups otherwise, which will also complicate programming against the DB with the chance to increase application errors.
I am in the process of creating a second version of my technical wiki site and one of the things I want to improve is the database design. The problem (or so I think) is that to display each document, I need to join upwards of 15 tables. I have a bunch of lookup tables that contain descriptive data associated with each wiki entry such as programmer used, cpu, tags, peripherals, PCB layout software, difficulty level, etc.
Here is an example of the layout:
doc
--------------
id | author_id | doc_type_id .....
1 | 8 | 1
2 | 11 | 3
3 | 13 | 3
_
lookup_programmer
--------------
doc_id | programmer_id
1 | 1
1 | 3
2 | 2
_
programmer
--------------
programmer_id | programmer
1 | USBtinyISP
2 | PICkit
3 | .....
Since some doc IDs may have multiples entries for a single attribute (such as programmer), I have created the DB to compensate for this. The other 10 attributes have a similiar layout as the 2 programmer tables above. To display a single document article, approx 20 tables are joined.
I used the Sphinx Search engine for finding articles with certain characteristics. Essentially Sphinx indexes all of the data (does not store) and returns the wiki doc ID of interest based on the filters presented. If I want to find articles that use a certain programmer and then sort by date, MYSQL has to first join ALL documents with the 2 programmer tables, then filter, and finally sort the remaining by insert time. No index can help me ordering the filtered results (takes a LONG time with 150k doc IDs) since it is done in a temporary table. As you can imagine, it gets worse really quickly with the more parameters that need to be filtered.
It is because I have to rely on Sphinx to return - say all wiki entries that use a certain CPU AND programer - that lead me to believe that there is a DB smell with my current setup....
edit: Looks like I have implemented a [Entity–attribute–value model]1
I don't see anything here that suggests you've implemented EAV. Instead, it looks like you've assigned every row in every table an ID number. That's a guaranteed way to increase the number of joins, and it has nothing to do with normalization. (There is no "I've now added an id number" normal form.)
Pick one lookup table. (I'll use "programmer" in my example.) Don't build it like this.
create table programmer (
programmer_id integer primary key,
programmer varchar(20) not null,
primary key (programmer_id),
unique key (programmer)
);
Instead, build it like this.
create table programmer (
programmer varchar(20) not null,
primary key (programmer)
);
And in the tables that reference it, consider cascading updates and deletes.
create table lookup_programmer (
doc_id integer not null,
programmer varchar(20) not null,
primary key (doc_id, programmer),
foreign key (doc_id) references doc (id)
on delete cascade,
foreign key (programmer) references programmer (programmer)
on update cascade on delete cascade
);
What have you gained? You keep all the data integrity that foreign key references give you, your rows are more readable, and you've eliminated a join. Build all your "lookup" tables that way, and you eliminate one join per lookup table. (And unless you have many millions of rows, you're probably not likely to see any degradation in performance.)
My web application allows a user to define from 1 up to 30 emails (could be anything else).
Which of these options is best?
1) ...store the data inside only one column using a separator, like this:
[COLUMN emails] peter#example.com,mary#example.com,john#example.com
Structure:
emails VARCHAR(1829)
2) ...or save the data using distinct columns, like this:
[COLUMN email1] peter#example.com
[COLUMN email2] mary#example.com
[COLUMN email3] john#example.com
[...]
Structure:
email1 VARCHAR(60)
email2 VARCHAR(60)
email3 VARCHAR(60)
[...]
email30 VARCHAR(60)
Thank you in advance.
Depends on how you are going to use the data and how fixed the amount of 30 is. If it is an advantage to quickly query for the 3rd address or filter using WHERE clauses and such: use distinct fields; otherwise it might not be worth the effort of creating the columns.
Having the data in a database still has the advantage of concurrent access by several users.
Number two is the better option, without question. If you do the first one (comma separated), then it negates the advantages of using a RDBMS (you can't run an efficient query on your emails in that case, so it may as well be a flat file).
number 2 is better than number one.
However, you should consider another option of getting a normalized structure where you have a separate emails table with a foreign key to your user record. This would allow you to define an index if you wanted to search by email to find a user and place a constraint ensuring no duplicate emails are registered - if you wanted to do that.
Neither one is a very good option.
Option 1 is a poor idea because it makes looking a user up by email a complex, inefficient task. You are effectively required to perform a full text search on the email field in the user record to find one email.
Option 2 is really a WORSE idea, IMO, because it makes any surrounding code a huge pain to write. Suppose, again, that you need to look up all users who have a value X. You now need to enumerate 30 columns and check each one to see if that value exists. Painful!
Storing data in this manner -- 1-or-more of some element of data -- is very common in database design, and as Adam has previously mentioned, is best solved in MOST cases by using a normalized data structure.
A correct table structure, written in MySQL since this was tagged as such, might look like:
Users table:
CREATE TABLE user (
user_id int auto_increment,
...
PRIMARY KEY (user_id)
);
Emails table:
CREATE TABLE user_email (
user_id int,
email char(60) not null default '',
FOREIGN KEY (user_id) REFERENCES user (user_id) ON DELETE CASCADE
);
The FOREIGN KEY statement is optional -- the design will work without it, however, that line causes the database to force the relationship. For example, if you attempt to insert a record into user_email with a user_id of 10, there MUST be a corresponding user record with a user_id of 10, or the query will fail. The ON DELETE CASCADE tells the database that if you delete a record from the user table, all user_email records associated with it will also be deleted (you may or may not want this behavior).
This design of course also means that you need to perform a join when you retrieve a user record. A query like this:
SELECT user.user_id, user_email.email FROM user LEFT JOIN user_email ON user.user_id = user_email.user_id WHERE <your where clause>;
Will return one row for EACH user_email address stored in the system. If you have 5 users and each user has 5 email addresses, the above query will return 25 rows.
Depending on your application, you may want to get one row per user but still have access to all the emails. In that case you might try an aggregate function like GROUP_CONCAT which will return a single row per user, with a comma-delimited list of emails belonging to that user:
SELECT user.user_id, GROUP_CONCAT(user_email.email) AS user_emails FROM user LEFT JOIN user_email ON user.user_id = user_email.user_id WHERE <your where clause> GROUP BY user.user_id;
Again, depending on your application, you may want to add an index to the email column.
Finally, there ARE some situations where you do not want a normalized database design, and a single-column design with delimited text might be more appropriate, although those situations are few and far between. For most normal applications, this type of normalized design is the way to go and will help it perform and scale better.
I want to make user group system that imitates group policy in instant messengers.
Each user can create as many as groups as they want, but they cannot have groups with duplicate names, and they can put as many friends as they want into any groups.
For example, John's friend Jen can be in 'school' group of John and 'coworker' group of John at the same time. And, it is totally independent from how Jen puts John into her group.
I'm thinking two possible ways to implement this in database user_group table.
1.
user_group (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
group_name VARCHAR(30),
UNIQUE KEY (user_id, group_name)
)
In this case, all groups owned by all users will have a unique id. So, id alone can identify which user and the name of the group.
2.
user_group (
user_id INT,
group_id INT AUTO_INCREMENT,
group_name VARCHAR(30),
PRIMARY KEY (user_id, group_id),
UNIQUE KEY (user_id, group_name)
)
In this case, group_id always starts from 0 for each user, so, there could exist many groups with same group_id s. But, pk pair (user_id, group_id) is unique in the table.
which way is better implementation and why?
what are advantages and drawbacks for each case?
EDIT:
added AUTO_INCREMENT to group_id in second scenario to insure it is auto-assigned from 0 for each user_id.
EDIT:
'better' means...
- better performance in SELECT/INSERT/UPDATE friends to the group since that will be the mostly used operations regarding the user group.
- robustness of database like which one will be more safe in terms of user size.
- popularity or general preference of either one over another.
- flexibility
- extensibility
- usability - easier to use.
Personally, I would go with the 1st approach, but it really depends on how your application is going to work. If it would ever be possible for ownership of a group to be changed, or to merge user profiles, this will be much easier to do in your 1st approach than in the 2nd. In the 2nd approach, if either of those situations ever happen, you would not only have to update your user_group table, but any dependent tables as well that have a foreign key relation to user_group. This will also be a many to many relation (there will be multiple users in a group, and a user will be a member of multiple groups), so it will require a separate joining table. In the 1st approach, this is fairly straightforward:
group_member (
group_id int,
user_id int
)
For your 2nd approach, it would require a 3rd column, which will not only be more confusing since you're now including user_id twice, but also require 33% additional storage space (this may or may not be an issue depending on how large you expect your database to be):
group_member (
owner_id int,
group_id int,
user_id int
)
Also, if you ever plan to move from MySQL to another database platform, this behavior of auto_increment may not be supported. I know in MS SQL Server, an auto_increment field (identity in MSSQL) will always be incremented, not made unique according to indexes on the table, so to get the same functionality you would have to implement it yourself.
Please define "better".
From my gut, I would pick the 2nd one.
The searchable pieces are broken down more, but that wouldn't be what I'd pick if insert/update performance is a concern.
I see no possible benefit to number 2 at all, it is more complex, more fragile (it would not work at all in SQL Server) and gains nothing. Remeber the groupId is without meaning except to identify a record uniquely, likely the user willonly see the group name not the id. So it doesn't matter if they all start from 0 or if there are gaps because a group was rolled back or deleted.