How can disable quote join Zend db - zend-framework

I've sql query
select * from table1
left join (values (4),(1800),(103500)) AS "filter (id) on table1.id=filter.id
By default Zend_Db_Select table quoted.
For example:
$result = '(values (4),(1800),(103500)) AS filter (id)';
$select->joinInner($result, "table1.id = filter.id", '');
result:
SELECT * FROM "table1"
INNER JOIN "(values (4),(1800),(103500)) filter (id)" ON table1.id=filter.id
Me need
SELECT * FROM "table1"
INNER JOIN (values (4),(1800),(103500)) filter (id) ON table1.id=filter.id
How can disable quote table?

Try adding $result to your $select as a Zend_Db_Expr.

This is a little tricky. Look at the code below.
$dbh = Zend_Db_Table::getDefaultAdapter();
$select = $dbh->select();
$select->from('table1');
$select->joinInner(
array('filter (id)' => new Zend_Db_Expr('(values (4),(1800),(103500))')),
"table1.id = filter.id",
array()
);
echo $select->assemble() . PHP_EOL;
This code by default outputs the following statement which is not what we really want because identifier filter (id) is quoted. Here is the output.
SELECT `table1`.* FROM `table1`
INNER JOIN (values (4),(1800),(103500)) AS `filter (id)` ON table1.id = filter.id
We need to disable autoQuoteIdentifiers in configuration options. For example:
'db' => array(
'adapter' => 'pdo_mysql',
'isDefaultTableAdapter' => true,
'params' => array(
'host' => '<host>',
'username' => '<user>',
'password' => '<pass>',
'dbname' => '<db>',
'options' => array(
'autoQuoteIdentifiers' => false,
),
),
)
We get the following output
SELECT table1.* FROM table1
INNER JOIN (values (4),(1800),(103500)) AS filter (id) ON table1.id = filter.id
Note that in this case developer is responsible for quoting the identifiers when needed.
I think it's impossible to selectively disable quoting for one of the table alias. Well at least I found this impossible when reviewed 1.x Zend Framework code I have here locally ;)

Related

Additional conditions in JOIN

I have tables with articles and users, both have many-to-many mapping to third table - reads.
What I am trying to do here is to get all unread articles for particular user ( user_id not present in table reads ).
My query is getting all articles but those read are marked, which if fine as I can filter them out (user_id field contains id of user in question).
I have an SQL query like this:
SELECT articles.id, reads.user_id
FROM articles
LEFT JOIN
reads
ON articles.id = reads.article_id AND reads.user_id = 9
ORDER BY articles.last_update DESC LIMIT 5;
Which yields following:
articles.id | reads.user_id
-------------------+-----------------
57125839 | 9
57065456 |
56945065 |
56945066 |
56763090 |
(5 rows)
This is fine. This is what I want.
I'd like to get same result in Catalyst using my article model, but I cannot find any option to add conditions to a JOIN clause.
Do you know any way how to add AND X = Y to DBIx JOIN?
I know this can be done with custom resoult source and virtual view, but I have some other queries that could benefit from it and I'd like to avoid creating virtual view for each of them.
Thanks,
Canto
I don't even know what Catalyst is but I can hack the SQL query:
select articles.id, reads.user_id
from
articles
left join
(
select *
from reads
where user_id = 9
) reads on articles.id = reads.article_id
order by articles.last_update desc
limit 5;
I got an solution.
It's not straight forward, but it's better than virtual view.
http://search.cpan.org/dist/DBIx-Class/lib/DBIx/Class/Relationship/Base.pm#condition
Above describes how to use conditions in JOIN clause.
However, my case needs an variable in those conditions, which is not available by default in model.
So getting around a bit of model concept and introducing variable to it, we have the following.
In model file
our $USER_ID;
__PACKAGE__->has_many(
pindols => "My::MyDB::Result::Read",
sub {
my $args = shift;
die "no user_id specified!" unless $USER_ID;
return ({
"$args->{self_alias}.id" => { -ident => "$args->{foreign_alias}.article_id" },
"$args->{foreign_alias}.user_id" => { -ident => $USER_ID },
});
}
);
in controller
$My::MyDB::Result::Article::USER_ID = $c->user->id;
$articles = $channel->search(
{ "pindols.user_id" => undef } ,
{
page => int($page),
rows => 20,
order_by => 'last_update DESC',
prefetch => "pindols"
}
);
Will fetch all unread articles and yield following SQL.
SELECT me.id, me.url, me.title, me.content, me.last_update, me.author, me.thumbnail, pindols.article_id, pindols.user_id FROM (SELECT me.id, me.url, me.title, me.content, me.last_update, me.author, me.thumbnail FROM articles me LEFT JOIN reads pindols ON ( me.id = pindols.article_id AND pindols.user_id = 9 ) WHERE ( pindols.user_id IS NULL ) GROUP BY me.id, me.url, me.title, me.content, me.last_update, me.author, me.thumbnail ORDER BY last_update DESC LIMIT ?) me LEFT JOIN reads pindols ON ( me.id = pindols.article_id AND pindols.user_id = 9 ) WHERE ( pindols.user_id IS NULL ) ORDER BY last_update DESC: '20'
Of course you can skip the paging but I had it in my code so I included it here.
Special thanks goes to deg from #dbix-class on irc.perl.org and https://blog.afoolishmanifesto.com/posts/dbix-class-parameterized-relationships/.
Thanks,
Canto

