Create Doctrine query - mysql

I have a quiz application where played rounds are logged in UserPerformanceEntity. The corresponding MySQL table looks like:
UserPerformance
---------------
id (PK)
user
start_time
end_time
max_level
I want to display some statistics, like 'You performed better than x% of players'. To calculate the percent of players who performed worse than the current I want to execute the following MySQL query:
SELECT COUNT(DISTINCT `user`)
FROM `UserPerformance`
WHERE
(end_time IS NOT NULL
AND end_time NOT LIKE '0000-00-00 00:00:00')
AND `user` != :current_user
AND max_level < :current_level
ORDER BY max_level DESC
But I have no idea how to implement this with doctrine. How should my querybuilder look like?

try this
$query = $this->createQueryBuilder('u');
$query->select('COUNT(DISTINCT u.id) as usercount')
->where('u.end_time IS NOT NULL')
->andWhere('u.end_time NOT LIKE :end_time')
->andWhere('u.id != :current_userid')
->andWhere('u.max_level < :current_level')
->setParameter('end_time', '0000-00-00 00:00:00')
->setParameter('current_userid', $current_userid)
->setParameter('current_level', $current_level)
->setMaxResults(1);
$result = $query->getQuery()->getResult();
return $result[0]['usercount'] ;
The only change is, you need id from current user instead object

Additional information that a user can play many times, so user id is not unique, one user can have many records. This is my solution:
$em = $this->getDoctrine()->getManager();
$qb = $em->createQueryBuilder();
$result = $qb->select('COUNT(DISTINCT p.user) AS usercount')
->from('LoginetFBappVagoBundle:UserPerformanceEntity', 'p')
->where(
$qb->expr()->andX(
$qb->expr()->isNotNull('p.endTime'),
$qb->expr()->neq('p.endTime', ':endTime')))
->andWhere($qb->expr()->neq('p.user', ':userId'))
->andWhere($qb->expr()->lt('p.maxLevel', ':maxLevel'))
->setParameter('endTime', '0000-00-00 00:00:00')
->setParameter('userId', $userid)
->setParameter('maxLevel', $level)
->getQuery()
->getResult();
I started from #Alexander Keil's answer, but I used expressions, and I figured out that there is no need to group the (end_time IS NOT NULL AND end_time NOT LIKE '0000-00-00 00:00:00') part. However if I pass '0000-00-00 00:00:00' in the expression instead setting as a parameter, I get an error. And at last I have to count the distinct users the get the correct result.

Related

Symfony3-Doctrine : Order by Case When

