I'm currently developing a small invoicing system for a company that has many branches. I was wondering how can I generate different invoice autonumbers based on each branch.
Here is my table structure for table invoice_header:
id | number | cust | grand_total
Should I create a different table for each branch? For example invoice_header_1, invoice_header_2 , invoice_header_3.
It's nicer and more workable to make a table with the branches types in it.
And then you add only 1 column to the table invoice_header and there you give in the id of the exact branch from the branch table.
This way you can connect these two and keep it nice and clean. And easier to edit.
So:
table invoice_header
id | number | cust | grand_total | branch_id
table branch
id | branch_name
If you use ENGINE=MyISAM, MySQL will do this for you:
CREATE TABLE invoice_header (
branch_id INT UNSIGNED NOT NULL,
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
number INT UNSIGNED NOT NULL DEFAULT 1,
cust INT UNSIGNED NOT NULL,
grand_total DECIMAL(10,2) NOT NULL DEFAULT 0,
PRIMARY KEY(branch_id, id)
) ENGINE=MyISAM;
MySQL will generate an unique value for the id column, starting at 1, but For each different branch_id. This only works if the engine is MyISAM.
Search for 'multiple-column index' in http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html for more information.
Related
I'm facing some performance issues with MySql. The query to select the comments related to the specific url id took about 1.5 ~ 2 seconds to complete.
Comments Table
CREATE TABLE `comments` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`url_id` INT UNSIGNED NOT NULL,
`user_id` INT UNSIGNED NOT NULL,
`published` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`votes_up` SMALLINT UNSIGNED NOT NULL DEFAULT 0,
`votes_down` SMALLINT UNSIGNED NULL DEFAULT 0,
`text` TEXT,
PRIMARY KEY (id),
INDEX (url_id),
INDEX (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I have inserted 100.000 comments, and executed this query: SELECT * FROM comments WHERE url_id = 33 ORDER BY published ASC LIMIT 0,5.
Is this normal? A simple query taking almost 2 seconds to complete? Should I create a separate table just for the comment's text?
Youtube, Facebook and so on has millions (or billions) of comments, how they get the comments for that object (video, post, etc) so fast?
To resume my question:
I stop worrying about performance and stick with this and when the website reaches certain amount of user activity, I start worrying about this.
If I need to worry about this, what's wrong to my table structure? What I need to change to reduce the completion time of that query?
Update
The explain output:
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------------+
| 1 | SIMPLE | comments | ref | url_id | url_id | 4 | const | 549 | Using where; Using filesort |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------------+
The problem here is that mysql uses only one index per table. That's why your index on published wasn't used. Your explain shows that it's using the index to identify what rows to return, that leaves the RDBMS unable to use an index for the sorting.
What you should do is to create a composite index on (user_id,published)
I am working on a project where I need to store an unknown length list on names in an sql db. It will be a list of students in a class, which will be different for each class. I also need to be able to search for them (in PHP/SQL) by student name to see all the classes a student attended. I was thinking about storing the class as a row and the students as an array, but I can't figure out an sql to query the arrays in the db. Am I heading the right direction? Maybe creating a new db row for each student for each class? Maybe making the students rows and update an array of classes for each? I will probably using AJAX later to retrieve the info. Thanks!
Why don't you create two tables to store the information about existing Classes and Students and a relation-table Participant that stores the information about which student goes to which class?
Something along the lines of:
CREATE TABLE Classes (
id int NOT NULL,
description varchar(200),
PRIMARY KEY (id)
)
CREATE TABLE Studentes (
id int NOT NULL,
name varchar(200),
PRIMARY KEY (id)
)
CREATE TABLE Participant (
student_id int NOT NULL,
class_id int NOT NULL,
FOREIGN KEY (student_id) REFERENCES Students(id)
FOREIGN KEY (class_id) REFERENCES Classes(id)
)
Then later, you can find out what classes a students visits with a SQL-query like:
SELECT c.description
FROM Students s
LEFT JOIN Participant p ON (s.id = p.student_id)
LEFT JOIN Classes c ON (p.class_id = c.id)
WHERE s.name = 'SilverSlug'
;
What you are looking for is a relations table. It will keep track of class and students. Example
Classes Table : table of all the classes
ID = Primary key
|ID | Name |
| 1 | Math |
Students Table : table of all the students
ID = Primary key
|ID | Name |
| 1 | Sam |
| 2 | Tom |
Relations Table : Used to keep track of an instance of a class
ID = Primary key Class_ID & Student_ID are Secondary keys
|ID | Class_ID | Student_ID | Time |
| 1 | 1 | 1 | 6am |
| 2 | 1 | 2 | 6am |
With these tables you can make simple queries to find out who's in what class, you can also find out what classes a student has.
I’m using a host which only supports MyISAM tables engines for MySQL. I’m trying to create a CMS using php and MySQL, however, I’m having issues working out how to create relationships between the tables. For example, one of the features within this system is being able to assign tags to an article/blogpost, similar to how stack overflow has tags on their questions.
My question is, as I cannot change my tables to use InnoDB, how can I form a relationship between the two tables? I am unable to use foreign keys as they are not supported in MyISAM, or at least not enforced.
So far, all I've found when searching is keeping track of it through PHP by ensuring that I update multiple tables at a time, but there must be a way of doing this on the MySQL side.
Below are examples of the Article and Tag tables.
+---------------------------+ +---------------------------+
| Article | | Tags |
+---------------------------+ +---------------------------+
| articleID int(11) | | tagID int(11) |
| title varchar(150) | | tagString varchar(15) |
| description varchar(150) | +---------------------------+
| author varchar(30) |
| content text |
| created datetime |
| edited datetime |
+---------------------------+
I’ve found loads of related questions on this site, but most of them InnoDB, which I cannot do as my host does not support it.
I've found a solution (kind of). I've added another table called ArticleTags
+---------------------------+
| ArticleTags |
+---------------------------+
| articleID int(11) |
| tagID int(11) |
+---------------------------+
This query returns the correct result, but I'm not sure if it's a bit of a hack, or if there is a better way to do it.
SELECT `tagString`
FROM `Tags`
WHERE id
IN (
SELECT `tagID`
FROM `ArticleTags`
WHERE `articleID` = :id
)
ORDER BY `Tags`.`tagString`
Can someone tell me if this this right?
Try TRIGGERs:
Enforcing Foreign Keys Programmatically in MySQL
Emulating Cascading Operations From InnoDB to MyISAM Tables
Example MyIsam with Foreign-Key:
Create parent table:
CREATE TABLE myisam_parent
(
mparent_id INT NOT NULL,
PRIMARY KEY (mparent_id)
) ENGINE=MYISAM;
Create child table:
CREATE TABLE myisam_child
(
mparent_id INT NOT NULL,
mchild_id INT NOT NULL,
PRIMARY KEY (mparent_id, mchild_id)
) ENGINE = MYISAM;
Create trigger (with DELIMITER):
DELIMITER $$
CREATE TRIGGER insert_myisam_child
BEFORE INSERT ON myisam_child
FOR EACH ROW
BEGIN
IF (SELECT COUNT(*) FROM myisam_parent WHERE mparent_id=new.mparent_id)=0 THEN
INSERT error_msg VALUES ('Foreign Key Constraint Violated!');//Custom error
END IF;
END;$$
DELIMITER ;
Test case:
Try insert (create 3 lines in myisam_parent and 6 lines in myisam_child):
INSERT INTO myisam_parent VALUES (1), (2), (3);
INSERT INTO myisam_child VALUES (1,1), (1,2), (2,1), (2,2), (2,3), (3,1);
Try insert:
INSERT INTO myisam_child VALUES (7, 1);
Returns this error:
ERROR 1062 (23000): Duplicate entry 'Foreign Key Constraint Violated!' for key 'PRIMARY'
Note:
This example is for INSERT, for "triggers" with DELETE and UPDATE read link (at the beginning the question)
I'm making an interactive festival programme, and decided to keep the information in a database for easy display and search. Each show can be presented in three different languages, they can have one or more show times, and many different artists. So there are many things that can be different from show to show, which is why it would be best to have some of the information in their own tables. The artists especially, as the same artist could appear at many different shows and the number of artists for each show varies.
So far I have one main table with the show title, its description, show time, ticket price etc. I want the artists to have their own table, and reference the artists by their ID and attach them to the different shows as the PHP script loops through the main table.
How can I reference an artist ID from another table in the main table?
What would a good query that fetches the artists from their table look like?
Tables (preliminary plan):
Main table
id | show title | show date | description | performers
------------------------------------------------------
1 | Beethoven | 13.4.2013 | A classic | 1,35,22,3
2 | Mozart | 14.4.2013 | Fantastic | 9,4,66
etc..
Artist table
id | name | instrument |
-------------------------
1 | Steve | Violin |
2 | Alex | Piano |
etc...
The numbers in the "performers" column would correspond to the ID's in the artist-table. Not sure that this is the right way to reference them, though. The PHP script would then combine both and output a complete show item.
Any help is appreciated.
Here is how I would set up the data schema:
CREATE TABLE `artists` (
`artist_id` int(10) unsigned NOT NULL auto_increment,
`artist_name` varchar(100) NOT NULL default '',
`instrument` varchar(100) NOT NULL default '',
PRIMARY KEY (`artist_id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
CREATE TABLE `Shows` (
`show_id` int(11) NOT NULL auto_increment,
`show_title` varchar(255) NOT NULL default '',
`description` varchar(255) NOT NULL default '',
`language` varchar(255) NOT NULL default '',
PRIMARY KEY (`show_id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
CREATE TABLE `show_times` (
`show_time_id` int(10) unsigned NOT NULL auto_increment,
`show_id` int(10) unsigned NOT NULL default '0',
`show_date` date NOT NULL default '0000-00-00',
`show_time` time NOT NULL default '00:00:00',
PRIMARY KEY (`show_time_id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
CREATE TABLE `show_time_artist_lookup` (
`show_time_id` int(10) unsigned NOT NULL default '0',
`artist_id` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`show_time_id`,`artist_id`)
) TYPE=MyISAM;
This will allow you to use the same show information for different dates/times. The artists would then be tied to show times using the lookup table.
Edit:
Actually for the languages, since you have a small set of options, you could use binary math and store a tiny number in a single field. For example:
1 = English
2 = Italian
4 = German
You can store a TinyINT value (change the schema to this)
`language` tinyint(2) NOT NULL default '1',
with any one of the three values, or a combination. For example if the show is offered in English and Italian, you would store the value 3. For all three languages it would be 7. Then when you pull your show data, you use whatever programming language to do a binary math comparison to decode it. Here is a page explaining how to do it in PHP: http://www.litfuel.net/tutorials/bitwise.htm
Lets assume you have show table with two columns: show_id, name. Where show_id is a PRIMARY KEY.
Lets assume you have artist table with two columns: artist_id, name. Where artist_id is a PRIMARY KEY.
What you need is called m:n relation, meaning one artist can be assigned to many shows and one show can have many artists. To do it you need to create a new table to store this relations.
Let's name it show_artist with two columns show_id, artist_id. Both columns will be PRIMARY KEY. Each row in this table will store one relation between one particular artist (defined by artist_id) and one show (defined by show_id)
if you know show_id, you can fetch all artist names in this show using this query:
SELECT name FROM show_artist JOIN artist USING (artist_id) WHERE show_id = [YOUR SHOW ID]
The way you do the queries you are asking about is using what's usually called a "join" table. This is an extra table in which you list the show id in one column and the artist id in another column. For example:
MainArtists table
showid | artistid
-------------------
1 | 1
1 | 2
2 | 1
In this join table, both Steve and Alex are in the Beethoven show, but only Steve is in the Mozart show.
To get them out, you use the join command in mysql:
SELECT * FROM Main
INNER JOIN MainArtists ON ShowArtists.showid = Main.id
INNER JOIN Artists ON Artists.id = MainArtists.artistid
This query would get you one row per artist per show. If you wanted only a list of artists from show #1, you would add "WHERE main.id = 1" at the end, and so forth.
It is good practice to assign the id columns in Main and Artists as primary keys, and showid and artistid columns in MainArtists as foreign keys that link back to their respective tables. There are many resources on the internet concerning joins and foreign/primary key relationships, so searching for those terms should help you as you go along.
Consider i have 2 database that have same field.
first database is for export data,
second database act as copy have database
Database 1 will export database in file format csv that output from PHP Script.
Database 2 will import database from php script.
There is two table is in each database that relations with foreign key.
table_transaction
create table `table_transaction` (
`id` int auto_increment primary key
`date` DATE default now()
) engine = innoDB;
sample data
id | date
1 | 2012-12-31
2 | 2012-12-30
3 | 2012-12-29
table_transaction_product
create table `table_transaction_product` (
`id` int auto_increment primary key
`product` string NOT NULL default '' /* Product Name */
`fk_transaction` int auto_increment NOT NULL
foreign key (`fk_transaction`)
references table_transaction(`id`)
on update cascade
on delete cascade
) engine = innoDB;
sample data
id | product | fk_transaction
1 | shampoo | 1
2 | soap | 1
3 | conditioner | 1
And this is sample exported CSV from database 1 and will be imported to table 2, that exporting transaction id 1.
insert into table_transaction (id, date) values (1, '2012-12-31');
insert into table_transaction_product (id, product, fk_transaction)
values
(1, 'shampoo', 1),
(2, 'soap', 1),
(3, 'conditioner', 1)
Question
Since the ID both table is auto_increment. Isn't there will be any problem that i insert manually the table.id and crash the auto_increment mysql system? But if didn't input the ID and let's mysql decide it, then the foreign key will be not match. What should i do?
Thank you.
It shouldn't cause a problem, but I would recommend changing the table definitions on the second database to remove the auto_increment flag.