DBIx::Class joins generate a large number of select statements

I've noticed that when I do a join in a select in DBIx::Class, I get a ton of SELECT statements as a result. For instance, I have a query that looks like:
my $testSetRs = $db->resultset('Testset')->search(
{%searchCriteria},
{
prefetch => [
{
'ax_group_testsets' => {'ax_group' => 'ax_user_groups'},
},
{'ax_user_testsets' => 'ax_user'},
],
'+columns' => [
{
'me.ax_group_testsets.ax_group_id' => 'ax_group_testsets.ax_group_id',
'me.ax_group_testsets.ax_group.public' => 'ax_group.public',
'me.ax_group_testsets.ax_group.name' => 'ax_group.name',
},
],
collapse => 1,
group_by => ['me.ts_id', 'me.ts_name'],
cache => 1,
}
);
generates:
SELECT me.ts_id, me.ts_name
FROM testset me
LEFT JOIN ax_group_testset ax_group_testsets ON ax_group_testsets.ts_id = me.ts_id
LEFT JOIN ax_group ax_group ON ax_group.ax_group_id = ax_group_testsets.ax_group_id
LEFT JOIN ax_user_group ax_user_groups ON ax_user_groups.ax_group_id = ax_group.ax_group_id
LEFT JOIN ax_user_testset ax_user_testsets ON ax_user_testsets.ts_id = me.ts_id
LEFT JOIN ax_user ax_user ON ax_user.ax_user_id = ax_user_testsets.ax_user_id
WHERE ( ( ax_user.ax_user_id = ? OR ax_user_groups.ax_user_id = ? ) )
GROUP BY me.ts_id, me.ts_name: '349', '349'
SELECT me.ax_group_id, ax_group.public, ax_group.name FROM ax_group_testset me JOIN ax_group ax_group ON ax_group.ax_group_id = me.ax_group_id WHERE ( me.ts_id = ? ): '4476'
SELECT me.ax_group_id, ax_group.public, ax_group.name FROM ax_group_testset me JOIN ax_group ax_group ON ax_group.ax_group_id = me.ax_group_id WHERE ( me.ts_id = ? ): '4566'
SELECT me.ax_group_id, ax_group.public, ax_group.name FROM ax_group_testset me JOIN ax_group ax_group ON ax_group.ax_group_id = me.ax_group_id WHERE ( me.ts_id = ? ): '4701'
etc.
The first SELECT, I want. Its the ones that come after that bother me. There is one for every group (over 40). This happens on all of my joins and can jump into the hundreds with the right query. Why are so many queries generated and what can I do about it?
I'm running Perl ActiveState 5.16 and DBIx .08250.
Your answer lies in the prefetch atribute as described in the DBIx::Class::Manual::Joining and DBIx::Class::ResultSet docs.
Add
prefetch => {'ax_group_testsets' => {'ax_group' => 'ax_user_groups'} },
and, assuming that you want all three related record columns:
'+columns' => [
{
'me.ax_group_testsets.ax_group_id' => 'ax_group_testsets.ax_group_id',
'me.ax_group_testsets.ax_group.public' => 'ax_group.public',
'me.ax_group_testsets.ax_group.name' => 'ax_group.name',
},
],
collapse => 1,
to the query attributes hash.

Zend Db Select add subquery to FROM part

