How to use date condition in Symfony (doctrine + oracle)? - oracle

How to use date condition in Symfony (doctrine + oracle) ?
I have trying
$qb = $this->createQueryBuilder('la')
->select('la.id')
->andWhere("la.dateCreated >= TO_DATE(':a', 'yyyy-mm-dd')")
->setParameter('a', '2020-09-01')
->getQuery();
However is error: the query defines 0 parameters and you bound 1
Any Ideas?

In your code, I don't think there should be quotes on ':a', I'd put :
TO_DATE(:a, 'yyyy-mm-dd')
Beside, how about :
$qb = $this->createQueryBuilder('la')
->select('la.id')
->andWhere("la.dateCreated >= :a)
->setParameter('a', '2020-09-01')
->getQuery();

Related

EF Core 5 Sum difference between two date times

We have upgraded to net 5 / ef core 5, and many of our queries have needed "fixing" due to the changes that have been made.
In reality, the queries were horribly inefficient, because the LINQ could not be translated to SQL and now this has been brought to our attention; one solution is to force this to be worked on the client instead of the SQL Server... however I would like this to make this more efficient.
Below is a query that I can't find a way to do efficiently:
DateTime toDate = DateTime.Now.AddDays(NumberofDays + 1).Date;
MyInsights.AvailableHours = DatabaseContext.WorkCenterSchedules
.Where(x => x.ForWorkCenter.WorkCenterId == WorkCenterID && x.dateTo > DateTime.Now && x.dateTo < toDate)
.Sum(y => (y.dateTo - (DateTime.Now > y.dateFrom ? DateTime.Now : y.dateFrom)).TotalMinutes) / 60;
The error is:
This could be changed to the following:
DateTime toDate = DateTime.Now.AddDays(NumberofDays + 1).Date;
List<WorkCenterSchedule> schedules = DatabaseContext.WorkCenterSchedules
.Where(x => x.ForWorkCenter.WorkCenterId == WorkCenterID && x.dateTo > DateTime.Now && x.dateTo < toDate).ToList();
MyInsights.AvailableHours = schedules.Sum(y => (y.dateTo - (DateTime.Now > y.dateFrom ? DateTime.Now : y.dateFrom)).TotalMinutes) / 60;
However, this is far from ideal. I know EF Core isn't ideal for complex queries, but I feel like I am missing something obvious here, is there something I can do to improve this?
You might be able to leverage SQL Server's DATEDIFF function:
EF.Functions.DateDiffMinute(y.dateTo, DateTime.Now > y.dateFrom ? DateTime.Now : y.dateFrom)

Case When Like in Eloquent in Laravel

i'm new with eloquent laravel
curently i have working mysql query below to transform data
select * from `dsa_data_realisasi_resolver` where (CASE WHEN akun LIKE '%51%' THEN 51 ELSE 0 END ) and year(`tanggal`) = 2020 and `kdsatker` = 412772 group by `akun`
when i try to use eloquent with something like this
$realisasi=Realisasi::whereRaw('(CASE WHEN akun LIKE '%51%' THEN 51 ELSE 0 END )')
->whereYear('tanggal','2020')
->where('kdsatker','412772')
->groupby('akun')
->get();
dd($realisasi);
there are errors
ErrorException
A non-numeric value encountered
when i try to remove single quotes '
$realisasi=Realisasi::whereRaw('(CASE WHEN akun LIKE %51% THEN 51 ELSE 0 END )')
->whereYear('tanggal','2020')
->where('kdsatker','412772')
->groupby('akun')
->get();
dd($realisasi);
the errors are
SQLSTATE[42000]: Syntax error or access violation: 1064 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 '%51% THEN 51 ELSE 0 END ) and year(`tanggal`) = ? and `kdsatker` = ? group by `a' at line 1 (SQL: select * from `dsa_data_realisasi_resolver` where (CASE WHEN akun LIKE %51% THEN 51 ELSE 0 END ) and year(`tanggal`) = 2020 and `kdsatker` = 412772 group by `akun`)
is there any solution with eloquent for query with case when like '%data%'
Thanks
Try with "(CASE WHEN akun LIKE '%51%' THEN 51 ELSE 0 END )" Notice the double quote to contain the whereRaw statement.
And to send dynamic data you can try "(CASE WHEN akun LIKE '%".data."%' THEN 51 ELSE 0 END )"
So your query should look like this:
$realisasi=Realisasi::whereRaw("(CASE WHEN akun LIKE '%".$data."%' THEN 51 ELSE 0 END )')
->whereYear('tanggal','2020')
->where('kdsatker','412772')
->groupby('akun')
->get();
MySQL requires the like pattern to be inside single quotes. For more reference: https://dev.mysql.com/doc/refman/8.0/en/pattern-matching.html

Spring boot 2 #Query named parameter binding value resolution messes up after upgrade from 1.5

We have the following working query using SpringBoot 1.5:
#Query(value = "SELECT DISTINCT c FROM Customer c INNER JOIN c.industry i WHERE " +
"c.role IN :roleFilter " +
"AND (:#{#industryFilter.size()} = 1 OR i.id IN :industryFilter) " +
"AND (:searchString IS NULL " +
"OR CONCAT_WS(' ', c.name, c.name2) LIKE CONCAT('%', :searchString, '%') " +
"OR CONCAT_WS(' ', c.name2, c.name) LIKE CONCAT('%', :searchString, '%')) " +
"AND (:includeDeleted = true OR c.deletedDate is NULL)",
countQuery = "SELECT COUNT(DISTINCT c) FROM Customer c INNER JOIN c.industry i WHERE " +
"c.role IN :roleFilter AND " +
"(:#{#industryFilter.size()} = 1 OR i.id IN :industryFilter) " +
"AND (:searchString IS NULL " +
"OR CONCAT_WS(' ', c.name, c.name2) LIKE CONCAT('%', :searchString, '%') " +
"OR CONCAT_WS(' ', c.name2, c.name) LIKE CONCAT('%', :searchString, '%')) " +
"AND (:includeDeleted = true OR c.deletedDate is NULL)")
Page<Customer> findCustomers(#Param("roleFilter") Set<Role> roleFilter,
#Param("industryFilter") Set<String> industryFilter,
#Param("searchString") String searchString,
#Param("includeDeleted") boolean includeDeleted, Pageable pageable);
Please note how we pass the input to the LIKE: CONCAT('%', :searchString, '%')
After upgrading from springBootVersion = '1.5.17.RELEASE' to springBootVersion = '2.1.3.RELEASE' (we use Gradle) that query will fail at runtime with an exception:
org.hibernate.QueryException: Named parameter not bound : includeDeleted
Replacing CONCAT('%', :searchString, '%') with %:searchString% fixes the problem.
The question I have is: why?
By going into debug mode and following the full callstack, I could see the parameters being correctly retrieved from the method invocation as observed in JdkDynamicAopProxy at line 205 makes a call Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); that results in:
argsToUse = {Object[5]#15562}
0 = {HashSet#15491} size = 4
1 = {HashSet#15628} size = 1
2 = null
3 = {Boolean#15629} false
4 = {PageRequest#15490} "Page request [number: 0, size 20, sort: name: ASC,name2: ASC]"
So far so good. Then, we keep going and the method to call is also correctly resolved:
parameterTypes = {Class[5]#15802}
0 = {Class#198} "interface java.util.Set"
1 = {Class#198} "interface java.util.Set"
2 = {Class#311} "class java.lang.String"
3 = {Class#15811} "boolean"
4 = {Class#9875} "interface org.springframework.data.domain.Pageable"
Then we go a bit further and we get to RepositoryFactorySupport line 599 calling private Object doInvoke(MethodInvocation invocation) throws Throwable which uses private final Map<Method, RepositoryQuery> queries; from the inner class public class QueryExecutorMethodInterceptor implements MethodInterceptor (I am unsure when/how was this variable created and populated), which contains all the queries annotated with #Query in my repository interface.
For our specific case, it contains an entry (last one) that matches the query I am invoking (findCustomers):
queries = {HashMap#16041} size = 3
0 = {HashMap$Node#16052} "public abstract com.swisscom.psp.domain.Customer com.swisscom.psp.repository.CustomerRepository.getOne(java.lang.String)" ->
1 = {HashMap$Node#16055} "public abstract boolean com.swisscom.psp.repository.CustomerRepository.existsWithRole(java.lang.String,java.util.Set)" ->
2 = {HashMap$Node#16058} "public abstract org.springframework.data.domain.Page com.swisscom.psp.repository.CustomerRepository.findCustomers(java.util.Set,java.util.Set,java.lang.String,boolean,org.springframework.data.domain.Pageable)" ->
And expanding that entry I can see where the error comes from, the binding for the :includeDeleted named parameter is simply not there:
value = {SimpleJpaQuery#16060}
query = {ExpressionBasedStringQuery#16069}
query = "SELECT DISTINCT c FROM Customer c INNER JOIN c.industry i WHERE c.role IN :roleFilter AND (:__$synthetic$__1 = 1 OR i.id IN :industryFilter) AND (:searchString IS NULL OR CONCAT_WS(' ', c.name, c.name2) LIKE CONCAT('%', :searchString, '%') OR CONCAT_WS(' ', c.name2, c.name) LIKE CONCAT('%', :searchString, '%')) AND (:includeDeleted = true OR c.deletedDate is NULL)"
bindings = {ArrayList#16089} size = 6
0 = {StringQuery$InParameterBinding#16092} "ParameterBinding [name: roleFilter, position: null, expression: null]"
1 = {StringQuery$ParameterBinding#16093} "ParameterBinding [name: __$synthetic$__1, position: null, expression: #industryFilter.size()]"
2 = {StringQuery$InParameterBinding#16094} "ParameterBinding [name: industryFilter, position: null, expression: null]"
3 = {StringQuery$ParameterBinding#16095} "ParameterBinding [name: searchString, position: null, expression: null]"
4 = {StringQuery$ParameterBinding#16096} "ParameterBinding [name: searchString, position: null, expression: null]"
5 = {StringQuery$ParameterBinding#16097} "ParameterBinding [name: searchString, position: null, expression: null]"
Now, I have the fix as mentioned earlier, but I would still very much like to know the following for future reference:
when and how is the private final Map<Method, RepositoryQuery> queries variable created and populated?
what exactly is causing this error? Did I miss something in the upgrade process? Am I using/mixing deprecated logic/wrong logic and should change the code further?
Our DB is MariaDB 10.1.36
EDIT: In all the places where this behaviour occurred (in some it still occurs), the unbound parameter is always the last one
EDIT2: Someone else also has a similar behaviour after the upgrade, why does this happen? reference
EDIT3: reference and also this weird behaviour has been reported. Interesting enough, I do not get the exception IF I pass already concatenated input to :searchString (eg: %SOMETHING%) and I do get the exception if I leave %:searchString% instead. And yes, moving those parameters in the end solves some errors I had with binding.
EDIT4: Maybe related bug?
Clearly there is something strange going on, so: how does this binding resolution happen exactly?
Thanks in advance and have a nice day
Actually, as far as I know, neither of your two approaches is the correct one to use here for handling LIKE with a wildcard placeholder. Instead, the LIKE expression should be:
LIKE :searchString
To this parameter :searchString you should be binding:
String searchString = "bananas";
String param = "%" + searchString + "%";
// then bind param to :searchString
That is, you bind the entire string, with the % wildcard, together. Then, let the database worry about how to escape it.

Query Builder reading conditional like a column - Unknown column in field list

I have a MySQL expression that uses a conditional IF statement. As a MySQL expression, it returns TRUE or FALSE, but when I use it in CodeIgniter's query builder, I get an error. The error suggests that the outcome of the conditional is reading like a column, but how can I fix this? Thanks.
MySQL:
SELECT
positions.max_vol AS attendee_limit,
COUNT(users_positions.user_id) AS total_attendees,
IF(COUNT(users_positions.user_id) < positions.max_vol
OR positions.max_vol IS NULL,
TRUE,
FALSE) AS result
FROM
positions
INNER JOIN
users_positions ON positions.id = users_positions.position_id
WHERE
positions.id = 16
AND users_positions.calendar_date = '2016-09-05'
Function:
private function check_attendee_limit($pos_id = NULL, $date = NULL)
{
$this->db->select('positions.max_vol, COUNT(users_positions.user_id), IF(COUNT(users_positions.user_id) < positions.max_vol OR positions.max_vol IS NULL, TRUE, FALSE)');
$this->db->from('positions');
$this->db->join('users_positions', "positions.id = users_positions.position_id", 'inner');
$this->db->where('positions.id', $pos_id);
$this->db->where('users_positions.calendar_date', $date);
$query = $this->db->get();
return $query->result(); // return the rows selected
}
Error:
A Database Error Occurred
Error Number: 1054
Unknown column 'TRUE' in 'field list'
SELECT `positions`.`max_vol`, COUNT(users_positions.user_id), IF(COUNT(users_positions.user_id) < positions.max_vol OR positions.max_vol IS NULL, `TRUE`, FALSE)
FROM `positions`
INNER JOIN `users_positions` ON `positions`.`id` = `users_positions`.`position_id`
WHERE `positions`.`id` = '15'
AND `users_positions`.`calendar_date` = '2016-09-05'
Filename: models/projects/Calendar_model.php
Line Number: 141
CI adds backticks - which means you've to prevent CI from escaping your select
Try this instead
$this->db->select('positions.max_vol, COUNT(users_positions.user_id), IF(COUNT(users_positions.user_id) < positions.max_vol OR positions.max_vol IS NULL, TRUE, FALSE)', false);
If you are worry about security - in your case it doesn't matter because you don't use any user input in your select query.
You can find more informations about the Query Builder here

Laravel 5 where condition to get pass 30 days worth of records

I have a date column in the instances table with varchar datatype stored as d-m-Y. In my where condition I am trying to fetch records for just past 30 days only.
$backdate = Carbon::parse('-30 days')->toDateString();
$date30DaysBack = Carbon::parse($backdate)->format('d-m-Y');
$adverts = DB::table('adverts')
->where(DB::raw('STR_TO_DATE(instances.date,"%d-%m-%Y")'), '>=',$date30DaysBack)
The full query
$adverts = DB::table('adverts')
->select(DB::raw('(SELECT IF(ext = \'jpeg\', CONCAT(fullpath, \'_1.\', ext), (CONCAT(fullpath,\'.\',ext))) as fullpath FROM advertsstorage where uid_dir = adverts.ad_uid ORDER BY id ASC limit 1)as fullpath, adverts.*, domains.location,instances.date'))
->join('domains','adverts.domain', '=' ,'domains.domain')
->join('advertiser_domains','domains.id', '=' ,'advertiser_domains.domain_id')
->join('advertisers','advertiser_domains.advertiser_id', '=' ,'advertisers.u_id')
->join('instances','adverts.ad_uid','=','instances.ad_uid')
->join('urls','instances.u_id','=','urls.id')
->join('sites','urls.sites_id','=','sites.id')
->where('advertisers.u_id', '=',$advertiserID)
->where(DB::raw('STR_TO_DATE(instances.date,"%d-%m-%Y")'), '>=',Carbon::now()->subDays(30)->format('d-m-Y'))
->orderBy(DB::raw('STR_TO_DATE(instances.date,"%d-%m-%Y")'), 'DESC')
->get();
Did you try DATEDIFF method
$adverts = DB::table('adverts')
->select(DB::raw('(SELECT IF(ext = \'jpeg\', CONCAT(fullpath, \'_1.\', ext), (CONCAT(fullpath,\'.\',ext))) as fullpath FROM advertsstorage where uid_dir = adverts.ad_uid ORDER BY id ASC limit 1)as fullpath, adverts.*, domains.location,instances.date'))
->join('domains','adverts.domain', '=' ,'domains.domain')
->join('advertiser_domains','domains.id', '=' ,'advertiser_domains.domain_id')
->join('advertisers','advertiser_domains.advertiser_id', '=' ,'advertisers.u_id')
->join('instances','adverts.ad_uid','=','instances.ad_uid')
->join('urls','instances.u_id','=','urls.id')
->join('sites','urls.sites_id','=','sites.id')
->where('advertisers.u_id', '=',$advertiserID)
->where(DB::raw('DATEDIFF( now(), STR_TO_DATE(instances.date,"%d-%m-%Y") )'), '<=', 30)
->orderBy(DB::raw('STR_TO_DATE(instances.date,"%d-%m-%Y")'), 'DESC')
->get();
Have you checked out the Carbon docs?
You can use the subtraction modifier for this; Carbon::now()->subDays(30);.
Would look something like this in your code:
$adverts = DB::table('adverts')->where(DB::raw('STR_TO_DATE(instances.date,"%d-%m-%Y")'), '>=', Carbon::now()->subDays(30)->format('d-m-Y'))
Try this.
Carbon has subDays function by which you can get past 30 days data
$date30DaysBack= Carbon::now()->subDays(30)->format('d-m-Y');
$today = Carbon::now()->format('d-m-Y');
$adverts = DB::table('adverts')
->whereBetween(DB::raw('STR_TO_DATE(instances.date,"%d-%m-%Y")'),[$date30DaysBack,$today])->get()

Resources