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.
Related
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
I want to create a automaprimary key(auto-increment) which is start with JTM0605160001. Here
JTM: would be constant
060516: date in ddmmyy format (06-day 05-month 16-year
0001 would be iterate
So when the user want to input the data, there is already have a number series in that form. so it will be easy for me to check their data only based on the form's series number. so can you help me with some tutorial and coding?
I am using XAMPP control panel and Dreamweaver cs6.
I want serial number like this:
JTM0605160001
JTM0605160002
JTM0705160003
...
so the form will be like this:
no series : JTM0605160001 (automatic provided when user want to fill the form)
name : sally (user will fill this form)
age : 34 (user will fill this form)
table schema:
CREATE TABLE combinedKey (
id INT(4) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT,
prefix VARCHAR(3) NOT NULL,
datePrefix VARCHAR(6) NOT NULL,
PRIMARY KEY (id, prefix, datePrefix),
age INT(3),
name VARCHAR(30)
);
So, we combine three columns to a primary key consisting of your auto-incrementing id (note the ZEROFILL for the leading zeros), your constant prefix and the date prefix (your format is not a default date format, so we format it on INSERT, see below), based on INSERT-date.
An insert could look like this:
INSERT INTO combinedKey (`prefix`,`datePrefix`,`age`,`name`) VALUES ('JTM',DATE_FORMAT(NOW(),'%d%m%y'),34,'Sally' );
A select statement with output of series number
SELECT CONCAT(`prefix`,`datePrefix`,`id`) AS series, `name`, `age` FROM combinedKey;
+---------------+-------+------+
| series | name | age |
+---------------+-------+------+
| JTM0106160001 | Sally | 34 |
+---------------+-------+------+
1 row in set (0.00 sec)
If you need the new series number before inserting name, age, etc. you could insert with dummy data/null, store the inserted row/new series number, and UPDATE afterwards.
i am building a website and i have a little knowledge of php and sql.
I have many problems when it comes to many to many relationship in a database.
I have product that are specifical for every material e.g. there can't be the same product for 2 material
material are leather, simil-leather, cloth, PVC
field of use are the field which that material can be used: sport, leisure, work
The problem is that material can be used in many field and a field have associated many material, so it's N:M
a product can be used in many field and a field can be used for many product so it's too N:M
For example, leather can be used in work, sport, cloth in work sport and office
product can be used in some or all field of application and vice versa.
to achieve this is better architecture A or B?
(product have always the same application field of materials, can't be a product belonging to a materials that has an application field materials doesn't have)
A) http://i60.tinypic.com/27zdk4k.jpg
B) http://i57.tinypic.com/2mhc03o.jpg
If i understand correctly, many to many relationship work with a "mid"table between the two.
So, when it comes to insert data and values in my database, what i have is:
MATERIAL
1 leather
2 cloth
MAtERIAL_APPL_FIELD
1 1
1 2
2 2
APPLICATION_FIELD
1 nautic
2 leisure
In this way, leather has 2 application field. But how i can fill the mid table in a smart way?
Also
When i want to cancel something, which is the better architecture?
and i should cancel from all table?
Here's another good solution:
create table materials (material_id tinyiny unsigned not null auto_increment primary key, material varchar(60));
create table uses (use_id tinyiny unsigned not null auto_increment primary key, use varchar(60));
create table applications (application_id int unsigned not null auto_increment primary key, material tinyint unsigned, use tinyint unsigned, active tinyint unsigned default = 1);
insert into applications (material, use) values (?,?);
The question marks would represent the ID of the material and the ID of the use.
It might be better not to actually delete rows from your table. Instead you may wish to inactivate them. Try this:
update applications set active = 0 where application_id = ?;
update applications set active = 0 where material = 1 and use = 2;
update applications set active = 0 where material = (select material_id from materials where material = 'leather') and use = (select use_id from uses where use = 'sport');
If I understand your question correctly, the solution below should work. Keep in mind that their may be multiple correct solutions to your problem, and that I did not actually test this code so there may be typos.
create table applications (application_id int unsigned not null auto_increment primary key, material enum('leather', 'semi-leather', 'cloth', 'PVC'), use enum('sport','leisure','work'));
insert into applications (material,use) values ('leather','work');
insert into applications (material,use) values ('leather','leisure');
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.
I need an elegant way to store dynamic arrays (basically spreadsheets without all the functionality) of various sizes (both x and y), mostly being used as ENUMs, lists, lookup data, price sheets, that sort of thing. Multi-lingual would be a great bonus. Speed of the essence.
Here's an example of a typical "sheet" ;
| 1 | 2 | 3 | 4
-------------------------------
model A | 2$ | 5$ | 8$ | 10$
model B | 3$ | 6$ | 9$ | 12$
model C | 4$ | 8$ | 10$ | 13$
So, to get info, I would do ;
$price = this_thing_im_after ( '3', 'model B' ) ;
echo $price ; // Prints '9$'
I'm in the PHP5 and Zend Framework world, but thoughts on design and SQL is just as dandy, even suggestions on and from the outside world, libs, extensions, etc. as I don't want to reinvent too much of the wheel. I need the backend stuff the most, and I'll write a GUI for dynamic sheets later. Thoughts, ideas, pointers?
Just an edit to point out that I'd prefer not to serialize and blob the data as I would like to query the indeces and sheets, perhaps even the data (or type for those who support such, now that would be awsome!) if I'm in a crazy mood. But again, this is not a breaker for me; if someone has a nice library or class for serializing in and out quickly out of a database with some simple querying, I'm all happy.
Other than serializing the whole thing into a blob field, you probably end up with a key/value table where your key is the row and col fields:
CREATE TABLE sheet (
sheet_id int not null,
name varchar(32),
rows int, -- stores max dimension if needed
cols int, -- stores max dimension if needed
primary key (sheet_id)
);
CREATE TABLE cells (
cell_id identity, -- auto inc field for ease of updates
sheet_id int not null, -- foreign key to sheet table
row int not null,
col int not null,
value smalltext, -- or a big varchar depending on need
primary key (cell_id), -- for updates
unique index (sheet_id, row, col), -- for lookup
index (value) -- for search
);
CREATE TABLE row_labels (
sheet_id int not null,
row int not null,
label varchar(32),
primary key (sheet_id, row)
);
CREATE TABLE col_labels (
sheet_id int not null,
col int not null,
label varchar(32),
primary key (sheet_id, col)
);
This allows you to slice the data nicely:
// Slice [4:20][3:5]
SELECT row, col, value FROM cells
WHERE sheet_id = :sheet
AND row BETWEEN 4 AND 20
AND col BETWEEN 3 AND 5
ORDER BY row, col
while ($A = fetch()) {
$cell[$A['row'][$A['col']] = $A['value']; // or unserialize($A['value']);
}
Is there any need to fetch only part of a spreadsheet, or query by contained data?
If you just want to store and retrieve the whole thing, I would just use an unambiguous textual representation of the array (e.g. serialize()) and store it as TEXT.