is_unique syntax in mysql? - php

I have a table entity called position. I am storing the value using smallint(2) datatype, where I intend to store the value from 0 to 9 (max). I want to make sure the value is just a single integer and an integer should not be repeated at any cost. There should be a unique value assigned to each id of the position entity in the table ranging from 0 to 9. How do I achieve it? Is there any mysql syntax for that?
I am using MySQL with PHP.
here is the mysql table could someone add me the unique code and show
CREATE TABLE `advertisements` (
`id` int(11) NOT NULL auto_increment,
`title` varchar(255) NOT NULL,
`position` smallint(2) NOT NULL,
`active` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
is this the correct syntax?
CREATE TABLE `advertisements` (
`id` int(11) NOT NULL auto_increment,
`title` varchar(255) NOT NULL,
`position` smallint(2) NOT NULL UNIQUE,
`active` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

You must define that column as UNIQUE.
ALTER TABLE `a` ADD UNIQUE (
`id`
)

Your question has two parts:
How do I ensure that a value is unique in a column?
Add a unique index.
How do I restrict a value to be in the range 0 to 9?
You can add a foreign key constraint to a table that lists the allowed values.

First of all, smallint(2) and smallint(1234) and smallint are exactly the same in MySQL. The numeric argument is not a limit on the length or the range of values, it's only a hint for display width. MySQL's smallint is a 16-bit integer, with a range from -32768 to 32767. smallint unsigned has a range from 0 to 65535.
You can ensure no duplicates exist in a column by declaring a PRIMARY KEY or a UNIQUE KEY:
CREATE TABLE MyTable (
position SMALLINT NOT NULL,
UNIQUE KEY (position)
);
To restrict the values to the range 0 to 9, there are a few options:
Write a trigger that causes an error if you try to store a value outside the range.
DELIMITER //
CREATE TRIGGER ZeroToNine BEFORE INSERT ON MyTable
FOR EACH ROW
BEGIN
DECLARE p SMALLINT;
IF (NEW.position NOT BETWEEN 0 AND 9) THEN
SET p = 'Position must be between 0 and 9';
END IF;
END //
DELIMITER ;
Use a foreign key constraint so the column references a table that you populate with the values 0 to 9.
CREATE TABLE ZeroToNine (
position SMALLINT NOT NULL PRIMARY KEY
) ENGINE=InnoDB;
INSERT INTO ZeroToNine VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9);
CREATE TABLE MyTable (
position SMALLINT NOT NULL,
UNIQUE KEY (position),
FOREIGN KEY (position) REFERENCES ZeroToNine (position)
) ENGINE=InnoDB;
Other databases also support a CHECK constraint, but MySQL unfortunately doesn't support this feature.
CREATE TABLE MyTable (
position SMALLINT NOT NULL
CHECK (position BETWEEN 0 AND 9),
UNIQUE KEY (position)
);

Related

improve MySQL query performance from slow query log

I turned on slow query monitor in MySQL config.
Below is the query and time:
Time: 160330 20:54:11
User#Host: user[user] # [xx.xx.xxx.xxx]
Query_time: 8.794170 Lock_time: 0.000141 Rows_sent: 3942 Rows_examined: 4742825
SET timestamp=1459371251;
SELECT (SELECT (CASE WHEN ce_type = 'IN' then SUM(payment_amount)
END) as debit
FROM customer_payment_options cpo
WHERE wallet_id=cw.id
AND (cpo.real_account_type='HQ')
AND cpo.source_country_id='40'
GROUP BY cpo.wallet_id)
as debit,
(SELECT SUM(payment_amount)
as credit
FROM customer_payment_options cpo
WHERE wallet_id=cw.id
AND (cpo.real_account_type='HQ')
AND cpo.tran_id IS NOT NULL
AND cpo.source_country_id='40'
GROUP BY cpo.wallet_id)
as credit
FROM customer_wallet cw
WHERE cw.company_id='1'
AND cw.currency='40'
AND cw.is_approved = '1'
AND DATE(cw.date_added) < '2016-03-30';
Indexes on customer_payment_options:
company_id
tran_id
ce_id
wallet_id
What should I do to improve it's performance?
EXPLAIN:
http://i.stack.imgur.com/iH8rt.png
SCHEMA
CREATE TABLE `customer_payment_options` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`local_branch_id` int(11) NOT NULL,
`tran_id` bigint(11) DEFAULT NULL,
`ce_id` int(11) DEFAULT NULL,
`wallet_id` int(11) DEFAULT NULL,
`reward_credit_id` int(11) DEFAULT NULL,
`ce_invoice_id` varchar(32) DEFAULT NULL,
`ce_type` enum('IN','OUT') DEFAULT NULL,
`payment_type` enum('CASH','DEBIT','CREDIT','CHEQUE','DRAFT','BANK_DEPOSIT','EWIRE','WALLET','LOAN','REWARD_CREDIT') NOT NULL,
`payment_amount` varchar(20) NOT NULL,
`payment_type_number` varchar(100) DEFAULT NULL,
`source_country_id` int(11) NOT NULL,
`real_account_id` int(11) DEFAULT NULL,
`real_account_type` enum('LOCAL','HQ') DEFAULT NULL,
`date_added` datetime NOT NULL,
`event_type` enum('MONEY_TRANSFER','CURRENCY_EXCHANGE','WALLET') DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `real_account_type` (`real_account_type`),
KEY `tran_id` (`tran_id`),
KEY `ce_id` (`ce_id`),
KEY `wallet_id` (`wallet_id`),
CONSTRAINT `customer_payment_options_ibfk_4` FOREIGN KEY (`wallet_id`) REFERENCES `customer_wallet` (`id`),
CONSTRAINT `customer_payment_options_ibfk_1` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`),
CONSTRAINT `customer_payment_options_ibfk_2` FOREIGN KEY (`tran_id`) REFERENCES `transaction` (`id`),
CONSTRAINT `customer_payment_options_ibfk_3` FOREIGN KEY (`ce_id`) REFERENCES `currency_exchange` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=412 DEFAULT CHARSET=utf8
CREATE TABLE `customer_wallet` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`wallet_unique_id` varchar(100) DEFAULT NULL,
`company_id` int(11) NOT NULL,
`branch_admin_id` int(11) DEFAULT NULL,
`emp_id` int(11) DEFAULT NULL,
`emp_type` enum('SUPER_ADMIN','ADMIN','AGENT_ADMIN','AGENT','OVER_AGENT_ADMIN','OVER_AGENT') DEFAULT NULL,
`cus_id` bigint(11) NOT NULL,
`tran_id` bigint(11) DEFAULT NULL,
`beehive_id` int(11) DEFAULT NULL,
`type` enum('DEPOSIT','WITHDRAW','TRANSACTION') NOT NULL,
`sub_type` enum('MONEY_TRANSFER','BEEHIVE_DEPOSIT') DEFAULT NULL,
`credit_in` varchar(20) DEFAULT NULL,
`credit_out` varchar(20) DEFAULT NULL,
`currency` varchar(20) NOT NULL,
`date_added` datetime NOT NULL,
`note` varchar(255) DEFAULT NULL,
`location` enum('DIRECT') DEFAULT NULL,
`is_approved` enum('0','1') NOT NULL DEFAULT '1',
`idebit_issconf` varchar(50) DEFAULT NULL,
`idebit_issname` varchar(50) DEFAULT NULL,
`idebit_isstrack2` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `cus_id` (`cus_id`),
KEY `company_id` (`company_id`),
KEY `branch_admin_id` (`branch_admin_id`),
KEY `emp_id` (`emp_id`),
KEY `tran_id` (`tran_id`),
KEY `beehive_id` (`beehive_id`),
CONSTRAINT `customer_wallet_ibfk_1` FOREIGN KEY (`cus_id`) REFERENCES `customers` (`id`),
CONSTRAINT `customer_wallet_ibfk_2` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`),
CONSTRAINT `customer_wallet_ibfk_3` FOREIGN KEY (`tran_id`) REFERENCES `transaction` (`id`),
CONSTRAINT `customer_wallet_ibfk_4` FOREIGN KEY (`emp_id`) REFERENCES `employees` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=152 DEFAULT CHARSET=utf8
What you are doing as a correlated query on every wallet ID to get the corresponding debits and credits. It appears you are getting one record per wallet id. This is very busy. Having a join to the customer payments table on your criteria that is common (including the join per wallet id). Then, simplify the CASE as a SUM( case/when ) as respective debit / credit.
I don't know your underlying criteria of table columns, but I would even hedge to (and did) include NOT the CE_TYPE = 'IN' as that appears basis of a debit and you would not want to falsely count as part of a credit too. Again, dont know correlation of fields, trans_id, types.
Now, as stated, having individual indexes on individual fields will not help optimize this query. I would suggest the following indexes.
table index
customer_wallet ( company_id, is_approved, currency, id, date_added )
customer_payment_options ( wallet_id, account_type, country_id )
SELECT
cw.wallet_id,
SUM( case when cpo.ce_type = 'IN'
then cpo.payment_amount
ELSE 0 end ) as Debit,
SUM( case when NOT cpo.ce_type = 'IN'
AND cpo.tran_id IS NOT NULL
then cpo.payment_amount
ELSE 0 end ) as Credit
FROM
customer_wallet cw
JOIN customer_payment_options cpo
ON cw.id = cpo.wallet_id
AND cpo.real_account_type = 'HQ'
AND cpo.source_country_id = '40'
WHERE
cw.company_id = '1'
AND cw.currency = '40'
AND cw.is_approved = '1'
AND cw.date_added < '2016-03-30'
GROUP BY
cw.id
One additional comment. if your ID columns, Currency flag, country ID, approved are actually numeric values in the table structure, remove the quotes and let compare directly on the numeric value. Also, for your date_added. You had that based on DATE( date_added ). Doing a function on a column can not fully utilize the index. Since date() strips off any time portion of a date/time stamp column, and you are asking for all added less than Mar 30, then date added of March 29 # 11:59:59pm is still less than Mar 30 at 12:00:00am, so no date conversion is required.
As commented by Ivan (below), if you want ALL Wallet IDs regardless of having any payments (debit or credit), then change from a join to a LEFT JOIN.
You need to add indexes and multi-column indexes to make it fast.
Please keep in mind, that if you have large table, extra indexes will slow-down the insertions , since index file update will take more time.
If a multiple-column index exists on col1 and col2, the appropriate
rows can be fetched directly. If separate single-column indexes exist
on col1 and col2, the optimizer attempts to use the Index Merge
optimization (see Section 8.2.1.4, “Index Merge Optimization”), or
attempts to find the most restrictive index by deciding which index
excludes more rows and using that index to fetch the rows.
If the table has a multiple-column index, any leftmost prefix of the
index can be used by the optimizer to look up rows. For example, if
you have a three-column index on (col1, col2, col3), you have indexed
search capabilities on (col1), (col1, col2), and (col1, col2, col3).
Read more

