MYSql - Using correlated subquery in Zend Db - mysql

I'm trying to construct a working MySql query with a correlated subquery in zend_db_select (ZF 1.12) to use that in Zend_Paginator_Adapter. The working query is as follows:
SELECT f.*, (SELECT (COUNT(p.post_id) - 1)
FROM `forum_topic_posts` AS p WHERE f.topic_id = p.topic_id) AS post_count
FROM `forum_topics` AS f WHERE f.forum_id = '2293'
ORDER BY post_count DESC, last_update DESC
So i worked out:
$subquery = $db->select()
->from(array('p' => 'forum_topic_posts'), 'COUNT(*)')
->where('p.topic_id = f.topic_id');
$this->sql = $db->select()
->from(array('f' => 'forum_topics'), array('*', $subquery . ' as post_count'))
->where('forum_id=?', $forumId, Zend_Db::PARAM_INT)
->order('post_count ' . $orderDirection);
But Zend stops with the following exception when executing the query:
Zend_Db_Statement_Mysqli_Exception: Mysqli prepare error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT COUNT(*) FROM forum_topic_posts AS p WHERE (p.topic_id = f.to' at line 1
How could i get the subquery to work?

Here is the query written using the Zend_Db OO interface.
The key was mostly using some Zend_Db_Expr objects for the subquery and COUNT function.
$ss = $db->select()
->from(array('p' => 'forum_topic_posts'),
new Zend_Db_Expr('COUNT(p.post_id) - 1'))
->where('f.topic_id = p.topic_id');
$s = $db->select()
->from(array('f' => 'forum_topics'),
array('f.*', 'post_count' => new Zend_Db_Expr('(' . $ss . ')')))
->where('f.forum_id = ?', 2293)
->order('post_count DESC, last_update DESC');
echo $s;
// SELECT `f`.*, SELECT COUNT(p.post_id) - 1 FROM `forum_topic_posts` AS `p` WHERE (f.topic_id = p.topic_id) AS `post_count` FROM `forum_topics` AS `f` WHERE (f.forum_id = 2293) ORDER BY `post_count DESC, last_update` DESC

Related

SQL Query with JOIN, WHERE and AND clause to convert in Laravel queries

I'm having problem converting my SQL query to laravel queries. I want to join two tables users and messages so that i can get the users that has conversation with other users.
Database Structure:
Users:
Messages:
This is my SQL query where I get what I want:
SELECT u.id, c.id, u.first_name, u.last_name FROM messages c, users u
WHERE (CASE WHEN c.user_one = #USERID THEN c.user_two = u.ID WHEN c.user_two = #USERID THEN c.user_one= u.id END )
AND ( c.user_one = #USERID OR c.user_two = #USERID ) Order by c.id DESC Limit 20
This is my Laravel query:
DB::table('messages')
->join('users', function($join) use ($user_id) {
$join->select(DB::raw('CASE WHEN messages.user_one = ' . $user_id . ' THEN messages.user_two = ' . $user_id . 'WHEN messages.user_two = ' . $user_id . ' THEN messages.user_one = ' . $user_id));
$join->on(function($query) use ($user_id)
{
$query->where('messages.user_one', '=', $user_id);
$query->orWhere('messages.user_two', '=',$user_id);
});
})
->select('users.*', 'messages.*')
->get();
UPDATE:
Sorry my original answer was not injecting variables in a secure way. Please use this for securely injecting variables in raw queries.
SQL injection security in raw statements.
In raw queries we need remember to pass all variables to our mysql
database using provided second argument.
In your case, you just place question marks placeholders, where you want to have your variables injected and then put your variables in same order as your placeholders in your second argument array.
Source: http://s4.jeffsbio.co.uk/laravel-5-query-builder-where-raw-2
Updated Query:-
DB::table('messages as m')
->join('users as u',function($join){
$join->on('u.id','=','m.user_one')
->orOn('u.id','=','m.user_two');
})
->where(function($query) use($user_id){
$query->where('m.user_one','=',$user_id);
$query->orWhere('m.user_two','=',$user_id);
})
->where(function($query) use($user_id){
$query->whereRaw(
'CASE WHEN m.user_one = ?'.
' THEN m.user_two = u.id'.
' WHEN m.user_two = ?'.
' THEN m.user_one = u.id END',[$user_id,$user_id]);
//here we passed the variables in exact order of their usage in query
})
->select('u.id as user_id', 'm.id as messages_id','u.first_name','u.last_name')
->orderBy('m.id','DESC')
->limit('20')
->get(
Original Answer:
You can't use select clause in join clause like you have.
Use this :-
DB::table('messages as m')
->join('users as u',function($join){
$join->on('u.id','=','m.user_one')
->orOn('u.id','=','m.user_two');
})
->where(function($query) use($user_id){
$query->where('m.user_one','=',$user_id);
$query->orWhere('m.user_two','=',$user_id);
})
->where(function($query) use($user_id){
$query->whereRaw(DB::raw(
'CASE WHEN m.user_one = '.$user_id.
' THEN m.user_two = u.id'.
' WHEN m.user_two = '.$user_id.
' THEN m.user_one = u.id END'));
})
->select('u.id as user_id', 'm.id as messages_id','u.first_name','u.last_name')
->orderBy('m.id','DESC')
->limit('20')
->get();

how to convert this simple sql query to doctrine querybuilder

I'm using php doctrine, and i can't convert this sql query to querybuilder .
i have problems with "group by"
any help please ?
SELECT * FROM message WHERE sender_id='2' OR receiver_id = '2' Group By (if(sender_id > receiver_id, sender_id, receiver_id)) , (if(sender_id <= receiver_id, sender_id,receiver_id)) Order BY createdAt DESC
this is what i did but i need condition on groupby
$queryBuilder = $this->createQueryBuilder('m');
$queryBuilder->where('m.sender = :user or m.receiver =:user')
->groupBy('m.sender','m.receiver')
->orderBy('m.createdAt', 'Desc')
->setParameter('user', $user);
This should probably work:
$qb = $this->createQueryBuilder('m');
$qb->where($qb->expr()->orX(
$qb->expr()->eq('m.sender', ':user'),
$qb->expr()->eq('m.receiver', ':user')
))->groupBy('m.sender')
->addGroupBy('m.receiver')
->orderBy('m.createdAt', 'DESC')
->setParameter('user', $user);
So apart from factoring out your where statement into relevant expr() statements (documentation) the only other change is splitting your groupBy into groupBy and then addGroupBy. You can do the same if you ever need to order by more than one things (orderBy and addOrderBy).

How to run raw SQL Query with Zend Framework 2

Is there a way to execute a SQL String as a query in Zend Framework 2?
I have a string like that:
$sql = "SELECT * FROM testTable WHERE myColumn = 5"
now I want to execute this string directly.
Just pass the sql string to your db adapter like this:
$resultSet = $adapter->query($sql, \Zend\Db\Adapter\Adapter::QUERY_MODE_EXECUTE);
And if you want to pass parameters:
$sql = "SELECT * FROM testTable WHERE myColumn = ?";
$resultSet = $adapter->query($sql, array(5));
EDIT: Please note that the query method does not always returns a resultset. When its a resultset producing query(SELECT) it returns a \Zend\Db\ResultSet\ResultSet otherwise(INSERT, UPDATE, DELETE, ...) it will return a \Zend\Db\Adapter\Driver\ResultInterface.
And when you leave the second Parameter empty you will get a \Zend\Db\Adapter\Driver\StatementInterface which you can execute.
use Zend\Db\Sql\Sql;
use Zend\Db\Adapter\Adapter;
$dbAdapterConfig = array(
'driver' => 'Mysqli',
'database' => 'dbname',
'username' => 'dbusername',
'password' => 'dbuserpassword'
);
$dbAdapter = new Adapter($dbAdapterConfig);
$sql = new Sql($dbAdapter);
$select = $sql->select();
$select->from('testTable');
$select->where(array('myColumn' => 5));
$statement = $sql->prepareStatementForSqlObject($select);
$result = $statement->execute();
S. docu: Zend\Db → Zend\Db\Sql
If you are using tableGateway, you can run your raw SQL query using this statement,
$this->tableGateway->getAdapter()->driver->getConnection()->execute($sql);
where $sql pertains to your raw query. This can be useful for queries that do not have native ZF2 counterpart like TRUNCATE / INSERT SELECT statements.
If you have EntityManager $em on your hands, you can do something like this:
$select = $em->getConnection()->executeQuery("
SELECT a.id, a.title, a.announcement, asvc.service_id, COUNT(*) AS cnt,
GROUP_CONCAT(asvc.service_id SEPARATOR \", \") AS svc_ids
FROM article AS a
JOIN articles_services AS asvc ON asvc.article_id = a.id
WHERE
asvc.service_id IN (
SELECT tsvc.service_id
FROM tender AS t
JOIN tenders_services AS tsvc ON tsvc.tender_id = t.id
WHERE t.id = :tenderId
)
GROUP BY a.id
ORDER BY cnt DESC, a.id DESC
LIMIT :articlesCount
", [
'articlesCount' => 5,
'tenderId' => $tenderId,
], [
'articlesCount' => \PDO::PARAM_INT,
]);
$result = $select->fetchAll(); // <-- here are array of wanted rows
I think this way to execute complex queries is best for Zend. But may be I'm not very smart in Zend still. Will glad to see if it helps to someone.

Question mark DQL is not considered?

I have the following DQL query:
$query = Doctrine_Query::create()
->select('p.genre')
->from('Profile p')
->where('sf_guard_user_id = ?', 11);
If I return the SQL syntax with $sql = $query->getSqlQuery(); I get:
SELECT p.id AS p__id, p.genre AS p__genre FROM profile p WHERE (p.sf_guard_user_id = ?)
This is not normal. It should be 11 not ?:
SELECT p.id AS p__id, p.genre AS p__genre FROM profile p WHERE (p.sf_guard_user_id = 11)
And if I write:
$query = Doctrine_Query::create()
->select('p.genre')
->from('Profile p')
->where('sf_guard_user_id = ' . 11);
The SQL syntax is correct.
Normally DQL should do this automatically. Why isn't happening ?
This is how prepared statement works. Values will be bound on database server Hence doctrine can not show the real values with the query.
Doctrine will show a question mark if you use prepared statement not the real value.
Checkout how it is describing here

SQL to Zend_Db_Table

I'm trying to convert a SQL to Zend_Db_Table
SELECT c1.* FROM beneficios c1
left join beneficios c2 on c1.document_id = c2.document_id and c1.versao < c2.versao
where c1.id_projeto = 8 and c2.document_id is null order by ordem ASC;
I have a method inside a zend db table class
$info = $this->info();
$select = $this->select()
->from(array('c1' => $info['name']))
->joinLeft(array('c2' => $info['name']),
'c1.document_id = c2.document_id and c1.versao < c2.versao')
->where('c2.document_id is null')
->where('c1.id_projeto = ?', $id_projeto)
->order('ordem ASC');
return $this->fetchAll($select);
I get the following error
Zend_Db_Statement_Exception: SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'ordem' in order clause is ambiguous
if I remove order
Zend_Db_Statement_Exception: SQLSTATE[HY093]: Invalid parameter number: no parameters were bound
What's the correct way to convert that SQL?
If anyone could help me, Thanks!
It's just as it says: "Column 'ordem' in order clause is ambiguous". Prefix ordem with either c1. or c2., depending on which table's ordem column you want to sort by.
Instead of $this->select() use $this->getAdapter()->select(). Also you can specify that you don't want any of the columns from table c2 by passing an empty array to the joinLeft function:
$info = $this->info();
$select = $this->getAdapter->select()
->from(array('c1' => $info['name']))
->joinLeft(array('c2' => $info['name']),
'c1.document_id = c2.document_id and c1.versao < c2.versao', array())
->where('c2.document_id is null')
->where('c1.id_projeto = ?', $id_projeto)
->order('ordem ASC');
return $this->fetchAll($select);