Symfony2 Doctrine query builder as a subquery in FROM clause - php

I got a query using query builder and that is assigned to $qb variable. It works fine both from PHP and from the DB. Now, I was trying to use that query as a subquery like below:
$subQuery = $qb->getQuery()->getSql();
$query = 'select res.some_name
from ('.$subQuery.') as res';
But I get the following exception:
Caused by Doctrine\DBAL\Driver\PDOException: SQLSTATE[HY000]: General error: 1 no such column: res.some_name
As Doctrine already converted the $qb to something like this where Doctrine converted the original SQL query. For instance, there was something called AS legalentity_name but it's showing AS name1:
select res.some_name from (SELECT o0_.id AS id0, o0_.name AS name1, b1_.id AS id2, b1_.display AS display3, m2_.id AS id4, m2_.total AS total5 FROM Invoice i3_ INNER JOIN CodeableItem c4_ ON i3_.id = c4_.id INNER JOIN MonetaryItem m2_ ON i3_.id = m2_.id AND (1=1) INNER JOIN LineItem l5_ ON c4_.id = l5_.codeableItem_id LEFT JOIN MonetaryItem m6_ ON l5_.id = m6_.id AND (1=1) LEFT JOIN PresetLineItem p7_ ON c4_.id = p7_.codeableItem_id LEFT JOIN MonetaryItem m8_ ON p7_.id = m8_.id AND (1=1) INNER JOIN OrgUnit o0_ ON c4_.legalentity_id = o0_.id AND (1=1) INNER JOIN monetaryitem_listitem m9_ ON m2_.id = m9_.monetaryitem_id INNER JOIN BWListItem b1_ ON b1_.id = m9_.bwlistitem_id AND (1=1) INNER JOIN BWList b10_ ON b1_.bwlist_id = b10_.id AND (1=1) WHERE b10_.type = 'Vendor' GROUP BY c4_.legalentity_id, b1_.active, b1_.attributes, b1_.display, b1_.created, b1_.updated, b1_.lft, b1_.lvl, b1_.rgt, b1_.root, b1_.id, b1_.orgunit_id, b1_.bwlist_id, b1_.parent_id, b1_.rootou_id, m2_.created, m2_.updated, m2_.subtotal, m2_.total, m2_.description, m2_.id, c4_.number, c4_.externalId, c4_.status, c4_.overriddenDuringApproval, i3_.invoiceDate, i3_.dueDate, i3_.poNumber, m2_.rootou_id, c4_.image_id, c4_.legalentity_id, c4_.creator, c4_.owner_id ORDER BY o0_.name ASC, b1_.display ASC) as res
My question is: how can I use the raw SQL from $subQuery? Any help would be really beneficial. Cheers!

First off, subqueries in DQL is not possible. See Selecting from subquery in DQL
Secondly, you are putting computed SQL from Doctrine Query Language (DQL) into a subquery. This does not work as the database cannot find the column due to DQL prefixing characters/numeric values to the columns.
This is so the entities can be mapped correctly when using DQL.
You will need to build the subquery NOT using the DQL language (stop using that query builder, not sure if there is one that builds raw SQL).

Related

Optimizing mysql IN query having large values

