Update a custom column value sql - php

When outside the boxing thinking. I came up with the following solution:
$column = 'product';
$enum = '1';
$product_access = $dbh->prepare("UPDATE products_access SET {$column} = :enum WHERE products_access.id = :id");
$product_accessvar = trim($user['id']);
$product_access->bindParam(':id', $product_accessvar, PDO::PARAM_INT);
$product_access->bindParam(':enum', $enum, PDO::PARAM_INT);
//$product_access->bindParam(':product_enum', $enum);
//foreach($_POST["checkbox2"] as $loc_id)
$product_access->execute();
Thanks for your help, maybe i can help someone with my solution. It works for me now!
Below my question
I have a question about my follow project. I build an sql table where the users can add custom columns with a enum value. But the user can also update the value of this column. I can't set any column name because they all custom made by the user, so there is no column name.
My SQL:
tabel `products_access`
--
CREATE TABLE IF NOT EXISTS `products_access` (
`id` int(30) NOT NULL,
`user_id` int(30) NOT NULL,
`product 2` enum('0','1') NOT NULL COMMENT 'Dit is een product beschrijving van product 2.dgfdg'
)
Array:
Array
(
[id] => 17
[name] => product 2
[number] => 2002
[description] => Dit is een product beschrijving van product 2.
[mount] => 34
[price] => 6778
[deleted] => 0
[user_id] => 17
[product 2] => 1
)
Script:
<label>
<input name="clickedproduct[]" type="checkbox" value="<?php echo $avlue['id']; ?>" <?php echo (($_SERVER['REQUEST_METHOD'] == 'POST') ? ((isset($_POST[$avlue['name']])) ? ' value="'.$avlue[$avlue['name']].'" checked' : ' value="'.$avlue[$avlue['name']].'"') : (($avlue[$avlue['name']] == '1') ? ' value="'.$avlue[$avlue['name']].'"checked' : ' value="'.$avlue[$avlue['name']].'"')); ?>>
<?php echo $avlue['name']; ?>
</label>
PDO:
$product_access = $dbh->prepare('UPDATE products_access() VALUES(:id, :loc)');
$product_access->bindValue(':id', $id);
$product_access->bindParam(':loc', $loc_id);
foreach($_POST["checkbox2"] as $loc_id) $product_access->execute();
Below the array after saving.
Array
(
[username] => joshua
[rank] => Array
(
[0] => 0
)
[koppel] => Array
(
[0] => 1
)
[clickedproduct] => Array
(
[0] => 17
)
)
Can some one explain the solution?

This sort of application ordinarily is implemented using a metadata table, also known as a key / value store.
Each row of this metadata table identifies
the object it describes, with a product_id or similar foreign key.
the name of the data item it holds, e.g. 'price' or 'mount'
the value of the data item it holds, e,g, '6778' or '34'
optionally a code identifying the data type of the item ('money'? 'text'?)
It's easy to add metadata to an object. Insert a row into the metadata table giving the "column name" you want and the value. It's a little trickier to retrieve it. You need a query, for example, like this.
SELECT p.product_id, a.val as price, b.val as mount
FROM product p
LEFT JOIN metadata a ON a.product_id = p.product_id AND a.key='price'
LEFT JOIN metadata b ON b.product_id = p.product_id AND b.key='mount'
WordPress's wp_postmeta table setup is a good and widely used example of this data design pattern. It has a workable API.
It's ordinarily considered bad practice to use data definition language commands (like ALTER TABLE CHANGE colname newcolname INT) in production. For one thing these commands are quite slow and thread-unsafe. For another, when a schema contains all kinds of user-defined columns it's hard to troubleshoot. You're better off using application code, like what I have suggested here, to allow your users to create their own data keys and values.

Create a column named 'custom_column' in 'products_access'.(Add user
defined column name here)
Create another table named 'tbl_custom_column'
Add fields 'user_id', 'custom_column_value' (Add your values here)
Hope that will help.

Related

How to optimize my query to not reach max time execution time?

