MySQL override auto_increment? - php

I'm creating a messaging system (using PHP) and want to assign an ID number to each message (aside from each actual message having an unique ID number)...however, if someone replies to a message then i want to be able to give that message the same ID as the message being replied to...then of course I can disply them by time and show them in order.
So, if i give the field an auto_increment type is that able to be overwritten?
Meaning...each new message has auto value e.g. 1, 2, 3 etc but someone replies to number 2 so it's ID needs to also 2
Or is there a better way to do this?

Absolutely nothing prevents you from assigning any arbitrary value to an AUTO_INCREMENT column. If necessary, the table counter will adjust accordingly.
However, you cannot set as AUTO_INCREMENT a column that's not unique.
Honestly, I can't understand your design. A typical messaging system would look like this:
message_id in_reply_to
========== ===========
1 NULL
2 NULL
3 1
4 NULL
5 1
6 3
7 NULL
Duplicating IDs kind of beats the purpose of using IDs.
Update #1: OMG, it seems that it can actually be done under certain circumstances:
For MyISAM tables you can specify AUTO_INCREMENT on a secondary column
in a multiple-column index. In this case, the generated value for the
AUTO_INCREMENT column is calculated as MAX(auto_increment_column) + 1
WHERE prefix=given-prefix. This is useful when you want to put data
into ordered groups.
http://dev.mysql.com/doc/refman/5.5/en/example-auto-increment.html
Update #2: For the records, I've just tested it and you can use duplicate auto-incremented IDs in InnoDB tables as well:
CREATE TABLE foo (
foo_id INT(10) NOT NULL DEFAULT '0',
bar_id INT(10) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (foo_id),
INDEX bar_id (bar_id)
)
ENGINE=InnoDB

No, auto_increment columns cannot occur multiple times.

I would keep each message ID unique - whether it is via auto increment or a uuid. Add an additional column to the message structure for thread_id - unique on creation, and then have all replies include this thread_id to link them together logically.

The way you would do this is have another column in your table called parent_id or something of the sort.
The original message would have a parent_id of NULL.
Then, when anyone posts a reply to a message, then the original ID of the message goes into the parent_id column of the new message. For instance:
id text parent_id created_at
============================================
1 'Lorem ipsum' null [time]
2 'Lorem ipsum' 1 [time]
3 'Lorem ipsum' 1 [time]
You could even go further and have the replies nested:
id text parent_id created_at
============================================
1 'Lorem ipsum' null [time]
2 'Lorem ipsum' 1 [time]
3 'Lorem ipsum' 2 [time]
In the latter case, you'd probably need some sort of recursive function to get all of the nested messages; the first way is simpler.

Best way to put unique id is; to add uniqid(rand()).

Related

Arrays in Mysql database for image path storing? [duplicate]