I'm working on a Symfony 3.4 project.
I want to order a table by updated_at if exists (not null), by created_at if not.
In SQL, this works :
SELECT * FROM `contract`
ORDER BY
(CASE WHEN ISNULL(`updated_at`) THEN `created_at` ELSE `updated_at` END)
DESC
I tried a lot of things but I don't manage to make it work with Doctrine Query Builder.
First, I tried this (syntax error) :
$contracts = $em->createQuery(
'SELECT c
FROM AppBundle:Contract c
ORDER BY (CASE WHEN c.updatedAt = :update THEN c.createdAt ELSE c.updatedAt END) DESC')
->setParameter('update', NULL)
->getResult();
Then, I tried this according to this topic, but I have no result (no error) :
$contracts = $rp->createQueryBuilder('c')
->select('(CASE WHEN c.updatedAt != :update THEN 1 ELSE 0 END) AS HIDDEN orderDate')
->orderBy('orderDate', 'DESC')
->addOrderBy('c.createdAt', 'DESC')
->setParameter('update', NULL)
->getQuery()->getResult();
How can I sort my contracts by their updated date if they have been updated, or by their created date if they haven't been modified ?
If it helps, I use the DoctrineExtensions bundle for other queries, I saw IfNull and IfElse classes but I don't how to use them with my case.
After several attempts, I finally found the solution.
Use COALESCE : returns the first value not null in the list, so if A is null and B not null, then COALESCE(A,B) will return B.
$contracts = $rp->createQueryBuilder('c')
->select('c')
->addSelect('COALESCE(c.updatedAt,c.createdAt) AS HIDDEN orderDate')
->orderBy('orderDate', 'DESC')
->getQuery()->getResult();
No need to use the DoctrineExtensions bundle.

Add a not empty check for a date field into a query

Is there a way to alter this query so that it would check if frp_fundraisingprogram.start_date is not empty or contains the default value?
SELECT frp_fundraisingprogram.id AS id
FROM frp_fundraisingprogram
WHERE frp_fundraisingprogram.start_date <= DATE_ADD(UTC_TIMESTAMP(), INTERVAL - 2 day)
AND frp_fundraisingprogram.date_entered > '2015-04-09 16:55:18'
AND NOT EXISTS
(SELECT * FROM aow_processed
WHERE aow_processed.aow_workflow_id='9bc1bb2e-cd5a-5c75-cc68-5526ae30331e'
AND aow_processed.parent_id=frp_fundraisingprogram.id
AND aow_processed.status = 'Complete' AND aow_processed.deleted = 0)
AND frp_fundraisingprogram.deleted = 0
Just include that in your where clause...
`...WHERE not isnull(frp_fundraisingprogram.start_date)
AND frp_fundraisingprogram.start_date != '0000-00-00 ...`
That is if you are trying to exclude these records. You can reverse the logic if you only want these records to show up.

Eloquent query building complex query to get unique records searching for an ID in 2 different columns in same table

I'm migrating a project to Laravel 4 and I am stuck with a quite complex query, which I'd like to migrate into a proper Eloquent query.
I have a table that contains chat messages, called chat_messages with a representing Model Chatmessage
The table contains a sender and a receipient column with a user id linking to the users table and User Model.
The query to get a list with all user IDs of all chat partners in raw SQL on the old version of the application is as follows:
$sql_allChatPartners = "SELECT DISTINCT chatPartner
FROM ( SELECT * FROM (
SELECT cm_receipient AS chatPartner, cm_sent_at
FROM chat_messages WHERE cm_sender = '".$me->userID."'
UNION
SELECT cm_sender AS chatPartner, cm_sent_at
FROM chat_messages WHERE cm_receipient = '".$me->userID."'
) whateva ORDER BY whateva.cm_sent_at DESC ) other";
Sorry for naming the "fake" tables whateva and other :-)
Could anyone put me in the right direction to do this with Eloquent Querybuilder?
It is important that I get the list of chatPartner IDs in the correct order, where the last chat message has been exchanged as first chatPartner. And the chatPartner where longest inactivity was in the chat as last entry.
This is what I got so far in my User Model...
public function allopenchats(){
$asSender = Chatmessage::where('sender', $this->id)->select('receipient as chatPartner, created_at');
$asBoth = Chatmessage::where('receipient', $this->id)->select('sender as chatPartner, created_at')
->union($asSender)->orderBy('created_at', 'desc')->get();
}
I renamed the columns cm_receipient to receipient, cm_sender to sender and sent_at to created_at in the new database for the new version
Your help would be very much appreciated!
You sql may change to:
SELECT IF (cm_receipient = '10', cm_sender, IF (cm_sender = '10',cm_receipient, 'no')) AS chatPartner, cm_sent_at
FROM chat_messages
WHERE cm_receipient = '10' OR cm_sender = '10'
GROUP BY chatPartner
HAVING chatPartner != 'no'
order by cm_sent_at DESC
In orm:
Chatmessage::where('sender','=',$this->id)
->orWhere('receipient','=',$this->id)
->select(DB::raw('IF (receipient = '.$this->id.', sender, IF (sender = '.$this->id.',receipient, 'no' )) AS chatPartner'), 'created_at')
->groupBy('chatPartner')
->having('chatPartner', '!=', 'no')
->orderBy('created_at', 'desc')
->get();
Thanks very much to Vitalik_74, I wouldn't have come that far without him.
Here is now the final query, although its not in ORM, but it is working fine and gives me the result I need.
$result = DB::select("SELECT *
FROM (
SELECT IF( receipient = '".$this->id."', sender, IF( sender = '".$this->id."', receipient, 'no' ) ) AS chatPartner, created_at
FROM chatmessages
WHERE receipient = '".$this->id."'
OR sender = '".$this->id."'
HAVING chatPartner != 'no'
ORDER BY created_at DESC
)whateva
GROUP BY whateva.chatPartner
ORDER BY whateva.created_at DESC");
if there is someone out there who can do this query with the Laravel Query Builder, I would be happy to see it. For now I'll leave it like this.

MySQL: Selecting something different if select doesnt return any rows

SELECT timestamp FROM table WHERE id = '10'
Is it possible to get the SELECT above to return a timestamp of '0000-00-00 00:00:00' if there is no id of '10' in the table?
Yes...
Use an If Exist / else statement. Your else can return the timestamp.
If you want to have a select for it, instead of handling it with if-else or from php/etc, the following trick should work:
SELECT COALESCE(t1.timestamp,t2.tst_hack) timestamp
FROM table t1
RIGHT JOIN (SELECT 10 as id, '0000-00-00 00:00:00' as tst_hack) t2 ON t1.id=t2.id;
(just note that it is really hacking, so you should have a good reason for it)
[update]
If there is a matching id and its timestamp is NULL, COALESCE will overwrite it to '0000-00-00 00:00:00' too. If it is a problem and it is important to keep the NULL value, then IF(t1.id IS NULL, t2.tst_hack, t1.timestamp) works better than COALESCE.
If you're using MySQLi :
<?php
$result = mysqli_query($conn, "SELECT timestamp FROM table WHERE id = 10");
/* determine number of rows result set */
$row_cnt = mysqli_num_rows($result);
if ($row_cnt == 0) {
echo '0000-00-00 00:00:00';
//or store as a variable to use else where.
$my_result = '0000-00-00 00:00:00';
} else {
// else if there are results, do something ...
}

Codeigniter And MYSQL DateTime

I Have a query as follows:
$query = $this->db->get_where('Bookings', array('Status' => 0, 'Driver_ID' => null, 'Date'=> 'NOW()'));
The Date field is a datetime type, and I want to be garbing records where their date is the same as today, however the above, and everything else I have tired does not work.
Can anyone one show me how to correctly pull records that date it today, ignoring the time part of datetime.
Thanks for any help
UPDATE
I Have now converted the query to the following
$start = date('Y-m-d 00:00:00');
$end = date('Y-m-d 23:59:59');
$query = $this->db->get_where('Bookings', array('Status' => 0, 'Driver_ID' => null, 'Date' => 'BETWEEN '.$start.' AND '.$end));
However still no luck, just retuning no results!
I would try
$this->db->where('Status', 0);
$this->db->where('Driver_ID', null);
$this->db->where('DATE(Date)', date('Y-m-d'), FALSE);
$query = $this->db->get('Bookings');
$params = array('Status' => 0, 'Booking_Date'=> date('Y-m-d'));
$this->db->query('SELECT * FROM Bookings WHERE Status=? AND Driver_ID Is Null AND Date(Booking_Date) = ?',$params);
Think that will work
In your code, you are saying you want a DateTime field to be NOW(). The problem is that NOW() gives a DateTime value, that is, a date in the form "YYYY-MM-DD" followed by a time in the form "HH:MM:SS".
What your query is doing is saying "Give me records where the Date is today, at this exact second". What you want is "Give me records where the Date is today".
This is why using DateTime fields in a database is usually cumbersome. You will have to convert your Date field to be just the date, without the time, using the MySQL function DATE(), and instead of NOW() which returns a DateTime value, you will want to use CURDATE() which returns only the Date value. I am not experienced with CodeIgniter specifically, but try:
$query = $this->db->get_where('Bookings', array('Status' => 0, 'Driver_ID' => null, 'DATE(Date)'=> 'CURDATE()'));
(I don't know if you can apply MySQL functions to fields with $this->db->get_where).
MySql standard datetime format is date("Y-m-d H:i:s").
So you query will need to get everything for the current day, so a where clause someything like;
SELECT * FROM `Bookings` WHERE `Date` BETWEEN date("Y-m-d 00:00:00") AND date("Y-m-d 23:59:59")