How do I manage multiple combined index keys in Yii MVC? - php

I have developed a script using Yii MVC and i have a problem with the index keys and criterias.
I want to prevent the insertion of a record that is already stored in the database;
My example, fails to check and tries to add a new record each time.
Why ? And how to do this ?
CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '21-/popular/category/1.html' for key 'index_link'. The SQL statement executed was: INSERT INTO `categories` (`server_id`, `website_id`, `slave_category_id`, `link`, `name`, `image`, `videos`, `status`) VALUES (:yp0, :yp1, :yp2, :yp3, :yp4, :yp5, :yp6, :yp7)
CREATE TABLE IF NOT EXISTS `categories` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`server_id` int(11) NOT NULL,
`website_id` int(11) NOT NULL,
`slave_category_id` int(11) NOT NULL,
`link` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`image` varchar(255) NOT NULL,
`videos` int(11) NOT NULL,
`status` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_link` (`website_id`,`link`),
UNIQUE KEY `index_name` (`website_id`,`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=332 ;
the PK is id;
another index, is website_id + link;
another index is website_id + name;
the following code fails to check and prevent the insertion:
1
$criteria_categories = new CDbCriteria();
$criteria_categories->condition = " `server_id`=':server_id' and `website_id`=':website_id' and `link`=':link' and `name`=':name' ";
$criteria_categories->params = array(
':server_id' => $model_website->server_id,
':website_id' => $model_website->id,
':link' => $matches_url[$value->link][$key2],
':name' => $matches_url[$value->name][$key2],
);
$record_categories = Categories::model()->find($criteria_categories);
print_r($record_categories);
if (!$record_categories) {
$model_categories = new Categories();
$model_categories->server_id = $model_website->server_id;
$model_categories->website_id = $model_website->id;
$model_categories->slave_category_id = 1; //??
$model_categories->link = $matches_url[$value->link][$key2];
$model_categories->name = $matches_url[$value->name][$key2];
$model_categories->image = $matches_url[$value->image][$key2];
$model_categories->videos = 0;
$model_categories->status = 0;
$model_categories->save();
}

