PDO update statement not updating record - mysql

I was just wondering if I could get some pointers as to where I may be going wrong.
I have been using mysql statements and I am in the process of switching to PDO statements to use with MySQL.
I have been able to get my head around SELECT statements, but I am having a bit of trouble trying to get the insert statement to work.
I have been Googling and tried a couple of different ways to get this to work but to no avail.
This is what I have so far:
$sqlu = $conn->prepare("UPDATE ".PERSON." p
JOIN contact c ON c.personID = p.adbkid
JOIN address a ON a.personID = p.adbkid
JOIN misc m ON m.personID = p.adbkid
JOIN variables v ON v.personID = p.adbkid
SET lastname = :ln
WHERE p.pid = :id");
$sqlu->bindParam(':ln', $ln, PDO::PARAM_STR);
$sqlu->bindParam(':id', $id, PDO::PARAM_STR);
$sqlu->execute();
I have also tried it without using bindParam and using as follows:
$sqlu->execute(array('ln' => $ln, 'id' => $id));
I have also used a '?' instead of ':' and then bound the parameter or used it in the array.
When I hit the update button, I have echoed the query so I can see what is being passed through and this is what I get:
PDOStatement Object ( [queryString] => UPDATE person p JOIN contact c ON c.personID = p.adbkid JOIN address a ON a.personID = p.adbkid JOIN misc m ON m.personID = p.adbkid JOIN variables v ON v.personID = p.adbkid SET lastname = :ln WHERE p.pid = :id ) 1
I just can't see where I am going wrong. Like I say, I have Googled this and come across some answers on here too and I seem to be stuck as to where to go next.
This is a personal project I am working on and I am not looking for someone to figure this out for me, I am just looking for some pointers so I can try to fix and learn myself.
Thanks in advance.

One possibility is that the records do not have matching records in all the tables. You could try using left join. But why are you doing joins at all? Does this work?
UPDATE ".PERSON." p
SET lastname = :ln
WHERE p.pid = :id;
This assumes that lastname is in the Person table, but that seems like a reasonable assumption.

I have gone through everything and it would appear that the echoing of the query is showing the query with the placeholders and it is actually updating the database.

Related

How to use EXIST to improve speed in mySQL subquery?

I am stuck with this query below. It takes forever to complete the query.
I have been using describe to help me find best way to pull this data. But this is what I can come up with so far. But this take forever to pull just 1000 records . I would like to get some recommendations / suggestions on how to approve this issue ? I'm stuck.
I have read about "EXISTS" that I can use to improve subquery , but I am not quite understand how it would work in this situation.
I have already used primary keys as much as I could ( id , contact_id , flag_if ) => those ending with _id are all primary keys.
Thanks.
SELECT ac.id,
acc.category,
ac.value,
fg.*
FROM contact AS ac
JOIN application AS app ON app.id = ac.id
JOIN category AS acc USING (contact_id)
LEFT JOIN (SELECT af.name_short
,att.value
,att.flag_id
FROM attribute AS att
LEFT JOIN attribute_flag AS af ON af.flag_id = att.flag_id
LEFT JOIN application app on
( (att.level = 'customer' AND att.related_id = app.customer_id)
OR (att.level = 'application' and att.related_id = app.id)
)
WHERE app.id IN (001,002,003)
) fg on ac.value = fg.value
WHERE ac.id IN (001,002,003)
== UPDATE ==
here is the result from explain query'

Joomla 2.5 joining 3 tables

I am trying to join 3 tables and store the result.
The problem is I simply cannot seem to deal with it. Hours of Googling and searching led to nothing.
Here is the query:
$db = JFactory::getDBO();
$item_kit = array();
$q = "SELECT p.virtuemart_product_id,
pr.product_price,
pr.virtuemard_product_id,
pb.group_id
FROM #_virtuemart_products p
INNER JOIN #_virtuemart_product_prices pr ON p.virtuemart_product_id = pr.virtuemart_product_id,
INNER JOIN #_pb_group_vm_prod_xref pb ON pb.vm_product_id = pr.virtuemart_product_id";
$db->setQuery($q);
$item_kit = $db->loadObjectList();
return $item_kit;
Please notice that I am using the ID as the common element in all tables.
After all this nonsense occurs it should so something like:
if(!empty($item_kit['group_id'])){
echo 'It works';
}
Debugging resulted in discovering an SQL error near 'INNER JOIN #_virtuemart_product_prices pr ON p.virtuemart_product_id = pr.virtu...'
Can you please help me? Or, at least, point out where am I doing stupidly wrong...
I know it's something small and easy, but I can't see it...
Found the problem!
pr.virtuemard_product_id to pr.virtuemart_product_id
and I had to manually type in the database table prefix.
What silly of me. Thank you all for your time reading this question.
Regards,
Vlad
Remove the comma at the end of the first inner join line
INNER JOIN #_virtuemart_product_prices ... pr.virtuemart_product_id,
it should become
INNER JOIN #_virtuemart_product_prices ... pr.virtuemart_product_id

INNER JOIN Results from Select Statement using Doctrine QueryBuilder

Can you use Doctrine QueryBuilder to INNER JOIN a temporary table from a full SELECT statement that includes a GROUP BY?
The ultimate goal is to select the best version of a record. I have a viewVersion table that has multiple versions with the same viewId value but different timeMod. I want to find the version with the latest timeMod (and do a lot of other complex joins and filters on the query).
Initially people assume you can do a GROUP BY viewId and then ORDER BY timeMod, but ORDER BY has no effect on GROUP BY, and MySQL will return random results. There are a ton of answers out there (e.g. here) that explain the problem with using GROUP and offer a solution, but I am having trouble interpreting the Doctrine docs to find a way to implement the SQL with Doctrine QueryBuilder (if it's even possible). Why don't I just use DQL? I may have to, but I have a lot of dynamic filters and joins that are much easier to do with QueryBuilder, so I wanted to see if that's possible.
Sample MySQL to Reproduce in Doctrine QueryBuilder
SELECT vv.*
FROM view_version vv
#inner join only returns where the result sets overlap, i.e. one record
INNER JOIN (
SELECT MAX(timeMod) maxTimeMod, viewId
FROM view_version
GROUP BY viewId
) version ON version.viewId = vv.viewId AND vv.timeMod = version.maxTimeMod
#join other tables for filter, etc
INNER JOIN view v ON v.id = vv.viewId
INNER JOIN content_type c ON c.id = v.contentTypeId
WHERE vv.siteId=1
AND v.contentTypeId IN (2)
ORDER BY vv.title ASC;
Theoretical Solution via Query Builder (not working)
I am thinking that the JOIN needs to inject a DQL statement, e.g.
$em = $this->getDoctrine()->getManager();
$viewVersionRepo = $em->getRepository('GutensiteCmsBundle:View\ViewVersion');
$queryMax = $viewVersionRepo->createQueryBuilder()
->addSelect('MAX(timeMod) AS timeModMax')
->addSelect('viewId')
->groupBy('viewId');
$queryBuilder = $viewVersionRepo->createQueryBuilder('vv')
// I tried putting the query in a parenthesis, to no avail
->join('('.$queryMax->getDQL().')', 'version', 'WITH', 'vv.viewId = version.viewId AND vv.timeMod = version.timeModMax')
// Join other Entities
->join('e.view', 'view')
->addSelect('view')
->join('view.contentType', 'contentType')
->addSelect('contentType')
// Perform random filters
->andWhere('vv.siteId = :siteId')->setParameter('siteId', 1)
->andWhere('view.contentTypeId IN(:contentTypeId)')->setParameter('contentTypeId', $contentTypeIds)
->addOrderBy('e.title', 'ASC');
$query = $queryBuilder->getQuery();
$results = $query->getResult();
My code (which may not match the above example perfectly) outputs:
SELECT e, view, contentType
FROM Gutensite\CmsBundle\Entity\View\ViewVersion e
INNER JOIN (
SELECT MAX(v.timeMod) AS timeModMax, v.viewId
FROM Gutensite\CmsBundle\Entity\View\ViewVersion v
GROUP BY v.viewId
) version WITH vv.viewId = version.viewId AND vv.timeMod = version.timeModMax
INNER JOIN e.view view
INNER JOIN view.contentType contentType
WHERE e.siteId = :siteId
AND view.contentTypeId IN (:contentTypeId)
ORDER BY e.title ASC
This Answer seems to indicate that it's possible in other contexts like IN statements, but when I try the above method in the JOIN, I get the error:
[Semantical Error] line 0, col 90 near '(SELECT MAX(v.timeMod)': Error: Class '(' is not defined.
A big thanks to #AdrienCarniero for his alternative query structure for sorting the highest version with a simple JOIN where the entity's timeMod is less than the joined table timeMod.
Alternative Query
SELECT view_version.*
FROM view_version
#inner join to get the best version
LEFT JOIN view_version AS best_version ON best_version.viewId = view_version.viewId AND best_version.timeMod > view_version.timeMod
#join other tables for filter, etc
INNER JOIN view ON view.id = view_version.viewId
INNER JOIN content_type ON content_type.id = view.contentTypeId
WHERE view_version.siteId=1
# LIMIT Best Version
AND best_version.timeMod IS NULL
AND view.contentTypeId IN (2)
ORDER BY view_version.title ASC;
Using Doctrine QueryBuilder
$em = $this->getDoctrine()->getManager();
$viewVersionRepo = $em->getRepository('GutensiteCmsBundle:View\ViewVersion');
$queryBuilder = $viewVersionRepo->createQueryBuilder('vv')
// Join Best Version
->leftJoin('GutensiteCmsBundle:View\ViewVersion', 'bestVersion', 'WITH', 'bestVersion.viewId = e.viewId AND bestVersion.timeMod > e.timeMod')
// Join other Entities
->join('e.view', 'view')
->addSelect('view')
->join('view.contentType', 'contentType')
->addSelect('contentType')
// Perform random filters
->andWhere('vv.siteId = :siteId')->setParameter('siteId', 1)
// LIMIT Joined Best Version
->andWhere('bestVersion.timeMod IS NULL')
->andWhere('view.contentTypeId IN(:contentTypeId)')->setParameter('contentTypeId', $contentTypeIds)
->addOrderBy('e.title', 'ASC');
$query = $queryBuilder->getQuery();
$results = $query->getResult();
In terms of performance, it really depends on the dataset. See this discussion for details.
TIP: The table should include indexes on both these values (viewId and timeMod) to speed up results. I don't know if it would also benefit from a single index on both fields.
A native SQL query using the original JOIN method may be better in some cases, but compiling the query over an extended range of code that dynamically creates it, and getting the mappings correct is a pain. So this is at least an alternative solution that I hope helps others.

Find products with duplicate attribute value

I am trying to find out products with duplicate values on the basis of any specific attribute. Suppose I have an attribute MPN, and have to get all the products which are sharing the same MPN. I have designed a query which partially works but I found that it calls few products that were unique with attribute value.
select e.entity_id as ID,n.value as name,e.sku as sku,m.value as mpn from `catalog_product_entity` as e
left join `catalog_product_entity_varchar` as m
on e.entity_id = m.entity_id and m.attribute_id=156
left join `catalog_product_entity_varchar` as n
on e.entity_id = n.entity_id and n.attribute_id=71
group by m.value having count(*)> 1 order by e.entity_id asc
seems my logic is not fair enough to get what i want.
Any database Guru to help me out?
I have resolved this issue by applying another query to filter out the results. this may not be a good solution but it helped me out.
after getting results from above query, I used a foreach loop to find if the attribute value is unique or not.
foreach($QueryCollection as $data){
$query1 = "select * from catalog_product_entity_varchar as cpev where cpev.attribute_id=156 and cpev.value='".$data['mpn']."'";
$IsDuplicate = $_conn2->fetchAll($query1);
if(count($IsDuplicate)>1){
//found this as duplicate
}
}
I have very less products around 200, so it was Ok for me, but I guess this is not good for higher counts.

multiple left join's issue

$sth = $db->prepare("SELECT tblCompanies.*, users.Username, cargo.Name, (tblCompanies.Money + SUM(tblCTerminals.sellValue)) as assets, (COUNT(tblFinishedContracts.ID) + COUNT(tblFinishedSubContracts.ID)) as completed FROM ((((tblCompanies LEFT JOIN users ON tblCompanies.CompanyCEO = users.ID) LEFT JOIN cargo ON (tblCompanies.PreferredCargo = cargo.Cargo_ID)) LEFT JOIN tblCTerminals ON (tblCompanies.Company_ID = tblCTerminals.companyID)) LEFT JOIN tblFinishedContracts ON (tblCompanies.Company_ID = tblFinishedContracts.companyID)) LEFT JOIN tblFinishedSubContracts ON (tblCompanies.Company_ID = tblFinishedSubContracts.companyID) WHERE (users.Username LIKE :info || tblCompanies.CompanyName LIKE :info2 || CONCAT('$',FORMAT((tblCompanies.Money + SUM(tblCTerminals.sellValue)),2)) LIKE :info3 || CONCAT('$',FORMAT(tblCompanies.Money,2)) LIKE :info4 || cargo.Name LIKE :info5 || users.pLevel LIKE :info6 || CONCAT('$',FORMAT((users.Cash_In_Bank + users.Cash_In_Hand),2)) LIKE :info7)");
$sth->bindValue(':info', '%'.$_GET['sSearch'].'%', PDO::PARAM_STR);
$sth->bindValue(':info2', '%'.$_GET['sSearch'].'%', PDO::PARAM_STR);
$sth->bindValue(':info3', '%'.$_GET['sSearch'].'%', PDO::PARAM_STR);
$sth->bindValue(':info4', '%'.$_GET['sSearch'].'%', PDO::PARAM_STR);
$sth->bindValue(':info5', '%'.$_GET['sSearch'].'%', PDO::PARAM_STR);
$sth->bindValue(':info6', '%'.$_GET['sSearch'].'%', PDO::PARAM_STR);
$sth->bindValue(':info7', '%'.$_GET['sSearch'].'%', PDO::PARAM_STR);
Ok so now my reasoning for this mess:
I'm using the jQuery Datatables plugin and when it does a request to the server it sends the query. It expects back the results, how many results in the search query and how many total results. With this query I can return the results.
However on COUNT(tblFinishedContracts.ID) it is grabbing the table 8 times. So for example instead of returning the correct amount of 8 it is returning a count of 112.
When I do:
SELECT COUNT(tblFinishedContracts.ID)
FROM tblCompanies
LEFT JOIN tblFinishedContract
ON (tblCompanies.Company_ID = tblFinishedContracts.ID)
WHERE tblCompanies.Company_ID = 11
It works, it returns 8. If someone knows a better way of doing what I'm trying to accomplish or can tell me how to fix the issue, it would be greatly appreciated!
Thanks,
Jeff
EDIT: to add, no columns have duplicate names everything is different.
There's no single silver bullet for this issue; getting information about a parent table, while also getting information from multiple child tables with one-to-many relationships, is always tricky. But there are a few techniques that can help.
Firstly, you don't actually do anything with tblFinishedContracts except get a count of records from it; so instead of writing COUNT(tblFinishedContracts.ID) and using a join, you can use a subquery: write (SELECT COUNT(1) FROM tblFinishedContracts WHERE Company_ID = tblCompanies.Company_ID). The same thing applies to tblFinishedSubContracts, so you can eliminate that join as well. (By the way, eliminating these joins should also improve your performance significantly, since you'll only need to retrieve information about contracts that belong to companies that are actually being returned.)
That takes care of most of the problem. The joins to users and cargo to get the CEO and preferred cargo shouldn't cause any problems. (Right?)
The only remaining difficulty is tblCTerminals, since SUM(tblCTerminals.sellValue) field occurs both in the field list and in the WHERE clause. There's no really compelling way to handle it. One option is to use the above-mentioned subquery approach, with two copies of the subquery. Another is to replace FROM tblCompanies with FROM (SELECT tblCompanies.*, COALESCE(SUM(tblCTerminals.sellValue), 0) AS totalCTerminalSellValue FROM tblCompanies LEFT JOIN tblCTerminals ON tblCTerminals.Company_ID = tblCompanies.Company_ID GROUP BY tblCompanies.Company_ID) AS tblCompanies, which in essence creates a temporary copy of tblCompanies that's augmented with a new field named totalCTerminalSellValue. So, elsewhere in the query, you can replace SUM(tblCTerminals.sellValue) with tblCompanies.totalCTerminalSellValue.