I want to add multiple data in mysql table with php

I have 1 user and I want to insert more than one language(and degree) to him. How can I do this with php?
My languagetype table(all languages inserted)
CREATE TABLE TLANGUAGETYPE (
ID INT NOT NULL AUTO_INCREMENT,
language VARCHAR(100) NOT NULL,
PRIMARY KEY (ID)
)
My language table connected with user table.
CREATE TABLE TLANGUAGE (
languageID INT NOT NULL AUTO_INCREMENT,
userID INT NOT NULL,
ID INT NOT NULL,
degree FLOAT,
PRIMARY KEY (languageID),
FOREIGN KEY (userID) REFERENCES TUSER(userID),
FOREIGN KEY (ID) REFERENCES TLANGUAGETYPE(ID)
)
Instead of adding the languageId in user Table. You can create a mapping table which just contains the "USERID" and "LanguageID" columns.

mysql 'likes' structure table

I'm working on a web site where users can post articles with this table structure :
CREATE TABLE IF NOT EXISTS `articles` (
`id_articles` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_users` int(10) unsigned NOT NULL,
`articles` text NOT NULL,
PRIMARY KEY (`id_articles`),
UNIQUE KEY `id_articles` (`id_articles`),
KEY `id_users` (`id_users`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Each user can 'like' the articles.
Is that the right way below to create a 'like table' :
CREATE TABLE IF NOT EXISTS `articles_likes` (
`id_articles` int(10) unsigned NOT NULL,
`id_users` int(10) unsigned NOT NULL,
KEY `id_articles` (`id_articles`,`id_users`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
It is correct but you will want to add separte indexes on id_articles and id_users (also you might want to name the columns 'id_article' and 'id_user' for sanity).
CREATE TABLE IF NOT EXISTS `article_likes` (
`id_article` int(10) unsigned NOT NULL,
`id_user` int(10) unsigned NOT NULL,
KEY `id_article` (`id_article`),
KEY `id_user` (`id_user`)
) ENGINE=InnoDB;
The reason you want separate indexes is because in mysql if you create an index on columns (A, B) that index will be used in queries having in the where clause column A, or columns A and B.
In your case for example if you made a query "SELECT * FROM article_likes WHERE id_user=X" this query would not use an index.
An ever better option would be to add a combined index and a separate index on the second column from the combined index. Like this:
CREATE TABLE IF NOT EXISTS `article_likes` (
`id_article` int(10) unsigned NOT NULL,
`id_user` int(10) unsigned NOT NULL,
KEY `id_article_user` (`id_article`, `id_user`),
KEY `id_user` (`id_user`)
) ENGINE=InnoDB;
This way you would have optimal performance on queries like 'WHERE id_user=X', "WHERE id_article=X', "WHERE id_article=X AND id_user=Y"
This is a valid way Chris. You can use COUNT() to match the id_articles in the articles_likes table against the current article you are viewing in articles.
$articles_id = 23;
mysql_query("SELECT COUNT(*) FROM articles_likes
WHERE id_articles = ".$articles_id);
You can also just leave COUNT() (MySQL) out and instantly know which users are the "likers" of the articles and use count() (PHP) on the returned Array to duplicate the effect of COUNT() in MySQL.
i would have a total of 3 tables. an articles table, and the user id could be a column in that for users who submit articles , but you need a separate user table since not all users will submit articles (i am assuming), and then a 3rd table for likes, that takes the primary key from users and the primary key from articles and uses them as foreign keys. so each time an article is liked, an entry is made in the 3rd table

Mysql error with converting from myISAM to innodb

hey guys, i'm getting this error.
Error 1452 : Cannot add or update a child row: a foreign key constraint fails (`s2794971db/ProfileInterests`, CONSTRAINT `ProfileInterests_ibfk_2` FOREIGN KEY (`InterestID`) REFERENCES `Interests` (`ID`))
I change my tables from myISAM to innodb...found out I needed to so that delete was easier.
I had issues with it so I deleted the table which I needed to create the relationships with.
Then I made it again
I originally had
create table if not exists Users (
ID int not null auto_increment primary key,
FirstName varchar(40) not null,
LastName varchar(40) not null,
UserName varchar(40) not null,
UserEmail varchar(40) not null,
UserDOB timestamp not null,
UserJoin datetime not null
);
create table if not exists Interests(
ID int not null auto_increment primary key,
Interests varchar(40) not null
);
create table if not exists ProfileInterests (
userID int not null References Users(ID),
InterestID int not null References Interests(ID),
MiddleID int not null auto_increment primary key
);
but then I deleted the last table and made it
create table if not exists ProfileInterests (
userID int not null,
InterestID int not null,
MiddleID int not null auto_increment primary key
);
and then I made userID and InterestID into index's and then I added a relation User-> ID for userID and interests->ID for interestID
the error occurs when i'm trying to input data into via a php form.
Any ideas...really need some help!
Obviously, you're trying to insert a record into ProfileInterests for which there is no matching record in the Interests table. Look at the exact insert query, and check that you're supplying a valid value for every field in the table which is part of a foreign key relationship.

PHP/MySQL database question?

I have a question I'm trying to have the q_id column take the value of the id column when a tag is submitted I have been trying to do this using PHP but I was thinking should I just add AUTO INCREMENT to the q_id column since but tables are updated when the tag is submited. or is this the wrong way to do it or is there a better way to do this?
Here is the MySQL tables below.
CREATE TABLE q_tags (
q_id INT UNSIGNED NOT NULL,
users_q_id INT UNSIGNED NOT NULL
);
CREATE TABLE tags (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
tag VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
AUTO INCREMENT is used mainly to avoid adding the same ID into a table, so that you can keep a loyal index. I'd suggest you add a primary key to the q_tags, and change the current q_id to a foreign key, so that it can become a reference to tags table.
EDIT :
CREATE TABLE q_tags (
q_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
t_id INT UNSIGNED NOT NULL,
users_q_id INT UNSIGNED NOT NULL,
PRIMARY KEY (q_id)
);
CREATE TABLE tags (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
tag VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
t_id in the first table is the "foreign key". You can add it as a real mysql foreign key or just keep it this way (if you're planning on be able to "cascade delete" tags and t_tags related, it would matter)

Categories