I have a temporary table with 100k rows. It comes from a csv file and I use LOAD DATA LOCAL INFILE to put it in temporary table and I need to process those raw data because it has duplication. So I make 3 tables which are product, product_images and product_description to process the raw data and put it there.
My sample code:
Insert Raw Products to products table
$data = $this->getRawDistinct(); // raw data I used sql group by here and get 2000 data
$insertedID = [];
foreach (array_chunk($data, 1000) as $key => $values) {
foreach($values as $value) {
$this->db->insert("{$this->prefix}products", [
'name' => $value['product_title'],
'brand_name' => $value['brand_name'],
'brand_logo' => $value['brand_logo_image'],
'status' => $value['product_status'],
'keywords' => $value['keywords'],
]);
array_push($insertedData, $this->db->insert_id); // store inserted data id
}
}
Insert Raw Products to product_image and product_description
$processedProduct = $this->db->get_results(
"SELECT id, name " .
"FROM {$this->prefix}products " .
"WHERE id IN ( ".implode(",", $insertedData)." ) "); // Get the data of those inserted earlier
foreach ($processedProduct as $key => $value) {
$rawProducts = $this->db->get_results(
"SELECT * " .
"FROM {$this->prefix}raw_products " .
"WHERE product_title = '$value->name'");
if($rawProducts) {
foreach ($rawProducts as $rawProduct) {
//insert to product_image
$this->db->insert("{$this->prefix}product_images", [
'product_id' => $value->id,
'thumbnail' => $rawProduct->thumbnail_image,
'color_swatch' => $rawProduct->color_swatch_image,
'product_image' => $rawProduct->product_image,
'front_flat' => $rawProduct->front_flat,
'back_flat' => $rawProduct->back_flat,
'front_model' => $rawProduct->front_model,
'back_model' => $rawProduct->back_model,
'side_model' => $rawProduct->side_model,
'three_q_model' => $rawProduct->three_q_model,
'color_square_image' => $rawProduct->color_square_image,
'color_product_image' => $rawProduct->color_product_image,
]);
//insert to product_descriptions
$this->db->insert("{$this->prefix}product_descriptions", [
'product_id' => $value->id,
'size' => $rawProduct->size,
'piece_weight' => $rawProduct->piece_weight,
'piece_price' => $rawProduct->piece_price,
'dozen_price' => $rawProduct->dozen_price,
'case_price' => $rawProduct->case_price,
'piece_sale_price' => $rawProduct->piece_sale_price,
'dozen_sale_price' => $rawProduct->dozen_sale_price,
'case_sale_price' => $rawProduct->case_sale_price,
'inventory_key' => $rawProduct->inventory_key,
'size_index' => $rawProduct->size_index,
'catalog_color' => $rawProduct->catalog_color,
'price_code' => $rawProduct->price_code,
'catalog_color' => $rawProduct->catalog_color,
]);
}
}
}
As you can see at the code, I distinct the records at first and get 2k of data because those are the products that I need to put in the products tables.
Don't get consfused of 100k then it comes to 2k, the other records are duplicated because it creates another row for different sizes, color, images and etc which is pertaining to one product only. And those sizes, images I need it to insert in product_image and product_description which both should have 100k records.
Insert Raw Products to products table is working fine but the inserting the raw product to product_image and product_description it insert data but not all because I reach max time execution and I already increase that to 5mins but I don't like that because the user will just for 5mins and still not processing all the data. How can I optimize it and make it work? or even handle more larger data than I'm facing now. Thanks.
Let the database do all the work.
Assuming that products has an autoincrement id field,and that each product_title has only one brand name, brand logo, status, and keywords value:
INSERT INTO products
SELECT DISTINCT product_title name, brand_name, brand_logo_image brand_logo, product_status status, keywords
FROM raw_products
Now we should have a good products table with a product_id field (the autoincrement field). Now, we need to create the relational tables product_images and product_descriptions. (side note, do these have a 1:1 relationship with each other? For the sake of argument I'll assume they don't and you've established that they need to be separate)
INSERT INTO product_images
SELECT p.id product_id, r.thumbnail_image thumbnail, r.color_swatch_image color_swatch, r.product_image, r.front_flat, r.back_flat, r.front_model, r.back_model, r.side_model, r.three_q_model, r.color_square_image, r.color_produt_image
FROM raw_products r
LEFT JOIN products p ON r.product_title=p.name
The left join is the magic here. For each row of raw products, the db will attempt to join the left joined table on the matching conditions. If it finds a match, it joins it; if no match is found, it puts nulls for the table's fields. (In this case there will be no rows with null values for products.)
Using the second insert as an example, it should be trivial for you to craft the third insert.

MySQL stored procedure no insert ID returned?

I have a very simple query, not sure what I am doing wrong here.
My DB call is not receiving an insert id as I would expect it to.
Table:
Stored Procedure:
CREATE DEFINER=`root`#`localhost` PROCEDURE `addCustomerProduct`(IN in_customerID INT, in_productID INT)
BEGIN
INSERT INTO order_customer_product (customerID, productID, retailAmountAtPurchase, faceValue)
SELECT
in_customerID,
in_productID,
p.retail,
p.faceValue
FROM
products as p
WHERE
p.productID = in_productID;
END
PHP:
public function addProduct($data, $userID)
{
// Do we already have a pending order for this user?
$orderID = $this->doesOrderExist($userID);
// We had no order, lets create one
if (!$orderID) {
$orderID = $this->createOrder($userID);
}
/**
* Insert the customer product.
* This relates a denomination to a customer.
*/
$customerProductID = $this->addCustomerProduct($data);
// Add this customer product to the order
$this->addProductToOrder(array("customerProductID" => $customerProductID, "orderID" => $orderID));
// Return
return $customerProductID;
}
/**
* Description: Add a customer product / reward
* Page: client/add_reward
*/
public function addCustomerProduct($data){
$procedure = "CALL addCustomerProduct(?,?)";
$result = $this->db->query($procedure, $data);
return $this->db->insert_id();
}
The line with the issue is: $customerProductID = $this->addCustomerProduct($data);.
A new record is being inserted into the table and the table has a PK/AI. Data goes in fine but 0 is returned as the $customerProductID.
Will an insert from select statement not return an insert ID perhaps?
Update For #Ravi-
Update 2:
I created a separate method and hard coded the query and data being sent.
It adds the records fine, AI goes up, 0 is returned as the last id.
public function test(){
$procedure = "CALL addCustomerProduct(?,?)";
$result = $this->db->query($procedure, array("customerID" => 1, "productID" => 20));
echo $this->db->insert_id();
}
Also restarted the MySQL server to make sure there wasn't anything weird going on there.
Also, updated the SP to just insert random data into the table without using a select.
CREATE DEFINER=`root`#`localhost` PROCEDURE `addCustomerProduct`(IN in_customerID INT, in_productID INT)
BEGIN
INSERT INTO order_customer_product (customerID, productID, retailAmountAtPurchase, faceValue)
VALUES(8,2,'4.55',25);
END
Update 3:
Right after the insert, I am printing out the last query that was ran as well as the result. You will notice that there is 1 affected row (the insert is happening) but the insert_id is still 0.
CALL addCustomerProduct('8','33')
CI_DB_mysqli_result Object
(
[conn_id] => mysqli Object
(
[affected_rows] => 1
[client_info] => mysqlnd 5.0.12-dev - 20150407 - $Id: b396954eeb2d1d9ed7902b8bae237b287f21ad9e $
[client_version] => 50012
[connect_errno] => 0
[connect_error] =>
[errno] => 0
[error] =>
[error_list] => Array
(
)
[field_count] => 0
[host_info] => Localhost via UNIX socket
[info] =>
[insert_id] => 0
[server_info] => 5.6.35
[server_version] => 50635
[stat] => Uptime: 1637 Threads: 3 Questions: 508 Slow queries: 0 Opens: 113 Flush tables: 1 Open tables: 106 Queries per second avg: 0.310
[sqlstate] => 00000
[protocol_version] => 10
[thread_id] => 25
[warning_count] => 0
)
[result_id] => 1
[result_array] => Array
(
)
[result_object] => Array
(
)
[custom_result_object] => Array
(
)
[current_row] => 0
[num_rows] =>
[row_data] =>
)
Update 4:
From some of the research I have done, unless you use the mysqli method such as $this->db->insert(), it won't provide a last insert id back to you.
I am going to try and figure out Ravi's suggestion but it seems that code igniter doesn't allow the example that was shown. At least I know now that I am not crazy and its just not normal behavior unless you use the ``insert` method vs a stored procedure.
This answer may explain why your existing code doesn't work. To quote:
CodeIgniter's insert_id() will only return an ID of an insert().
Unless you are executing something like $this->db->insert('table', $data); before calling the function it will not be able to return an ID.
MySQL's LAST_INSERT_ID(); should help you here (assuming you have permission to alter the stored procedure definition). Change it to:
CREATE DEFINER=`root`#`localhost` PROCEDURE `addCustomerProduct`(
IN in_customerID INT, in_productID INT, OUT out_customerProductID INT)
BEGIN
INSERT INTO order_customer_product (
customerID, productID, retailAmountAtPurchase, faceValue)
VALUES(8,2,'4.55',25);
SELECT LAST_INSERT_ID() INTO out_customerProductID;
END
Then use something like the following to get the output parameter value:
public function addCustomerProduct($data) {
$procedure = "CALL addCustomerProduct("
. $this->db->escape($data["customerID"]).", "
. $this->db->escape($data["productID"]).", "
. "#customerProductID);"
$this->db->query($procedure);
$query = $this->db->query("SELECT #customerProductID AS customerProductID");
if($query->num_rows() > 0)
return $query->result()->customerProductID;
else
return NULL;
}
If the above doesn't work, try adding a $this->db->trans_start(); and $this->db->trans_complete(); before and after the stored procedure call to ensure the transaction is committed.
Ideally, following line should work
$this->db->insert_id;
But, I'm not sure why is not working, so I would suggest a workaround as following, recompile your procedure with additional parameter out_lastId, which will return last inserted id
CREATE DEFINER=`root`#`localhost` PROCEDURE `addCustomerProduct`(IN in_customerID INT, in_productID INT, OUT out_lastId INT)
And, after insert set the value with last inserted id.
SET out_lastId = LAST_INSERT_ID();
==Updated==
$this->db->multi_query( "CALL addCustomerProduct($data, #id);SELECT #id as id" );
$db->next_result(); // flush the null RS from the call
$rs=$this->db->store_result(); // get the RS containing the id
echo $rs->fetch_object()->id, "\n";
$rs->free();
Why
insert_id() will only workes with Query Builder and Queries only. SP's are used to call with $this->db->query() but it won't retuns data insert_id().
How
Before the End of SP add SELECT MAX(id) FROM order_customer_product;. So this will return the last ID to your code.
Suggestion
As I see there is an Insert query to DB. If I use similar case will use normal Query Builder or/and will warp it with Codeigniter Transactions(My answer on another question).

Using array in where clause, where the checked field contains coma separated words

Please, read the question carefully before you mark it as duplicate. It is not.
I have a table named questions, and I have field questionMetaTags in that table which accepts coma separated values (keywords)
Here is my table:
`questionID` int(11) NOT NULL AUTO_INCREMENT,
`questioncategoryID` int(11) NOT NULL,
`questionstatusID` int(11) NOT NULL,
`organizationID` int(11) NOT NULL,
`legalformID` int(11) DEFAULT NULL,
`questionProtocolID` varchar(45) DEFAULT NULL,
`questionDisplayedRecordID` int(11) NOT NULL,
`questionTitle` text NOT NULL,
`questionSummary` text,
`questionText` longtext NOT NULL,
`questionAnswerSummary` text,
`questionAnswerText` longtext,
`questionMetaTags` text,
`questionAskedBy` int(11) NOT NULL,
`questionAnsweredBy` int(11) DEFAULT NULL,
`questionAskedOnDate` datetime NOT NULL,
`questionAnsweredOnDate` datetime DEFAULT NULL,
`questionAskedFromIp` varchar(255) NOT NULL
I am trying to create a query which will "produce" related questions. I am displaying one specific question on my page, and I want bellow to display only the questions that:
1. Belong to same category
AND
2. Have at least 2 or more same keywords
In those 2 variables I am holding the category and the keywords of the displayed question, and I would now like to build the query which will display "related" questions.
$keywordsInQurrentQuestion (array that holds the keywords)
$questioncategoryID (category of the question)
I am trying with something like:
SELECT *
FROM question WHERE `questionstatusID` = 5
AND questioncategoryID = $questioncategoryID
// I have no idea how to check if the question have at least 2 keywords that exists in the $keywordsInQurrentQuestion array
");
I have found answers on how to use array in where clause since I want to check for at least 2 same keywords, those answers are not helpful to me.
Your database schema is not really made for that kind of queries. MySQL is a relational database after all.
Solution in MySQL (change of DB schema required)
It would be better to have an extra table for just keywords (or meta tags):
ID tag
1 oranges
2 apples
... ...
154 motor oil
A second table would then hold the relations between questions and tags:
questionID tagID
1 1
1 2
1 37
2 3
... ...
18 102
19 154
Now, when querying for a similar/related question, you can do so by checking your minimum of identical tags. First, a subquery is used to get all tags from your displayed question:
SELECT tagID FROM relations WHERE questionID = 123;
This will then be used in the actual query that retrieves the similar questions:
SELECT DISTINCT(r1.questionID)
FROM relation r1
INNER JOIN relation r2
ON r1.questionID = r2.questionID AND NOT r1.tagID = r2.tagID
WHERE r1.tagID IN (SELECT tagID FROM relations WHERE questionID = 123)
AND r2.tagID IN (SELECT tagID FROM relations WHERE questionID = 123)
This query joins the relation table with itself (selfjoin), looking for rows with an identical question ID but different tag IDs. In the WHERE clause it checks if both joined tags are within the set of the original question. Finally, it will remove all duplicates (DISTINCT) and give you a list of related question IDs.
This query still has to be expanded to check for the category, this has been left out to make the statement clear.
Solution in PHP
According to your comment, you do not want to change the DB schema. Although retrieving all questions from the database first and then get the related ones in PHP might not be the best choice in terms of execution time and performance, it is still possible.
Assuming you have all terms of your original question in some value
$meta_tags = "oranges,apples,avocados,olive oil";
and all your questions from the same category in an array containing at least the question ID and the respective keywords
$questions = array(
0 => array(
"questionID" => 1,
"metaTags" => "apples,olive oil",
),
1 => array(
"questionID" => 2,
"metaTags" => "oranges,motor oil",
),
2 => array(
"questionID" => 3,
"metaTags" => "oranges,avocados",
),
...
);
you can loop through your questions, check if at least two keywords are identical and then store the question IDs in some array:
function check_tags($needles, $haystack) {
$original_tags = explode(',', $haystack);
$original_tags = array_map('trim', $original_tags);
$tags_to_check = explode(',', $needles);
$tags_to_check = array_map('trim', $tags_to_check);
$count = 0;
foreach ($tags_to_check as $t) {
if (in_array($t, $original_tags)) {
count++;
}
}
return $count;
}
$related = array();
foreach ($questions as $q) {
if (check_tags($q['metaTags'], $meta_tags) >= 2) {
$related[] = $q['questionID'];
}
}
The array $related now contains all question IDs which are similar to your original question.

Cakephp find returning null values for associated model

I have two models A and B. Relationships are defined as mentioned below.
A hasOne B
B belongsTo A
Everything was working great till a few days back. In last couple of days something has changes and now when I do a find on A (with recursive set to 2), it returns the values of B but all values are returned as NULL including the ID of B.
A (id - 1220, other_fields)
B (id - 11, a_id - 1220, other_fields)
Resulting array looks like this.
array(
'A' => array(
'id' => 1220,
'field1' => 'dsdsa',
...
)
'B' => array(
'id' => null,
'a_id' => null,
...
)
)
I have verified in database that row for B exists corresponding to A. I even tried executing the Host find query (copied from SQL debug dump) and it returns the results correctly so nothing wrong at database level. It's cake (or myself) which is messing up with the data.
It was all working fine but something has gone wrong and I am having hard time trying to figure out what.
Here is the query (replace Host with A and Service with B)
SELECT `Host`.`id`, `Host`.`user_id`, `Host`.`title`, `Host`.`featured`, `Host`.`slug`, `Host`.`profile_type`, `Host`.`social_security_number`, `Host`.`biz_id`, `Host`.`address`, `Host`.`lat`, `Host`.`lon`, `Host`.`description`, `Host`.`type`, `Host`.`size`, `Host`.`unavailability`, `Host`.`kids`, `Host`.`supervision`, `Host`.`emergency_transport`, `Host`.`experience`, `Host`.`first_aid`, `Host`.`oral_medication`, `Host`.`injected_medication`, `Host`.`payment_mode`, `Host`.`paypal_email`, `Host`.`name_on_check`, `Host`.`payment_address`, `Host`.`bank_name`, `Host`.`bank_account_number`, `Host`.`balance`, `Host`.`pending_balance`, `Host`.`approved`, `Host`.`created`, `Host`.`modified`, `Service`.`id`, `Service`.`host_id`, `Service`.`day_care`, `Service`.`day_care_price`, `Service`.`day_night_care`, `Service`.`day_night_care_price`, `Service`.`walking`, `Service`.`walking_price`, `Service`.`walking_max_distance`, `Service`.`checkup_visit`, `Service`.`checkup_visit_price`, `Service`.`checkup_visit_max_distance`, `Service`.`grooming`, `Service`.`grooming_price`, `Service`.`bathing`, `Service`.`bathing_price`, `Service`.`pickndrop`, `Service`.`pickndrop_price`, `Service`.`training`, `Service`.`training_price`, `Service`.`cancellation_policy`, `Service`.`accept_last_min_bookings`, `Service`.`created`, `Service`.`modified` FROM `hosts` AS `Host` LEFT JOIN `services` AS `Service` ON (`Service`.`host_id` = `Host`.`id`) WHERE `Host`.`id` = 2569 ORDER BY `Host`.`created` DESC LIMIT 1

cakephp complex find query example

I would like to perform a find('all') query on a table in a CakePHP application with the following conditions:
1- The primary key value is equal 17
AND
2- The value of another column my_column_id in the table is the MAX of all the values of the column my_column_id but less than the value of my_column_id of the datum whose primary key value is equal to 17
AND
3- The value of another column my_column_id in the table is the MIN of all the values of the column my_column_id but greater than the value of my_column_id of the datum whose primary key value is equal to 17
So the above query should give me 3 results.
Is it possible to get all this in one single query? or do I need three separate queries?
I tried something like:
$results = $this->Model->find('all', array(
'conditions' => array(
'MAX(Model.my_column_id) <' => 23,
'Mddel.id' => 17,
'MIN(Model.my_column_id) >' => 23
)
));
23 represents the value of my_column_id of the datum whose primary key is equal to 17
But that gives me the following error: SQL Error: 1111: Invalid use of group function
Any help please.
Often times when I have complex queries I'll just write out the SQL. I can't speak for the speed and I'm not sure if it's frowned upon, but you can try giving this a shot.
$this->Model->query('
SELECT MAX(table.column),
MIN(table.column)
FROM table
WHERE table.id = '$id'
');
Also, if this controller is outside of the model make sure to load the correct model (place it above the query)
$this->loadModel('Model');
You can't use MySQL aggregate functions (MAX, MIN etc) in your where clause.
I think the easiest way will be to explicitly include the GROUP BY and use HAVING, something like this:
$this->Model->find('all', array(
'group' => 'Model.some_field HAVING MAX(Model.some_field) > 23
AND MIN(some_field) < 23'
));
(although that doesn't make sense, I don't think!)*
That's something like what you want.
Toby
Update
[*] By which I mean, I don't think it makes sense to be querying both greater than and less than a value!
You can do it by passing conditions array inside find.
Example
$this->Model->find('all',array
(
'conditions' => array
(
'Model.field' => 17,
'MAX(Model.field) < ' => 17,
'MIN(Model.field) > ' => 17
)
))
OP Comment Response
SQL Error: 1305: FUNCTION NAX does not exist
Solution: Remove all spaces between the function and the parenthesis or set sql_mode="IGNORE_SPACE";
This is causing the error:
SELECT MAX (id) as something FROM Example
This will work correctly:
SELECT MAX(id) as something FROM Example

Categories