multiple left join's issue - mysql

$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.

Related

ID lost after left join on table

Hey I tried this thread but it doesn't work and i can't figure out why...
here's my SQL:
SELECT * FROM gone_items
LEFT JOIN items
ON gone_items.item_ID=items.ID
WHERE
gone_items.aus_ID='$ID'
ORDER BY items.name ASC
Now, I fetch that via PHP and have a $row and try another mysql to get the individual ID's of the gone_items table. But if i use $row['ID'] I get the ID of the items.ID not the one from gone_items.ID.
I tried setting the variable manually in the first query but it doesn't work.
I also tried this: MYSQL Left join A.table and b.table while retaining a.table id
Also didn't help me...
All I want is to retain the ID (Primary key) from the gone_items table..
Can anyone please tell me what I'm doing wrong ?
Love
Gram
EDIT
//Query for Joined infos
$sqlx="SELECT foto_res_ausgeliehene_geg.ID, foto_res_ausgeliehene_geg.aus_ID, foto_res_ausgeliehene_geg.geg_ID, foto_res_ausgeliehene_geg.zusaetzliches, foto_res_gegenstaende.ID, foto_res_gegenstaende.bezeichnung, foto_res_gegenstaende.seriennummer, foto_res_gegenstaende.interne_seriennummer, foto_res_gegenstaende.zusaetzliches FROM foto_res_ausgeliehene_geg
LEFT JOIN foto_res_gegenstaende
ON foto_res_ausgeliehene_geg.geg_ID=foto_res_gegenstaende.ID
WHERE
foto_res_ausgeliehene_geg.aus_ID='$ID'
ORDER BY foto_res_gegenstaende.bezeichnung ASC
";
$ergebnisx = mysqli_query($db,$sqlx);
while ($zeilex = mysqli_fetch_assoc($ergebnisx))
{
//Query for individual infos
$sqly="SELECT * FROM foto_res_ausgeliehene_geg
WHERE `geg_ID`='".$zeilex['ID']."'
AND `aus_ID`='$ID'
GROUP BY `geg_ID`
";
$ergebnisy = mysqli_query($db,$sqly);
while ($zeiley = mysqli_fetch_assoc($ergebnisy))
{};
Now I did select all items individually. The foto_res_ausgeliehene_geg.ID still merges with the foto_res_gegenstanede.ID due to the LEFT JOIN.
So if i access $zeilex['ID'] im getting the ID of foto_res_gegenstaende.ID.
Would it help if I rename the ID field in one of the tables into lets say item_ID ?
Thanks alot.
Love
Gram.
Instead of using select *, you should explicitly state what items you want to select. Else you can get conflicts with multiple id fields. In your case something like:
select gone_items.id, gone_items.column1, gone_items.column2, items.column1, items.column2
It is also considered good practice, to limit the amount of data there is being selected. But is meanwhile also a highly debateable what is the right way. Performance issue in using SELECT *?
WORKS!
I simply renamed one of the Primary ID keys to something else, in this case, one of them got ID -> item_ID. The other one still is ID that way the left join won't merge them.
yolo
EDIT
WORKING CODE
$sqlx="SELECT foto_res_ausgeliehene_geg.item_ID, foto_res_ausgeliehene_geg.aus_ID, foto_res_ausgeliehene_geg.geg_ID, foto_res_ausgeliehene_geg.zusaetzliches, foto_res_gegenstaende.ID, foto_res_gegenstaende.bezeichnung, foto_res_gegenstaende.seriennummer, foto_res_gegenstaende.interne_seriennummer, foto_res_gegenstaende.zusaetzliches FROM foto_res_ausgeliehene_geg
LEFT JOIN foto_res_gegenstaende
ON foto_res_ausgeliehene_geg.geg_ID=foto_res_gegenstaende.ID
WHERE
foto_res_ausgeliehene_geg.aus_ID='$ID'
ORDER BY foto_res_gegenstaende.bezeichnung ASC
";
$ergebnisx = mysqli_query($db,$sqlx);
while ($zeilex = mysqli_fetch_assoc($ergebnisx))
{
//Query for individual infos
$sqly="SELECT * FROM foto_res_ausgeliehene_geg
WHERE `item_ID`='".$zeilex['item_ID']."'
";
$ergebnisy = mysqli_query($db,$sqly);
while ($zeiley = mysqli_fetch_assoc($ergebnisy))
{

PDO update statement not updating record

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.

Combining these MySQL queries

I am learning MySQL and messing about with joins. I am wondering if I can cut these queries down and combine them. This is what I would NORMALLY do:
$stmt = $db_pdo->prepare('SELECT date, table2_id, comments
FROM table1
WHERE user = "victor"');
$stmt->execute();
foreach ($stmt as $row) {
$table2_id = $row['table2_id'];
}
$stmt = $db_pdo->prepare('SELECT table3_id
FROM table2
WHERE table2_id = '.$table2_id.'');
$stmt->execute();
foreach ($stmt as $row) {
$table3_id = $row['table3_id'];
}
$stmt = $db_pdo->prepare('SELECT previous_comments_count
FROM table3
WHERE table3_id = '.$table3_id.'');
$stmt->execute();
foreach ($stmt as $row) {
$table3_id = $row['table3_id'];
}
(I changed the variables, so it looks stupid on purpose--I did not design the table for this company)
Basically, I need to get most of my information from the first table, but there is one "id" that needs to cross two tables that I would like to put into the first query.
So what I tried was:
$stmt = $db_pdo->prepare('SELECT date, table2_id, comments
FROM table1
LEFT JOIN table2
ON table1.table2_id = table2.table2_id
LEFT JOIN table3
ON table2.table3_id = table3.table3_id
WHERE user = "victor"');
And obviously my first (and many tweaked versions of this line) have failed, leading to a blank fetch.
I can do the first left join, but even that doesn't seem to show the information from the second table. The second and third tables do not have the same columns as the first table. (I don't think this is a problem but I could be wrong.)
I've done some reading on various websites, and though I love figuring out my problems on my own, I think it's best to ask if what I'm trying to accomplish is even possible and to try and get it done ASAP. I've read some other questions like this, but I am just simply befuddled looking at the complexity of this let alone those ones. (It's one of those days...)
It could be that I'm also using pdo and prepared statements for the first time as well, I do NOT think that's the problem, but I apologize if it is. I'm not a MySQL genius and have not spent much time with databases.
Also (side question, if permitted): Do I have to use foreach? Or can I just trust that calling $stmt after the execute is good enough? (AKA passes true/false on the ONE result that should show up)
Try the joins like this:
table3
LEFT JOIN table2
ON table3.table3_id = table2.table3_id
LEFT JOIN table1
ON table2.table2_id = table1.table2_id

Performing Join with Multiple Criteria in Propel 1.5

This question follows on from the questions here and here.
I have recently upgraded to Propel 1.5, and have started using it's Query features over Criteria. I have a query I cannot translate, however - a left join with multiple criteria:
SELECT * FROM person
LEFT JOIN group_membership ON
person.id = group_membership.person_id
AND group_id = 1
WHERE group_membership.person_id is null;
Its aim is to find all people not in the specified group. Previously I was using the following code to accomplish this:
$criteria->addJoin(array(
self::ID,
GroupMembershipPeer::GROUP_ID,
), array(
GroupMembershipPeer::PERSON_ID,
$group_id,
),
Criteria::LEFT_JOIN);
$criteria->add(GroupMembershipPeer::PERSON_ID, null, Criteria::EQUAL);
I considered performing a query for all people in that group, getting the primary keys and adding a NOT IN on the array, but there didn't seem a particularly easy way to get the primary keys from a find, and it didn't seem very elegant.
An article on codenugget.org details how to add extra criteria to a join, which I attempted:
$result = $this->leftJoin('GroupMembership');
$result->getJoin('GroupMembership')
->addCondition(GroupMembershipPeer::GROUP_ID, $group->getId());
return $result
->useGroupMembershipQuery()
->filterByPersonId(null)
->endUse();
Unfortunately, the 'useGroupMembershipQuery' overrides the left join. To solve this, I tried the following code:
$result = $this
->useGroupMembershipQuery('GroupMembership', Criteria::LEFT_JOIN)
->filterByPersonId(null)
->endUse();
$result->getJoin('GroupMembership')
->addCondition(GroupMembershipPeer::GROUP_ID, $group->getId());
return $tmp;
For some reason this results in a cross join being performed for some reason:
SELECT * FROM `person`
CROSS JOIN `group_membership`
LEFT JOIN group_membership GroupMembership ON
(person.ID=GroupMembership.PERSON_ID
AND group_membership.GROUP_ID=3)
WHERE group_membership.PERSON_ID IS NULL
Does anyone know why this might be doing this, or how one might perform this join successfully in Propel 1.5, without having to resort to Criteria, again?
Propel 1.6 supports multiple criteria on joins with addJoinCondition(). If you update the Symfony plugin, or move to sfPropelORMPlugin, you can take advantage of that. The query can then be written like this:
return $this
->leftJoin('GroupMembership')
->addJoinCondition('GroupMembership', 'GroupMembership.GroupId = ?', $group->getId())
->where('GroupMembership.PersonId IS NULL');

What's wrong with this mySQL query?

Consider following two tables:
tag_names (tag_id, tag_name)
tag_links (tag_id, image_id)
An image can have multiple tags, I want to select all tags for a specific image id.
I am trying following query, but it doesnt seem to select correctly (selects only one row), What is wrong with it?
SELECT tag_name
FROM tag_names
LEFT JOIN tag_links.tag_id = tag_names.tag_id
WHERE tag_links.image_id = $image_id
Edit: I'm using CodeIgniter Active record query, but I wrote in basic SQL format so that if someone is not fimiliar with CodeIgniter can help. However, this query works fine with simple mysql format (without using CodeIgniter) but strangely does not work with CodeIgniter, even there is no any problem with the syntax, it just selects one row.
Here is CodeIgniter Syntax:
$this->db->select('tag_name');
$this->db->from('tag_names');
$this->db->join('tag_links', 'tag_links.tag_id = tag_names.tag_id', 'left');
$this -> db -> where('tag_links.image_id', (int)$image_id);
$query = $this->db->get();
Try this:
SELECT tag_name
FROM tag_names
LEFT JOIN tag_links
ON tag_links.tag_id = tag_names.tag_id
WHERE tag_links.image_id = $image_id
IMHO you forgot to join table (properly with ON statement) you are using.
EDIT: I have 2 ideas how to get rid of the problem:
First:
Change the line with SELECT
$this->db->select('tag_names.tag_name');
Second:
Use select() function with complete query:
$this->db->select($query, false);
$this->db->select() accepts an optional second parameter. If you set
it to FALSE, CodeIgniter will not try to protect your field or table
names with backticks. This is useful if you need a compound select
statement.
from: http://codeigniter.com/user_guide/database/active_record.html#select
It seems that you have a syntax error (you forgot tag_links in JOIN clause). By the way in my opinion you don't need LEFT JOIN for this purpose otherwise you may get incorrect results.
SELECT tag_name
FROM
tag_names
JOIN tag_links ON tag_links.tag_id = tag_names.tag_id
WHERE tag_links.image_id = $image_id
SELECT tag_names.tag_name
FROM tag_links
LEFT JOIN tag_names.tag_id = tag_links.tag_id
WHERE tag_links.image_id = $image_id
tag_names is only going to have single entry for a given ID, which means your query will return a single result. You need to primarily select from tag_links and then join the name of the tag on top of it, so you correctly select from the table with the multiple entries.