$criteria_categories->condition = " `server_id`=':server_id' and `website_id`=':website_id' and `link`=':link' and `name`=':name' ";
You have a couple errors going on. One is syntax. If using the parameter binding (which is a good thing to use), you don't want to quote the parameters.
That is, write :link instead of ':link'.
The above condition also does not correctly check for existing records that have either of your two unique keys. Try the following:
$criteria_categories->addCondition("`server_id`=:server_id AND `website_id` = :website_id
AND (`link`=:link OR `name` = :name)");
This both quotes correctly and will find the record if either of your two unique keys match, rather than requiring both to match.

Related

PHP SQL query slow?

Here is my function which i am using to un-follow users.It first DELETE the relationship between users and all the notifications that are related to this relationship.Then it INSERT a new notification for user which we are going to un-follow and then UPDATE his followers count (as one follower has left).I am using multi_query and this query seems to be bit slower on large database and i want to know whether it's a good practice or not or is there is any more complex form of query to get the job done.
PHP Function
// 'By' is the array that hold logged user and 'followed' is the user id which we are going to unfollow
function unFollowUser($followed,$by) {
$following = $this->getUserByID($followed);// Return fetch_assoc of user row
if(!empty($following['idu'])) { // if user exists
// return user followers as number of rows
$followers = $this->db->real_escape_string($this->numberFollowers($following['idu'])) - 1;
$followed_esc = $this->db->real_escape_string($following['idu']);
$by_user_esc = $this->db->real_escape_string($by['idu']);
// delete relationship
$query = "DELETE FROM `relationships` WHERE `relationships`.`user2` = '$followed_esc' AND `relationships`.`user1` = '$by_user_esc' ;" ;
// delete notification (user started following you )
$query.= "DELETE FROM `notifications` WHERE `notifications`.`not_from` = '$by_user_esc' AND `notifications`.`not_to` = '$followed_esc' ;" ;
// Insert a new notification( user has unfollowed you)
$query.= "INSERT INTO `notifications`(`id`, `not_from`, `not_to`, `not_content_id`,`not_content`,`not_type`,`not_read`, `not_time`) VALUES (NULL, '$by_user_esc', '$followed_esc', '0','0','5','0', CURRENT_TIMESTAMP) ;" ;
// update user followers (-1)
$query .= "UPDATE `users` SET `followers` = '$followers' WHERE `users`.`idu` = '$followed_esc' ;" ;
if($this->db->multi_query($query) === TRUE) {
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
Table structures
--
-- Table structure for table `notifications`
--
CREATE TABLE IF NOT EXISTS `notifications` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`not_from` int(11) NOT NULL,
`not_to` int(11) NOT NULL,
`not_content_id` int(11) NOT NULL,
`not_content` int(11) NOT NULL,
`not_type` int(11) NOT NULL,
`not_read` int(11) NOT NULL,
`not_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Table structure for table `relationships`
--
CREATE TABLE IF NOT EXISTS `relationships` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user1` int(11) NOT NULL,
`user2` int(11) NOT NULL,
`status` int(11) NOT NULL,
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Table structure for table `users`
--
CREATE TABLE IF NOT EXISTS `users` (
`idu` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL,
`password` varchar(256) NOT NULL,
`email` varchar(256) NOT NULL,
`first_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`last_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`verified` int(11) NOT NULL,
`posts` text CHARACTER SET utf32 NOT NULL,
`photos` text CHARACTER SET utf32 NOT NULL,
`followers` text CHARACTER SET utf32 NOT NULL,
UNIQUE KEY `id` (`idu`),
UNIQUE KEY `idu` (`idu`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
In my testing, multi_query has been the fastest way to execute multiple different queries. Why do you feel it's running slow? Compared to what?
Anyway, improvements could come from adding indexes to some of the columns you search frequently:
relationships.users2
relationships.users1
notifications.not_from
notifications.not_to
users.idu
Adding indexes makes searching faster, but it has at least two downsides:
Makes the DB a lot more resource hungry, which could affect your server performance
Makes writing operations take longer
I don't see any problem with your current queries. Really consider whether the slow performance you're seeing comes from the DB queries themselves, or from the rest of your PHP process. Try measuring the script time with the queries, then skipping the queries and taking another measurement (you could hardcode query results). It will give you an idea of whether the slowness is attributable to something else.
Either way, benchmark.
Try creating index on user where deletes are running , this may speed up query

no entry into parent table but "0" or "blank" insert into FK in child table

Hi guys I am trying to solve one problem with inserting data to Parent - Child tables. Tables below and also ERR diagram show a structure and PK/FK keys. I am inserting data from webform and PHP is used to capture data and pass it to the database.
Fields in mainTable - F_Name, L_Name and Email are just input textfields,
fields in college tables are checkboxes.
Imagine that one teacher can teach at one, two or three colleges where he checks the checkbox for each college/school where he is teaching. But if he teaches only at one college there is when my problem comes. As all of the "college" tables are linked to "Teacher" with PK/FK.
My question is, is there any way how to store auto generated College ID's if for example teacher is teaching only at one college. At the moment with my PHP it fails and I don't know how to fix it.
I have a example of my PHP under the Schema structure. Just a small note that connection to database works properly.
If this or similar was already asked I do appologize.
Thanks for any tips.
-------------------------------------------------------
-- Schema test
-- -----------------------------------------------------
CREATE SCHEMA IF NOT EXISTS `test` DEFAULT CHARACTER SET latin1 ;
USE `test` ;
-- -----------------------------------------------------
-- Table `test`.`CollegeA`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `test`.`CollegeA` (
`CollegeAID` INT(11) NOT NULL AUTO_INCREMENT,
`SchoolA` VARCHAR(45) NOT NULL,
`SchoolB` VARCHAR(45) NOT NULL,
`SchoolC` VARCHAR(45) NOT NULL,
PRIMARY KEY (`CollegeAID`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `test`.`CollegeB`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `test`.`CollegeB` (
`CollegeBID` INT(11) NOT NULL AUTO_INCREMENT,
`School1` VARCHAR(45) NOT NULL,
`School2` VARCHAR(45) NOT NULL,
`School3` VARCHAR(45) NOT NULL,
PRIMARY KEY (`CollegeBID`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `test`.`CollegeC`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `test`.`CollegeC` (
`CollegeCID` INT(11) NOT NULL AUTO_INCREMENT,
`School11` VARCHAR(45) NOT NULL,
`School22` VARCHAR(45) NOT NULL,
`School33` VARCHAR(45) NOT NULL,
PRIMARY KEY (`CollegeCID`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `test`.`Teacher`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `test`.`Teacher` (
`TeacherId` INT(11) NOT NULL AUTO_INCREMENT,
`F_name` VARCHAR(45) NOT NULL,
`L_name` VARCHAR(45) NOT NULL,
`Email` VARCHAR(45) NOT NULL,
`CollegeAID` INT(11) NOT NULL,
`CollegeBID` INT(11) NOT NULL,
`CollegeCID` INT(11) NOT NULL,
PRIMARY KEY (`MainId`),
INDEX `CollegeAID_idx` (`CollegeAID` ASC),
INDEX `CollegeBID_idx` (`CollegeBID` ASC),
INDEX `CollegeCID_idx` (`CollegeCID` ASC),
CONSTRAINT `CollegeAID`
FOREIGN KEY (`CollegeAID`)
REFERENCES `test`.`CollegeA` (`CollegeAID`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `CollegeBID`
FOREIGN KEY (`CollegeBID`)
REFERENCES `test`.`CollegeB` (`CollegeBID`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `CollegeCID`
FOREIGN KEY (`CollegeCID`)
REFERENCES `test`.`CollegeC` (`CollegeCID`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
SET SQL_MODE=#OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=#OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=#OLD_UNIQUE_CHECKS;
PHP example
if(empty($SchoolA) && empty($SchoolB) && empty($SchoolC)){
$CollegeAId = "";
}
else {
$queryCOLLEGEA = "
INSERT INTO CollegeA (SchoolA, SchoolB, SchoolC)
VALUES('$SchoolA','$SchoolB','$SchoolC')";
$result = mysqli_query($con, $queryCOLLEGEA);
$CollegeAId = mysqli_insert_id($con);
};
if(empty($School1) && empty($School2) && empty($School3)){
$CollegeBId = "";
}
else {
$queryCOLLEGEB = "
INSERT INTO CollegeB (School1, School2, School3)
VALUES('$School1','$School2','$School3')";
$result = mysqli_query($con, $queryCOLLEGEB);
$CollegeBId = mysqli_insert_id($con);
};
if(empty($School11) && empty($School22) && empty($School33)){
$CollegeCId = "";
}
else {
$queryCOLLEGEC = "
INSERT INTO CollegeB (School11, School22, School33)
VALUES('$School11','$School22','$School33')";
$result = mysqli_query($con, $queryCOLLEGEC);
$CollegeCId = mysqli_insert_id($con);
};
$queryMain = "
INSERT INTO Teacher (F_Name, L_Name, Email, CollegeAID, CollegeBID, CollegeCID)
VALUES ('$F_Name', '$L_Name', '$Email', '$CollegeAId', '$CollegeBId', '$CollegeCId')";
$result = mysqli_query($con, $queryMain);
You are using NOT NULL column as foreign key. In this case you cannot leave it empty, you must set here correct key from referenced table. You can change table definition to
CREATE TABLE IF NOT EXISTS `test`.`Teacher` (
`TeacherId` INT(11) NOT NULL AUTO_INCREMENT,
`F_name` VARCHAR(45) NOT NULL,
`L_name` VARCHAR(45) NOT NULL,
`Email` VARCHAR(45) NOT NULL,
`CollegeAID` INT(11),
`CollegeBID` INT(11),
`CollegeCID` INT(11),
PRIMARY KEY (`MainId`),
INDEX `CollegeAID_idx` (`CollegeAID` ASC),
INDEX `CollegeBID_idx` (`CollegeBID` ASC),
INDEX `CollegeCID_idx` (`CollegeCID` ASC),
CONSTRAINT `CollegeAID`
FOREIGN KEY (`CollegeAID`)
REFERENCES `test`.`CollegeA` (`CollegeAID`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `CollegeBID`
FOREIGN KEY (`CollegeBID`)
REFERENCES `test`.`CollegeB` (`CollegeBID`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `CollegeCID`
FOREIGN KEY (`CollegeCID`)
REFERENCES `test`.`CollegeC` (`CollegeCID`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
In this table you can insert NULL values into CollegeAID, CollegeBID and CollegeCID. So, if teacher works in college, it will have value in appropriate CollegeID. If no - CollegeID will be NULL.
Also you will ned to change your code. Change you code like this
if(empty($SchoolA) && empty($SchoolB) && empty($SchoolC)){
$CollegeAId = null;
}
for all three colleges. You need null, not empty string.
And another change is needed here
$queryMain = "
INSERT INTO Teacher (F_Name, L_Name, Email, CollegeAID, CollegeBID, CollegeCID)
VALUES ('$F_Name', '$L_Name', '$Email', '$CollegeAId', '$CollegeBId', '$CollegeCId')";
Variable $CollegeAId now contains proper NULL value. But this query will be produced into
INSERT INTO Teacher (F_Name, L_Name, Email, CollegeAID, CollegeBID, CollegeCID)
VALUES ('F_Name', 'L_Name', 'Email', '', 'CollegeBId', 'CollegeCId')
See it? Still empty string instead of NULL! You need to change query string. It must looks like
INSERT INTO Teacher (F_Name, L_Name, Email, CollegeAID, CollegeBID, CollegeCID)
VALUES ('F_Name', 'L_Name', 'Email', NULL, 'CollegeBId', 'CollegeCId')
For example, you can do it this way for college A:
$CollegeAId = isset($CollegeAId) ? "'$CollegeAId'" : 'NULL';
$queryMain = "
INSERT INTO Teacher (F_Name, L_Name, Email, CollegeAID, CollegeBID, CollegeCID)
VALUES ('$F_Name', '$L_Name', '$Email', $CollegeAId, '$CollegeBId', '$CollegeCId')";

Symfony2 MySQL: INSERT SELECT syntax error

I am having problems with writing correct MySql query. I want to insert new collection for every user with id higher than 1000 but less than 10000.
$conn = $this->em->getConnection();
$stmt = $conn->prepare('INSERT INTO collection (name, type)
values(:name, :type)
SELECT * FROM user WHERE id<:endUser AND id>:startUser');
$stmt->bindValue('name', 'Default');
$stmt->bindValue('type', 0);
$stmt->bindValue('startUser', 1000);
$stmt->bindValue('endUser', 10000);
$stmt->execute();
This what I tried to write, but I get syntax error. Please explain me how to correct query
UPD
I should have given detailed structure of tables.
Collection
CREATE TABLE IF NOT EXISTS `collection` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`type` smallint(6) NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_FC4D6532A76ED395` (`user_id`)
);
User
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
User has one-to-many relationship with Collection.
With a SELECT INTO you have to select the values you want to place in the new row and only those values. And you dont use the VALUES() clause.
As you are using static values for the new rows and not values from the user table you can do it like this.
Oh and I see in your edit you were using the wrong table name It should have been fos_user
Also as fos_user.user_id is a NOT NULL field you need to include that column in the list of fields in the insert.
$conn = $this->em->getConnection();
$stmt = $conn->prepare('INSERT INTO collection (user_id, name, type)
SELECT id, 'default', 0
FROM fos_user
WHERE id > :startUser AND id < :endUser');
$stmt->bindValue('startUser', 1000);
$stmt->bindValue('endUser', 10000);
$stmt->execute();

MySQL query doesn't work in php and works when run directly

I have a very strange problem with MySQL and PHP.
I have into a function the below query:
Global $Linker;
$query = "INSERT INTO ".$user_type." (id) VALUES (?)";
if($stmt = mysqli_prepare($Linker->DataBase,$query)) {
mysqli_stmt_bind_param($stmt,"i",$max_id);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
} printf("Error: %s.\n", mysqli_stmt_error($stmt));
where
$Linker is the variable that holds the database connection
and
$user_type is the table I want to insert in the database
I get the following error printed
Error: Cannot add or update a child row: a foreign key constraint fails (std10179db/Students, CONSTRAINT fk_Students_Users1 FOREIGN KEY (id) REFERENCES mydb.Users (Table_ID) ON DELETE NO ACTION ON UPDATE NO ACTION).
The problem is that when I run the same query directly in MySQL through command line in the server it works as expected with no errors.
I found a work around using:
$query= "SET FOREIGN_KEY_CHECKS=0";
$stmt = mysqli_prepare($Linker->DataBase,$query);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
before my query in the PHP function and another workaround would be to change my database schema
but I want to know why that is happening?
I have even tried to execute the query without parameters:
$query = "INSERT INTO Students (id) VALUES (3)";
to be exactly the same with the one I put directly into the server with no success.
Any ideas?
edit:
Table USERS
CREATE TABLE `std10179db`.`Users` (
`ID` INT NOT NULL ,
`Username` VARCHAR(45) NOT NULL ,
`Password` VARCHAR(45) NOT NULL ,
`email` VARCHAR(45) NOT NULL ,
`User_Class` VARCHAR(45) NOT NULL ,
`Table_ID` INT NOT NULL ,
PRIMARY KEY (`ID`) ,
UNIQUE INDEX `Username_UNIQUE` (`Username` ASC) ,
UNIQUE INDEX `Password_UNIQUE` (`Password` ASC) ,
UNIQUE INDEX `email_UNIQUE` (`email` ASC) )
ENGINE = InnoDB;
and Table Students
CREATE TABLE `std10179db`.`Students` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
`Univ_ID` VARCHAR(45) NULL ,
`Name` VARCHAR(45) NULL ,
`Surname` VARCHAR(45) NULL ,
`Telephone` VARCHAR(45) NULL ,
`Semester` INT NULL ,
`Department_id` INT UNSIGNED NULL ,
PRIMARY KEY (`id`, `Department_id`) ,
UNIQUE INDEX `idStudents_UNIQUE` (`id` ASC) ,
INDEX `fk_Students_Department1` (`Department_id` ASC) ,
CONSTRAINT `fk_Students_Department1`
FOREIGN KEY (`Department_id` )
REFERENCES `mydb`.`Department` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_Students_Users1`
FOREIGN KEY (`id` )
REFERENCES `mydb`.`Users` (`Table_ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;

MySQL ON DUPLICATE KEY two unique fields do not insert

I got this table
CREATE TABLE IF NOT EXISTS `set_indice_cuestionarios` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cuestionario` int(11) NOT NULL,
`fecha` date NOT NULL,
`hora` time NOT NULL,
`aplico` varchar(100) NOT NULL,
`cliente` int(11) NOT NULL,
`tienda` int(11) NOT NULL,
`status` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `tienda` (`tienda`),
UNIQUE KEY `cuestionario` (`cuestionario`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
And this is my SQL sentence:
$sql_indice = "INSERT INTO set_indice_cuestionarios (cuestionario, fecha, hora, aplico, cliente, tienda) values('$cuestionario','$fecha','$hora','$aplico','$cliente','$tienda') ON DUPLICATE KEY UPDATE cuestionario = '$cuestionario', fecha = '$fecha', hora = '$hora', aplico = '$aplico', cliente = '$cliente', status = 0";
I want to Update a row if $tienda = tienda and $cuestionario = cuestionario and to Insert a new one if the any of the values doesn't match.
The Update is happening when both values match but when I only change $cuestionario it Updates the table, instead of inserting a new record, and when I only change $tienda it does the Insert. I have no idea what I' am doing wrong. Thank you in advance!

Categories