I try to build a query with subquery in FROM part using the Zend_Db_Select. Im looking for somthing like this:
SELECT COUNT(row_1) AS count_row FROM (SELECT row,row2,... FROM table WHERE row= ...) AS temp WHERE row = 0)
So I try to do it like this:
$oSubSelect =
$this->select()
->setIntegrityCheck(false)
->from('table',
array(
'row_id'
)
)
->where(PRFX.'table.id = '.PRFX.'table2.id')
->from(PRFX.'table2',array('row','row2'));
$this->select(false)
->setIntegrityCheck(false)
->from(new Zend_Db_Expr($oSubSelect).' AS temp',
array(
'COUNT(row_id) AS row_count',
)
);
But this gives me an error message.
Best regards.
Ok I fix this. The problem was in
->from(new Zend_Db_Expr($oSubSelect).' AS temp',
Should be:
->from(new Zend_Db_Expr('('.$oSubSelect.')'),

Zend_Db_Select: LEFT JOIN on a subselect

I have a query, that does a LEFT JOIN on a subselect. This query is run in a high load environment and performs within the set requirements. The query (highly simplified) looks like:
SELECT
table_A.pKey
, table_A.uKey
, table_A.aaa
, table_B.bbb
, alias_C.ccc
, alias_C.ddd
FROM table_A
INNER JOIN table_B ON table_A.pKey = table_B.pKey
LEFT JOIN (
SELECT
table_X.pKey
, table_X.ccc
, table_Y.ddd
FROM table_X
INNER JOIN table_Y ON table_X.pKey = table_Y.pKey
) AS alias_C ON table_A.uKey = alias_C.pKey;
(for various reasons, it is not possible to rewrite the subselect as a (direct) LEFT JOIN).
Now, I cannot get the LEFT JOIN on subselect to work with Zend_Db_Select. I've tried everything I could come up with, but it does not work.
So my question is:
Is it not possible to do a query as described above with Zend_Db_Select?
What syntax do I need to get it to work within Zend Framework?
I think that it should work like this:
$subselect = $db->select->from(array('x' => 'table_X'), array('x.pKey', 'x.ccc', 'y.ddd'), 'dbname')
->join(array('Y' => 'table_Y'), 'x.pkey = y.pkey', array(), 'dbname');
$select = $db->select->from(array('a' => 'table_A'), array(/*needed columns*/), 'dbname')
->join(array('b' => 'table_B'), 'a.pkey = b.pkey', array(), 'dbname')
->joinLeft(array('c' => new Zend_Db_Expr('('.$subselect.')'), 'c.pkey = a.ukey', array())
I haven't tried it but I believe it'll work.
...
->joinLeft(array('c' => new Zend_Db_Expr('(' . $subselect->assemble() . ')'), 'c.pkey = a.ukey', array())

UNION and NOT IN Mysql Operation with Zend framework

I need to execute the following mysql query in Zend;I am not an excpert with Zend framework
SELECT `optionride`.`featureoption_id`,
`optionride`.`featureoptionride_id`,`foption`.`featureoptionblock_id`,
`foption`.`labelname`,`optionride`.`value` FROM
`engine4_ride_featureoptionrides` AS `optionride`
LEFT JOIN `engine4_ride_featureoptions` AS `foption`
ON foption.featureoption_id = optionride.featureoption_id
WHERE (optionride.ride_id = '1' ) AND (foption.featureoptiontab_id= '2' )
UNION
SELECT `foption`.`featureoption_id`, null as
`featureoptionride_id`,`foption`.`featureoptionblock_id`,
`foption`.`labelname`,null as `value` FROM `engine4_ride_featureoptions` AS `foption`
WHERE (foption.featureoptiontab_id= '2' ) AND `foption`.`featureoption_id` NOT IN
(
SELECT `optionride`.`featureoptionride_id` FROM `engine4_ride_featureoptionrides`
AS `optionride`
LEFT JOIN `engine4_ride_featureoptions` AS `foption` ON
foption.featureoption_id = optionride.featureoption_id
WHERE (optionride.ride_id = '1' ) AND (foption.featureoptiontab_id= '2' )
)
Anybody can help me please.
You can put all your query asis in the $db->fetch().
Also you can use $db->select()->union(array($sql1, $sql2)), where $sql1, $sql2 can be $db->select() or a string ("select...").
For the NOT IN you can use $db->where('someField NOT IN (?)', array());