I have a mysql query like
SELECT `tbl_ticket`.`id`, `tbl_ticket`.`hd_user_username`,
`tbl_ticket`.`hd_user_email`, `tbl_ticket`.`ticket_title`,
`tbl_complain_type`.`complains` FROM `tbl_ticket` LEFT JOIN
`tbl_ticket_complain` ON tbl_ticket_complain.ticket_id=tbl_ticket.id
LEFT JOIN `tbl_complain_type` ON tbl_complain_type.id=tbl_ticket_complain.complain_id
LEFT JOIN `tbl_assignment` ON tbl_assignment.ticket_id=tbl_ticket.id
WHERE ((((`hd_user_username` LIKE '%searchterm%')
AND (`tbl_assignment`.`id` IN ($array)))
OR (`hd_user_email`='searchterm'))
OR (`ticket_title`='searchterm')) OR (`tbl_complain_type`.`complains`='searchterm')
$array contains around 7000 values like `$array=array(1,2,3,..)`
This query takes around 8 seconds to execute. Is there any alternative solution for this query ?
The value of $array is got from another query
select max(id) from tbl_assignment group by ticket_id
The slowness of query is due to multiple joins between tables
If the values in the array use in you IN clause come from a select you could use the fact that
An IN clause is equivalent to an inner join so you could use a inner join between your_table_with_id and the table.column you need for match eg:
SELECT `
tbl_ticket`.`id`
, `tbl_ticket`.`hd_user_username`
, `tbl_ticket`.`hd_user_email`
, `tbl_ticket`.`ticket_title`
, `tbl_complain_type`.`complains`
FROM `tbl_ticket`
LEFT JOIN `tbl_ticket_complain` ON tbl_ticket_complain.ticket_id=tbl_ticket.id
LEFT JOIN `tbl_complain_type` ON tbl_complain_type.id=tbl_ticket_complain.complain_id
LEFT JOIN `tbl_assignment` ON tbl_assignment.ticket_id=tbl_ticket.id
INNER JOIN your_table_with_id ON `tbl_assignment`.`id` = JOIN your_table_with_id.id
WHERE ((((`hd_user_username` LIKE '%searchterm%')
OR (`hd_user_email`='searchterm'))
OR (`ticket_title`='searchterm')) OR (`tbl_complain_type`.`complains`='searchterm')
Remeber also that the content of values use IN clause is limited and fail when the limit is exceeded
and in your case
SELECT `
tbl_ticket`.`id`
, `tbl_ticket`.`hd_user_username`
, `tbl_ticket`.`hd_user_email`
, `tbl_ticket`.`ticket_title`
, `tbl_complain_type`.`complains`
FROM `tbl_ticket`
LEFT JOIN `tbl_ticket_complain` ON tbl_ticket_complain.ticket_id=tbl_ticket.id
LEFT JOIN `tbl_complain_type` ON tbl_complain_type.id=tbl_ticket_complain.complain_id
LEFT JOIN `tbl_assignment` ON tbl_assignment.ticket_id=tbl_ticket.id
INNER JOIN (
select max(id) as id
from tbl_assignment
group by ticket_id
) t ON `tbl_assignment`.`id` = t.id
WHERE ((((`hd_user_username` LIKE '%searchterm%')
OR (`hd_user_email`='searchterm'))
OR (`ticket_title`='searchterm')) OR (`tbl_complain_type`.`complains`='searchterm'))
This is basically your query:
SELECT . . .
FROM tbl_ticket t LEFT JOIN
tbl_ticket_complain tc
ON tc.ticket_id = t.id LEFT JOIN
tbl_complain_type tct
ON tct.id = tc.complain_id LEFT JOIN
tbl_assignment a
ON a.ticket_id = t.id
WHERE (((hd_user_username LIKE '%searchterm%' AND
a.id IN ($array)
) OR
`hd_user_email`='searchterm'
) OR
ticket_title = 'searchterm'
) OR
tct.complain` = 'searchterm';
The issue with performance has nothing to do with IN. In fact, MySQL optimizes IN, as explained in the documentation:
If all values are constants, they are evaluated according to the type
of expr and sorted. The search for the item then is done using a
binary search. This means IN is very quick if the IN value list
consists entirely of constants.
You are not going to get faster than an IN list with constants.
The problem with your query is the string of ORs. These make is almost impossible for the optimizer to use indexes -- so the full result set has to be created and then filtered down.
It is hard for me to see how to improve this in your query. Sometimes, splitting a query into simpler chunks and connecting them using union or union all does the trick. Your conditions are a bit hard to follow, making that approach difficult for an outsider.

MYSQL/PHP: Concat returning to many fields on LEFT JOIN

I had a SELECT query with a LEFT JOIN working as desired. I then added one more table via a smilar LEFT JOIN and now I am getting a wierd result. Basically, for a group_concat where I was getting one item for every record, I am getting eight records. I don't see why this is happening because the join to the new table is analagous to several other joins that do not have this problem (that I have omitted from the example for clarity).
Here is the query that is fine:
$sql = "SELECT t.*,
group_concat(tf.todoid) as `tftodoid`,
group_concat(tf.id) as `tfid`,
group_concat(tf.filedescript) as `tffiledescript`,
group_concat(tf.filename) as `tffilename`,
group_concat(tf.founderid) as `tffounderid`,
group_concat(tf.ext) as `tfext`,
group_concat(tf.lasttouched) as `tilt`
FROM titles `t`
LEFT JOIN titlefiles `tf`
ON (tf.todoid = t.id AND tf.founderid = '$userid')
WHERE t.userid='$userid'
GROUP BY t.id";
And here is the query with the extra join that is now spilling out the multiple copies of the items:
$sql = "SELECT t.*,
group_concat(tf.todoid) as `tftodoid`,
group_concat(tf.id) as `tfid`,
group_concat(tf.filedescript) as `tffiledescript`,
group_concat(tf.filename) as `tffilename`,
group_concat(tf.founderid) as `tffounderid`,
group_concat(tf.ext) as `tfext`,
group_concat(tf.lasttouched) as `tilt`,
group_concat(s.id) as `stepid`,
group_concat(s.step) as `steps`
FROM titles `t`
LEFT JOIN titlefiles `tf`
ON (tf.titleid = t.id AND tf.founderid = '$userid')
LEFT JOIN steps `s`
ON s.titleid = t.id
WHERE t.userid='$userid'
GROUP BY t.id";
Here is an example of output in JSON showing the difference:
First query:
"tfid":"56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81"
Second query:
"tfid":"56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81",
I suspect the problem has something to do with the JOIN or with the Group By statements but I can't see how to fix.
How can I ensure that I get only one fileid per file as opposed to eight?
Alter the line as follows:
group_concat(DISTINCT tf.id) as `tfid`,
This then only gets you the unique ids.
If you want them ordered add:
group_concat(DISTINCT tf.id ORDER BY tf.id ASC) as `tfid`,

mysql multiple joins on one query

I have 4 table in a database , I need joins Four tables together but I can't Two more tables joins
$query = '
SELECT
movie_title,genre_label,movie_year,director_name,movie_genre,movie_director,movie_actor
FROM
general LEFT JOIN genre ON movie_genre = genre_id AND
general LEFT JOIN movie_directors ON movie_director = director_id
WHERE
general.movie_genre = genre.genre_id AND
general.movie_director = movie_directors.director_id
ORDER BY
movie_year';
my Browser show this Error:
Unknown column 'general' in 'on clause'
please Help Me , Thankes
You don't have to insert AND between joins and repeat the table you are querying.
Your query should be like this:
$query = '
SELECT
movie_title,genre_label,movie_year,director_name,movie_genre,movie_director,movie_actor
FROM
general
LEFT JOIN genre ON movie_genre = genre_id
LEFT JOIN movie_directors ON movie_director = director_id
ORDER BY
movie_year';
it does not work?
SELECT movie_title,genre_label,movie_year,director_name,movie_genre,movie_director,movie_actor
FROM
general
LEFT JOIN genre ON general.movie_genre = genre.genre_id
LEFT JOIN movie_directors ON general.movie_director = movie_directors.director_id
ORDER BY
movie_year';
Try this:
SELECT
movie_title,genre_label,movie_year,
director_name,movie_genre,movie_director,movie_actor
FROM general
LEFT JOIN genre ON general.movie_genre = genre.genre_id
LEFT JOIN movie_directors ON general.movie_director = movie_directors.director_id
ORDER BY movie_year
Your JOIN/WHERE syntax is not correct. WHERE clause is not needed in your case. Additionally it is a good practice to specify the column names using table alias in your SELECT and ORDER BY statements.
Note: The above query statement is based on information provided in the question. The query can change if the OP adds table structure information int the question since it is not clear on what tables should the 2nd JOIN take place.

Select in 2 databases with Kohana PHP

How to JOIN tables in different databases in Kohana?
$tb_new = 'db_zaboo_feed.feed_' . $feed;
$ids = DB::query(Database::SELECT,
"SELECT d.fuid_id, d.user_id FROM db_zaboo.displays d
LEFT JOIN $tb_new f ON(d.fuid_id = f.uid)
WHERE d.user_id = (SELECT user_id FROM $tb_new GROUP BY user_id) AND f.uid IS NULL")->execute();
ERROR:
Database_Exception [ 1146 ]: Table 'db_zaboo.displays' doesn't exist [ SELECT d.fuid_id, d.user_id FROM db_zaboo.displays d LEFT JOIN db_zaboo_feed.feed_3 f ON(d.fuid_id = f.uid) WHERE d.user_id = (SELECT user_id FROM db_zaboo_feed.feed_3 GROUP BY user_id) AND f.uid IS NULL ]
This is not a Kohana specific question. It is a MySQL question.
For operating on multiple databases in the same query, you have to use table names with its database name. Such as:
SELECT * FROM database1.table1 WHERE database1.table1.id IN (SELECT table1_id FROM database2.table2)
Be careful: by literally writing your query, I am not sure if Kohana's query builder prevents SQL injection attacks as it would do by building the SQL query using the query builder's specific functions. I would prevent it using the function mysql_real_escape_string
To use a different database config group pass either the name or the config object to execute().
$result = $query->execute('config_name')
http://kohanaframework.org/3.0/guide/database/query/builder#executing
http://kohanaframework.org/3.0/guide/database/config#connection-settings

How to convert named inner join query to code-igniter syntax?

I have the following query
$query = $this->db->query(
'SELECT ii.json
FROM inventory ii
INNER JOIN
(
SELECT json, MAX(id) as MAX_ID
FROM inventory
GROUP BY business_unit
) group_json ON ii.id = group_json.MAX_ID
INNER JOIN business_units
ON ii.business_unit = business_units.id'
);
return $query->result_array();
We've had to migrate to a MSSQL test server and unfortunately the syntax is incorrect. To avoid this in the future I want to convert this to the CodeIgniter typed query.
Can anyone show me what this query would look like in CodeIgniter, I've tried a few things but I'm not getting anywhere close.
SELECT ii.json
FROM inventory ii
INNER JOIN
(
SELECT json, MAX(id) as MAX_ID
FROM inventory
GROUP BY business_unit, json --<-- "json" needs to be in GROUP BY
) group_json -- Since it is in SELECT but not
ON ii.id = group_json.MAX_ID -- contained in any aggregate function
INNER JOIN business_units
ON ii.business_unit = business_units.id
if you use any aggregate function(MAX, MIN, AVG, SUM, COUNT) in your SELECT any other columns that are not contained in any aggregate function in that select, must go in GROUP BY clause of your query.

Categories