I have two tables in MySQL. Table Person has the following columns:
id
name
fruits
The fruits column may hold null or an array of strings like ('apple', 'orange', 'banana'), or ('strawberry'), etc. The second table is Table Fruit and has the following three columns:
fruit_name
color
price
apple
red
2
orange
orange
3
-----------
--------
------
So how should I design the fruits column in the first table so that it can hold array of strings that take values from the fruit_name column in the second table? Since there is no array data type in MySQL, how should I do it?
The proper way to do this is to use multiple tables and JOIN them in your queries.
For example:
CREATE TABLE person (
`id` INT NOT NULL PRIMARY KEY,
`name` VARCHAR(50)
);
CREATE TABLE fruits (
`fruit_name` VARCHAR(20) NOT NULL PRIMARY KEY,
`color` VARCHAR(20),
`price` INT
);
CREATE TABLE person_fruit (
`person_id` INT NOT NULL,
`fruit_name` VARCHAR(20) NOT NULL,
PRIMARY KEY(`person_id`, `fruit_name`)
);
The person_fruit table contains one row for each fruit a person is associated with and effectively links the person and fruits tables together, I.E.
1 | "banana"
1 | "apple"
1 | "orange"
2 | "straberry"
2 | "banana"
2 | "apple"
When you want to retrieve a person and all of their fruit you can do something like this:
SELECT p.*, f.*
FROM person p
INNER JOIN person_fruit pf
ON pf.person_id = p.id
INNER JOIN fruits f
ON f.fruit_name = pf.fruit_name
The reason that there are no arrays in SQL, is because most people don't really need it. Relational databases (SQL is exactly that) work using relations, and most of the time, it is best if you assign one row of a table to each "bit of information". For example, where you may think "I'd like a list of stuff here", instead make a new table, linking the row in one table with the row in another table.[1] That way, you can represent M:N relationships. Another advantage is that those links will not clutter the row containing the linked item. And the database can index those rows. Arrays typically aren't indexed.
If you don't need relational databases, you can use e.g. a key-value store.
Read about database normalization, please. The golden rule is "[Every] non-key [attribute] must provide a fact about the key, the whole key, and nothing but the key.". An array does too much. It has multiple facts and it stores the order (which is not related to the relation itself). And the performance is poor (see above).
Imagine that you have a person table and you have a table with phone calls by people. Now you could make each person row have a list of his phone calls. But every person has many other relationships to many other things. Does that mean my person table should contain an array for every single thing he is connected to? No, that is not an attribute of the person itself.
[1]: It is okay if the linking table only has two columns (the primary keys from each table)! If the relationship itself has additional attributes though, they should be represented in this table as columns.
MySQL 5.7 now provides a JSON data type. This new datatype provides a convenient new way to store complex data: lists, dictionaries, etc.
That said, arrays don't map well databases which is why object-relational maps can be quite complex. Historically people have stored lists/arrays in MySQL by creating a table that describes them and adding each value as its own record. The table may have only 2 or 3 columns, or it may contain many more. How you store this type of data really depends on characteristics of the data.
For example, does the list contain a static or dynamic number of entries? Will the list stay small, or is it expected to grow to millions of records? Will there be lots of reads on this table? Lots of writes? Lots of updates? These are all factors that need to be considered when deciding how to store collections of data.
Also, Key/Value data stores, Document stores such as Cassandra, MongoDB, Redis etc provide a good solution as well. Just be aware of where the data is actually being stored (if its being stored on disk or in memory). Not all of your data needs to be in the same database. Some data does not map well to a relational database and you may have reasons for storing it elsewhere, or you may want to use an in-memory key:value database as a hot-cache for data stored on disk somewhere or as an ephemeral storage for things like sessions.
A sidenote to consider, you can store arrays in Postgres.
In MySQL, use the JSON type.
Contra the answers above, the SQL standard has included array types for almost twenty years; they are useful, even if MySQL has not implemented them.
In your example, however, you'll likely want to create three tables: person and fruit, then person_fruit to join them.
DROP TABLE IF EXISTS person_fruit;
DROP TABLE IF EXISTS person;
DROP TABLE IF EXISTS fruit;
CREATE TABLE person (
person_id INT NOT NULL AUTO_INCREMENT,
person_name VARCHAR(1000) NOT NULL,
PRIMARY KEY (person_id)
);
CREATE TABLE fruit (
fruit_id INT NOT NULL AUTO_INCREMENT,
fruit_name VARCHAR(1000) NOT NULL,
fruit_color VARCHAR(1000) NOT NULL,
fruit_price INT NOT NULL,
PRIMARY KEY (fruit_id)
);
CREATE TABLE person_fruit (
pf_id INT NOT NULL AUTO_INCREMENT,
pf_person INT NOT NULL,
pf_fruit INT NOT NULL,
PRIMARY KEY (pf_id),
FOREIGN KEY (pf_person) REFERENCES person (person_id),
FOREIGN KEY (pf_fruit) REFERENCES fruit (fruit_id)
);
INSERT INTO person (person_name)
VALUES
('John'),
('Mary'),
('John'); -- again
INSERT INTO fruit (fruit_name, fruit_color, fruit_price)
VALUES
('apple', 'red', 1),
('orange', 'orange', 2),
('pineapple', 'yellow', 3);
INSERT INTO person_fruit (pf_person, pf_fruit)
VALUES
(1, 1),
(1, 2),
(2, 2),
(2, 3),
(3, 1),
(3, 2),
(3, 3);
If you wish to associate the person with an array of fruits, you can do so with a view:
DROP VIEW IF EXISTS person_fruit_summary;
CREATE VIEW person_fruit_summary AS
SELECT
person_id AS pfs_person_id,
max(person_name) AS pfs_person_name,
cast(concat('[', group_concat(json_quote(fruit_name) ORDER BY fruit_name SEPARATOR ','), ']') as json) AS pfs_fruit_name_array
FROM
person
INNER JOIN person_fruit
ON person.person_id = person_fruit.pf_person
INNER JOIN fruit
ON person_fruit.pf_fruit = fruit.fruit_id
GROUP BY
person_id;
The view shows the following data:
+---------------+-----------------+----------------------------------+
| pfs_person_id | pfs_person_name | pfs_fruit_name_array |
+---------------+-----------------+----------------------------------+
| 1 | John | ["apple", "orange"] |
| 2 | Mary | ["orange", "pineapple"] |
| 3 | John | ["apple", "orange", "pineapple"] |
+---------------+-----------------+----------------------------------+
In 5.7.22, you'll want to use JSON_ARRAYAGG, rather than hack the array together from a string.
Use database field type BLOB to store arrays.
Ref: http://us.php.net/manual/en/function.serialize.php
Return Values
Returns a string containing a byte-stream representation of value that
can be stored anywhere.
Note that this is a binary string which may include null bytes, and
needs to be stored and handled as such. For example, serialize()
output should generally be stored in a BLOB field in a database,
rather than a CHAR or TEXT field.
you can store your array using group_Concat like that
INSERT into Table1 (fruits) (SELECT GROUP_CONCAT(fruit_name) from table2)
WHERE ..... //your clause here
HERE an example in fiddle

