Im trying to build a complex (well...) query with Zend_Db_Table where I will both need to join the original table with an extra table and get some extra info from the original table with zend_db_expr.
However, things go wrong from the start. What I to is this:
$select = $this->getDbTable()->select(Zend_Db_Table::SELECT_WITH_FROM_PART)
->setIntegrityCheck(false);
$select->from( $this->getDbTable() , array(
'*' ,
new Zend_Db_Expr('`end` IS NULL as isnull') ,
new Zend_Db_Expr('`sold` IN (1,2,3) as issold') ,
) );
Zend_Debug::dump( $select->__toString() );exit;
What results in this:
SELECT `items`.*, `items_2`.*, `end` IS NULL as isnull, `sold` IN (1,2,3) as issold FROM `items`
INNER JOIN `items` AS `items_2`
What I need it to be though, at this point before I do the join with the other table, is
SELECT `items`.*, `end` IS NULL as isnull, `sold` IN (1,2,3) as issold FROM `items`
I don't need an inner join with itself, I just need to add those two Zend_Db_Expr to the things that should be selected, after which I'd continue building the query with the join and where's etc. like
$select->joinLeft( ... )
->where(...)
Any ideas? Cheers.
You should not redo a ->from() call, which means yu add a new table in the query.
Instead you should just use ->where()->columns() calls containing you Zend_Db_expr.
edit: sorry for the mistake.
Related
Thanks for past help.
While doing an update using a join, I am getting the 'Error Code: 1288. The target table _____ of the UPDATE is not updatable' and figure out why. I can update the table with a simple update statement (UPDATE sales.customerABC Set contractID = 'x';) but can't using a join like this:
UPDATE (
SELECT * #where '*' contains columns a.uniqueID and a.contractID
FROM sales.customerABC
WHERE contractID IS NULL
) as a
LEFT JOIN (
SELECT uniqueID, contractID
FROM sales.tblCustomers
WHERE contractID IS NOT NULL
) as b
ON a.uniqueID = b.uniqueID
SET a.contractID = b.contractID;
If changing that update statement a SELECT such as:
SELECT * FROM (
SELECT *
FROM opwSales.dealerFilesCTS
WHERE pcrsContractID IS NULL
) as a
LEFT JOIN (
SELECT uniqueID, pcrsContractID
FROM opwSales.dealerFileLoad
WHERE pcrsContractID IS NOT NULL
) as b
ON a."Unique ID" = b.uniqueID;
the result table would contain these columns:
a.uniqueID, a.contractID, b.uniqueID, b.contractID
59682204, NULL, NULL, NULL
a3e8e81d, NULL, NULL, NULL
cfd1dbf9, NULL, NULL, NULL
5ece009c, , 5ece009c, B123
5ece0d04, , 5ece0d04, B456
5ece7ab0, , 5ece7ab0, B789
cfd21d2a, NULL, NULL, NULL
cfd22701, NULL, NULL, NULL
cfd23032, NULL, NULL, NULL
I pretty much have all database privileges and can't find restrictions with the table reference data. Can't find much information online concerning the error code, either.
Thanks in advance guys.
You cannot update a sub-select because it's not a "real" table - MySQL cannot easily determine how the sub-select assignment maps back to the originating table.
Try:
UPDATE customerABC
JOIN tblCustomers USING (uniqueID)
SET customerABC.contractID = tblCustomers.contractID
WHERE customerABC.contractID IS NULL AND tblCustomers.contractID IS NOT NULL
Notes:
you can use a full JOIN instead of a LEFT JOIN, since you want uniqueID to exist and not be null in both tables. A LEFT JOIN would generate extra NULL rows from tblCustomers, only to have them shot down by the clause requirement that tblCustomers.contractID be not NULL. Since they allow more stringent restrictions on indexes, JOINs tend to be more efficient than LEFT JOINs.
since the field has the same name in both tables you can replace ON (a.field1 = b.field1) with the USING (field1) shortcut.
you obviously strongly want a covering index with (uniqueID, customerID) on both tables to maximize efficiency
this is so not going to work unless you have "real" tables for the update. The "tblCustomers" may be a view or a subselect, but customerABC may not. You might need a more complicated JOIN to pull out a complex WHERE which might be otherwise hidden inside a subselect, if the original 'SELECT * FROM customerABC' was indeed a more complex query than a straight SELECT. What this boils down to is, MySQL needs a strong unique key to know what it needs to update, and it must be in a single table. To reliably update more than one table I think you need two UPDATEs inside a properly write-locked transaction.
I have query like this
SELECT *
FROM `GSheets`
WHERE `sheetcat` = 'Unsubscribed'
AND `sheetcat` IS NOT NULL
AND `user` LIKE '%,r00t,%'
OR `user` LIKE 'r00t,%'
OR '%,r00t'
OR `user` = 'r00t'
I specify sheetcat to be Unsubscribed, but query response is NULL (or blank)?
Why?
This is a problem when you store lists of things in delimited strings. Bad, bad, bad data design. The first and primary advice is to create a new table with one row per sheet and one row per user.
Now, sometimes we are stuck with other people's really bad, bad, bad design decisions. MySQL offers find_in_set() which does what you want:
SELECT g.*
FROM `GSheets` g
WHERE `sheetcat` = 'Unsubscribed' AND
FIND_IN_SET('r00t', `user`) > 0;
Note that sheetcat IS NOT NULL is redundant. A NULL value would fail the first condition. Unless you possibly intend:
SELECT g.*
FROM `GSheets` g
WHERE `sheetcat` = 'Unsubscribed' OR
(sheetcat IS NOT NULL AND FIND_IN_SET('r00t', `user`) > 0);
But that would not be my first guess as to your intention.
Your query would also work if you put parentheses around the OR conditions. But even that is too complicated. A simpler version using LIKE would be:
WHERE `sheetcat` = 'Unsubscribed' AND
CONCAT(',', `user`, ',') LIKE '%,r00t,%'
AND takes precedence over OR. You need to group your OR conditions:
SELECT *
FROM `GSheets`
WHERE `sheetcat` = 'Unsubscribed'
AND
( `user` LIKE '%,r00t,%'
OR `user` LIKE 'r00t,%'
OR `user` LIKE '%,r00t'
OR `user` = 'r00t'
)
The second sheetcat condition is also redundant, you can remove the NULL check.
This code runs fine. Pay special attention to the 'AS commercial' subquery field. It works.
SELECT `Contacts`.`id`,
(
SELECT `team_members`.`id`
FROM team_members
INNER JOIN team_categories_team_members AS memcat
ON `team_members`.`id` = `memcat`.`team_member_id`
WHERE `memcat`.`team_category_id` =3
) AS commercial
FROM `oys001`.`team_members` AS `Contacts`
JOIN `oys001`.`brands_team_members` AS `BrandsTeamMember` ON (
`BrandsTeamMember`.`brand_id` =2
AND `BrandsTeamMember`.`team_member_id` = `Contacts`.`id` )
However, now I want to perform a condition on it, so I just add this:
WHERE commercial > 0
And it tells me the field does not exist... What's going wrong here?
Wrap your inital query in brackets
SELECT *
FROM (yourqueryhere) AS `v`
WHERE commercial > 0
This query will be done in a cached autocomplete text box, possibly by thousands of users at the same time. What I have below works, bit I feel there may be a better way to do what I am doing.
Any advice?
UPDATED -- it can be 'something%':
SELECT a.`object_id`, a.`type`,
IF( b.`name` IS NOT NULL, b.`name`,
IF( c.`name` IS NOT NULL, c.`name`,
IF( d.`name` IS NOT NULL, d.`name`,
IF ( e.`name` IS NOT NULL, e.`name`, f.`name` )
)
)
) AS name
FROM `user_permissions` AS a
LEFT JOIN `divisions` AS b
ON ( a.`object_id` = b.`division_id`
AND a.`type` = 'division'
AND b.`status` = 1 )
LEFT JOIN `departments` AS c
ON ( a.`object_id` = c.`department_id`
AND a.`type` = 'department'
AND c.`status` = 1 )
LEFT JOIN `sections` AS d
ON ( a.`object_id` = d.`section_id`
AND a.`type` = 'section'
AND d.`status` = 1 )
LEFT JOIN `units` AS e
ON ( a.`object_id` = e.`unit_id`
AND a.`type` = 'unit'
AND e.`status` = 1 )
LEFT JOIN `positions` AS f
ON ( a.`object_id` = f.`position_id`
AND a.`type` = 'position'
AND f.`status` = 1 )
WHERE a.`user_id` = 1 AND (
b.`name` LIKE '?%' OR
c.`name` LIKE '?%' OR
d.`name` LIKE '?%' OR
e.`name` LIKE '?%' OR
f.`name` LIKE '?%'
)
Two simple, fast queries is often better than one huge, inefficient query.
Here's how I'd design it:
First, create a table for all your names, in MyISAM format with a FULLTEXT index. That's where your names are stored. Each of the respective object type (e.g. departments, divisions, etc.) are dependent tables whose primary key reference the primary key of the main named objects table.
Now you can search for names with this much simpler query, which runs blazingly fast:
SELECT a.`object_id`, a.`type`, n.name, n.object_type
FROM `user_permissions` AS a
JOIN `named_objects` AS n ON a.`object_id = n.`object_id`
WHERE MATCH(n.name) AGAINST ('name-to-be-searched')
Using the fulltext index will run hundreds of times faster than using LIKE in the way you're doing.
Once you have the object id and type, if you want any other attributes of the respective object type you can do a second SQL query joining to the table for the appropriate object type:
SELECT ... FROM {$object_type} WHERE object_id = ?
This will also go very fast.
Re your comment: Yes, I'd create the table with names even if it's redundant.
Other than changing the nested Ifs to use a Coalesce() function (MySql has Coalesce() doesn't it)? There is not much you can do as long as you need to filter on that input parameter with a like expresion. Putting a filter on a column using a Like expression, where the Like parameter has a wildcard at the begining, as you do, makes the query argument non-SARG-able, which means that the query processor must do a complete table scan of all the rows in the table to evaluate the filter predicate.
It cannot use an index, because an index is based on the column values, and with your Like parameter, it doesn't know which index entries to read from (since the parameter starts with a wild card)
if MySql has Coalesce, you can replace your Select with:
SELECT a.`object_id`, a.`type`,
Coalesce(n.name, c.name, d.Name, e.Name) name
If you can replace the search argument parameter so that it does not start with a wildcard, then just ensure that there is an index on the name column in each of the tables, and (if there are not indices on that column now), the query performance will increase enormously.
There are 500 things you can do. Optimize once you know where your bottlenecks are. Until then, work on getting those users onto your app. Its a much higher priority.
I have a view : vcompanyendofday
The following query executes in just 0.7 secs
Select * from vcompanyendofday
But a simple where condition to this query takes around 200.0 secs
select * from vcompanyendofday where companyid <= 51;
This is the view definition:
CREATE VIEW `vcompanyendofday` AS
select `c`.`companyid` AS `companyid`,
`c`.`scripcode` AS `scripcode`,
`e`.`eoddate` AS `eoddate`,
`e`.`prevclose` AS `prevclose`,
`e`.`delqty` AS `delqty`
from (
`company` `c`
left join
`endofday` `e`
on ((`c`.`companyid` = `e`.`companyid`)))
where (`e`.`eoddate` =
(
select max(`e2`.`eoddate`) AS `max(eoddate)`
from `endofday` `e2`
where (`e2`.`companyid` = `c`.`companyid`)
)
);
Seems you don't have an index on endofday.companyid
When you add the condition, company becomes leading in the join, and kills all performance.
Create an index on endofday.companyid:
CREATE INDEX ix_endofday_companyid ON endofday(companyid)
By the way, if you want all companies to be returned, you need to put the subquery into the ON clause of the OUTER JOIN, or your missing endofday's will be filtered out:
CREATE VIEW `vcompanyendofday` AS
select `c`.`companyid` AS `companyid`,
`c`.`scripcode` AS `scripcode`,
`e`.`eoddate` AS `eoddate`,
`e`.`prevclose` AS `prevclose`,
`e`.`delqty` AS `delqty`
from (
`company` `c`
left join
`endofday` `e`
on `c`.`companyid` = `e`.`companyid`
AND `e`.`eoddate` =
(
select max(`e2`.`eoddate`) AS `max(eoddate)`
from `endofday` `e2`
where (`e2`.`companyid` = `c`.`companyid`)
)
Have you tried the select used to create the view by itself with the WHERE clause to see what happens?
If the problem happens with that, run EXPLAIN on that query to see what's happening.
At a guess, there's no index on companyid in one of the tables, most likely endofday.