Store in database description with numeric value

I have this kind of text as "description" but some values are numeric and must be changed based on a ratio. I was wondering on how to properly store that in database.
"- Add 49 things in my 7 bags"
My initial idea was to do that :
+-------+------+---------------+------+---------+------------+-----------+
| part1 | num1 | part2 | num2 | part3 | rationum1 | rationum2 |
+-------+------+---------------+------+---------+------------+-----------+
| - Add | 49 | things in my | 7 | bags | 1.3 | 1.2 |
+-------+------+---------------+------+---------+------------+-----------+
It seems however very inefficient. Plus, I want to add a tooltip on some things. For example, "-Add" must have a tooltip linked but I don't know how to apply a property on only one part of the table.
Any advices would be welcome!
EDIT : I'm using PHP to fetch data as JSON, and then I'm using JavaScript (React) for the display.
There is nothing wrong with your proposed table layout. It's not inefficient either. MySql is built for this. It can handle millions of rows of this kind of thing without breaking a sweat.
Do add an autoincrementing id value to each row, to use as a primary key. You may wish to consider adding a timestamp column too.
Define your num1 and num2 columns as int, or if you need fractional values, as double. (Javascript treats all numbers as double).
Define your fractional columns as double.
Define your textual columns as varchar(250) or some such thing, and add a textual column for your tooltip's text.
And, you're done.
But when I look at your example Add 49 things in my 7 bags I see more meaning than just a phrase.
a verb: Add.
a source_count: 49
a source description: things.
a preposition: in
a possessive: my
a target_count: 7
a target_description: bags
Does your system also need to say Steal 5 grenades from Joe's 2 ammo cases or some such thing (I'm assuming you are making some kind of game)?
If so, you may want a more elaborate set of table layouts taking into account the parts of the phrase. Then your query can use appropriate JOIN operations.
Perhaps normalize it.
F.e. put the descriptions with placeholders in another table, together with the tooltip.
Then put a foreign key in the table with the items.
Example code:
DROP TABLE IF EXISTS tst_stuff;
DROP TABLE IF EXISTS tst_stufftodo;
CREATE TABLE tst_stufftodo (id int primary key auto_increment, description varchar(100), tooltip varchar(1000));
CREATE TABLE tst_stuff (id int primary key auto_increment, name varchar(100), num1 int not null default 0, num2 int not null default 0, rationum1 decimal(4,1) not null default 0, rationum2 decimal(4,1) not null default 0,
std_id int,
FOREIGN KEY (std_id) REFERENCES stufftodo(id)
);
INSERT INTO tst_stufftodo (description, tooltip)
VALUES
('Add &num1& things in my &num2& &name&', 'Add the stuff');
INSERT INTO tst_stuff (name, num1, num2, rationum1, rationum2, std_id) VALUES
('bags', 49, 7, 1.2, 1.3, 1),
('socks', 1000000, 2, 0.5, 0.6, 1);
select s.id, replace(replace(replace(std.description,'&name&',s.name), '&num1&',s.num1), '&num2&',s.num2) as description
from tst_stuff s
join tst_stufftodo std on std.id = s.std_id;
Result:
id description
1 Add 49 things in my 7 bags
2 Add 1000000 things in my 2 socks
But it's probably better to do the replacement of the placeholders in the PHP presentation layer.

Multiple foreign keys in one column?

I am trying to create a database for a homepage for dogs.
I use mysql and php. Every dog has a dog_id (PRIMARY KEY), which it can be identified by.
Now, I would like to be able to link a dog to its siblings. The problem here is that the number of siblings might be anywhere between 0 to let's say 25. Therefore, I am unsure how to do this.
Here is my 1st attempt
TABLE `dogs` (
`dog_id` INT(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'auto incremented PRIMARY KEY',
`dog_sibling1` INT(11) COLLATE utf8_unicode_ci NULL COMMENT 'FOREIGN KEY dog_id of the dog\'s 1st sibling',
`dog_sibling2` INT(11) COLLATE utf8_unicode_ci NULL COMMENT 'FOREIGN KEY dog_id of the dog\'s 2nd sibling',
and so on....
);
The problem I see here is that worst case I end up with 25 columns of NULL and most of the time with a lot of NULL columns.
My 2nd attempt
TABLE `dogs` (
`dog_id` INT(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'auto incremented PRIMARY KEY',
`dog_sibling_ids` INT(11) COLLATE utf8_unicode_ci NULL COMMENT 'FOREIGN KEY dog_ids of the dog\'s siblings',
);
Like this I would just have one column but I do not know if it is even possible to save an array of foreign keys in one column? And I also would not be sure how to deal with this in php then.
Is there a better way to solve this problem?
Your best bet would likely be to have a second table that 'groups' the siblings together.
CREATE TABLE dog_siblings (
sibling_group_id INTEGER,
dog_id integer
);
This way, if dog_ids 5-9 are all related, they just need to share a common sibling_group_id in the new table, and you can find all dogs related using a query similar to the following
SELECT ds2.dog_id
FROM dogs d
INNER JOIN dog_siblings ds
ON d.dog_id = ds.dog_id AND d.dog_id = 1
INNER JOIN dog_siblings ds2
ON ds.sibling_group_id = ds2.sibling_group_id
And that'll give you all dogs who are siblings of the dog with id 1 (including dog 1)
(edit: stupid inability to comment, the comments on the op appeared while i was crafting this, honest)
I would make a solution similar to the one you suggest in your question: create one table called siblings and reference it from dogs table:
CREATE TABLE `siblings` (
`siblings_id` INTEGER PRIMARY KEY
);
CREATE TABLE `dogs` (
`dog_id` INTEGER PRIMARY KEY,
`dog_sibling_id` INTEGER REFERENCES `siblings`
);
I wouldn't put any reference from the siblings table to the dogs table as it is clearly a one-to-many relationship (one siblings group can have many dogs but a dog can only belong to one siblings group). What about the dogs that have no siblings? You can either create a siblings group for those dogs also or just leave dog_sibling_id as NULL.
The previous works if you want to know the siblings of one litter (group of baby dogs born at the same time). If you need to track the siblings between different litters add the mother and father references:
CREATE TABLE `litters` (
`litter_id` INTEGER PRIMARY KEY,
`mother_id` INTEGER REFERENCES `dogs`,
`father_id` INTEGER REFERENCES `dogs`
);
CREATE TABLE `dogs` (
`dog_id` INTEGER PRIMARY KEY,
`dog_litter_id` INTEGER REFERENCES `litters`
);
You could also just add the mother and father columns to the dogs table and drop the litters table (in which case the litter specific information would be lost).

Mysql Auto-incrementing int of similar data

So this is more of a query of how one might go about doing this, I'm new to MySQL/PHP coding when it comes to more than the basics so I'm just wondering how one might set up an auto incrementing int where if two lastnames were the same it would count them.
I was unable to find anything on it while searching online but an example would be:
in the database we have 5 users
1. james smith 1
2. terry smith 2
3. john smith 3
4. jerry fields 1
5. tom straus 1
When these users register I need an int to be created that john smith was the 3rd person to have the same last name of smith while jerry fields is the first person with the last name fields etc. How might one do that?
The form I made is one that registers a user using a jquery/php ajax method but
I would like to add something similar to this so that it combines that number with their names to make a specific user ID.
As documented under Using AUTO_INCREMENT:
For MyISAM and BDB tables you can specify AUTO_INCREMENT on a secondary column in a multiple-column index. In this case, the generated value for the AUTO_INCREMENT column is calculated as MAX(auto_increment_column) + 1 WHERE prefix=given-prefix. This is useful when you want to put data into ordered groups.
Therefore, you could do:
CREATE TABLE my_table (
firstname VARCHAR(31) NOT NULL,
lastname VARCHAR(31) NOT NULL,
counter BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (lastname, counter)
) Engine=MyISAM;
INSERT INTO my_table
(firstname, lastname)
VALUES
('james', 'smith' ),
('terry', 'smith' ),
('john' , 'smith' ),
('jerry', 'fields'),
('tom' , 'straus')
;
See it on sqlfiddle.

searching mySQL by first char(s) in fields with php

I have a database that's setup in the following way
id coach team event status
14 test 8,7,12,13,15 4,1,2,14,4 2
13 test 8,12,13,14,15,16 1,2,8,16,16 3
What i need to do, is search for rows where the first number in the "event" column matches the query. They are separated by commas, but it can be 1 or 2 or 3 digits. Im sure this is possible, just not sure how or where to begin.
Have you considered normalizing your database? Isn't it a pain to work with a database, in which a field may contain an arbitrary number of arbitrarily formatted values? As a side effect (haha), it will solve the problem you've described in your question.
Example database schema:
create table whatever (
id int not null auto_increment primary key,
coach varchar(64),
status int
)
create table teams (
id int not null auto_increment primary key,
name varchar(255)
)
create table events (
id int not null auto_increment primary key,
name varchar(255)
)
create table whatever_teams (
id int not null auto_increment primary key,
whatever_id int,
team_id int
)
create table whatever_events (
id int not null auto_increment primary key,
whatever_id int,
event_id int
)
I want to apologize in advance for the obvious lack of sql-injection-enabled code, that can be always found in the questions and answers under the tags "php" and "mysql".
This will select all rows where the first number in event is 1:
SELECT * FROM `tableName` WHERE event LIKE '1,%';
You'd be better of by changing your database scheme. Storing fields with lists of ids is not very handy.
Make extra tabels to make the links.
For example:
coach_team
id coach_id team_id
1 14 7
2 14 8
3 14 12
4 14 13
Than you can use queries like:
SELECT * FROM table_name WHERE id in
(SELECT coach_id FROM coach_team WHERE team_id = 1)
(This of course also applies to events.
Extra information:
http://en.wikipedia.org/wiki/Database_normalization
You could use SUBSTRING_INDEX to get the first value, something like this:
SELECT * FROM table_name WHERE SUBSTRING_INDEX( event, ',', 1 ) = 'value'
With this approach you can use a prepared statement with a placeholder for the search value. Also works fine if there is just one number in the event column, i.e. no commas present to match against.
$sql = "SELECT * FROM table_name WHERE event LIKE '" . $query . "',%'";
I strongly recommend you to change your database schema because from my experience, sooner or later, you have to change it to serve all your needs in the future. SHould do it now be4 too late

Categories