count of subquery in doctrine - querybuilder - php

I have simple query:
$this->qb->select('l.value')
->addSelect('count(l) AS cnt')
->addSelect('hour(l.time) AS date_hour')
->from(Logs::class, 'l')
->where('l.header = :header')
->groupBy('l.value')
->addGroupBy('date_hour')
->setParameter('header', 'someheader')
This code select 3 columns have one condition and two groupBy.
I want get records count of this query. Of course I dont want to download all records and check size of downloaded data.
Question:
How to rebuild this query and get result from db as singleScalarValue()?

I think you should use (id column for count) here:
->addSelect('count(l) AS cnt')
Something like this, but if you show your Entity i can suggest right solution:
$this->qb->select('l')
->addSelect('count(l.id) AS cnt')
->addSelect('hour(l.time) AS date_hour')
->from(Logs::class, 'l')
->where('l.header = :header')
->groupBy('l.value')
->addGroupBy('date_hour')
->setParameter('header', 'someheader')
$count = $qb->getQuery()->getSingleScalarResult();

Since GROUP BY + COUNT + getSingleScalarResult does not seem to work together in DQL queries, I guess your best option is to count afterwards
$sub
->select('l.id')
->addSelect('l.value as hidden val')
->addSelect('hour(l.time) AS hidden date_hour')
->from(Logs::class, 'l')
->where('l.header = :header')
->groupBy('val')
->addGroupBy('date_hour')
->setParameter('header', 'someheader')
;
$subIds = array_column($sub->getQuery()->getResult(), 'id');
$count = count($subIds);

Related

Query where_in comma separated list

I have a 'list_table' table looks like:
id : list
1 : 1,2,44,5
2 : 4,3,5,2,56,66
Is it possible to check if '44' is in List column in mysql database?
I'm using codeigniter and my code looks like:
$this->db->select('*');
$this->db->from("list_table");
$this->db->where("find_in_set('44', 'list')");
$query = $this->db->get();
return $query->result();
I also tried with WHERE_IN but didn't get correct result.
This is what query I get when I enable_profile:
SELECT *
FROM `poslovi`
LEFT JOIN `firme` ON `firme`.`f_id` = `poslovi`.`po_firma_id`
LEFT JOIN `kategorije` ON `kategorije`.`k_id` = `poslovi`.`po_category`
WHERE `po_date_istek` > '2022-03-21 10:37:25'
AND (`po_naziv_oglasa` LIKE '%Radnik u ćevabdžinici%' ESCAPE '!' OR `f_name`
LIKE '%Radnik u ćevabdžinici%' ESCAPE '!')
AND find_in_set("61", po_category) <> 0
AND `po_status` = '1'
ORDER BY `po_date_istek` DESC
LIMIT 10
This is what I have in my database:
Just to mention, if I remove 'find_in_set' I get correct result so the rest of the query is good as I noticed
You need a true or false condition in the WHERE clause, so a comparison
$this->db->select('*');
$this->db->from("list_table");
$this->db->where("find_in_set('44', 'list') <> 0");
$query = $this->db->get();
return $query->result();
But it ot recomended to store data this way . Read mor in Is storing a delimited list in a database column really that bad?
In a screenshot you posted of your data, its possible to see that you include a space after each comma.
The value of po_category is 2, 7, 61, not 2,7,61—and find_in_set does not ignore those spaces!
You've noticed that find_in_set works when you search for the first entry, this is because that does not have a leading space; 7 and 61 do.
find_in_set(" 61", po_category) would match, in this case, but then it wouldn't match if it is the first entry. While you could do (find_in_set("61", po_category) <> 0 || find_in_set(" 61", po_category)) <> 0 to support both cases, that is unnecessarily slow and taxing. Just save your data without spaces. Or, better yet, not as a comma separated list.
In nbk's answer there's a link that explains why doing this is not optimal. One way to save a list of IDs is making a separate table for them and using JOINs. This will be better for performance. Another option that is slightly more complex to implement, if you are using MySQL 8.0.17 or higher, is to save it as a JSON array & index that array.
As nbk said, you need the true/false condition, however, the answer is not working for the OP. You need to remove the single quotes around list in that answer:
$this->db->where("find_in_set('44', 'list') <> 0");
Rewrite the code as below, minus the quotes around list:
$this->db->select('*');
$this->db->from("list_table");
$this->db->where("find_in_set('44', list) <> 0");
$query = $this->db->get();
return $query->result();
That should solve the issue for you.

Convert complex query from Laravel to SQL

I'm trying to move some statistics logic entirely to SQL to vastly improve performance, however it's quite complex and I'm not sure how to achieve this using procedures/functions in SQL - or whether it's even possible.
Stats table looks like this:
Row in the table looks like this:
This is the code I'm trying to convert. $this->stats is an Eloquent Collection of all the rows in the table:
return $this->stats
->groupBy(function ($stat) {
return $stat->created_at->format('Y-m-d');
})
->map(function ($stats) {
$times = [];
for ($hour = 0; $hour < 24; $hour++) {
$thisHour = $stats->filter(function ($stat) use ($hour) {
return (int) $stat->created_at->format('H') === $hour;
});
$times[$hour] = $thisHour->isNotEmpty()
? $thisHour->sum(function ($stat) {
return $stat->data->count;
}) : 0;
}
return $times;
});
This outputs something like this:
{
"2018-12-20": {
0: 54,
1: 87,
2: 18,
3: 44,
4: 35,
...
}
}
So it's grouped by date, and each date contains 0-23 (the hours of the day) with the corresponding value (in this case the summation of the row's data->count property).
In a different query I've been able to get the summation of the data->count property by using this:
SELECT SUM(data->>\"$.count\") AS value
So is this even possible to do in SQL? I'm imaging there to be a date columns, plus the hours column, so hour_0, hour_1 etc. with the values underneath.
Any help would be greatly appreciated!
The first step I would take to understand how Laravel is doing it would be enabling query logging and dumping the query;
DB::connection()->enableQueryLog();
// ... do your query
$queries = DB::getQueryLog();
That will allow you to recreated the Eloquent query in plain SQL. From there, you can work on optimizing and condensing.
https://laravel.com/docs/5.0/database#query-logging
UDPATED
As you have now included the table structure, the following query should give you required results in tabular format. You need to parse the results to convert to json.
Updated SQL
SELECT DATE_FORMAT(stats.updated_at, "%Y-%m-%d") AS `date`,
HOUR(stats.updated_at) AS `hour`,
COUNT(stats.id) AS record_count
FROM stats
GROUP BY `date`, `hour`
ORDER BY 1 ASC, 2 ASC
The resulting tabular data would look similar to this.

Get count query results via getSingleScalarResult() and groupBy() - exception

I want get results of my query (with limit 10) + count possible results.
I know there is similar questions and answers.
for example here
but if i trying get count possible rows (via getSingleScalarResult()) i will get excepton: The query returned multiple rows. Change the query or use a different result function like getScalarResult().
$query = $repository
->createQueryBuilder('t')
->select('COUNT(t.katId)', 't.hotel', 't.title', 't.desc', 'picture', 'MIN(t.price) AS price');
$query->where('t.visible = (:visible)')->setParameter('visible', 1);
// + some wheres, where in, more than....
$query->groupBy('t.hotel');
$query->setMaxResults(10);
echo $query->getQuery()->getSingleScalarResult();
exit();
I just need one integer whitch represent all results from my query.
How can i get this count number? Ideal in one shot to db.
EDIT:
if i remove $query->groupBy('t.hotel'); and in select keep only ->select('count(t.katId)'); then it work. But i need groupBy because it makes real count of results.
SOLUTION
I divided it on two queries so - to get results i rolled back changes to state before trying any count information, and make clone this query (before set setMaxResults and groupBy), change select (keep all wheres) and get count information.
I will be grateful if someone offers better solution
Get results:
removed COUNT() from select
asking for results changed to 'normal' ->getArrayResults
Get count:
$q = clone $query;
$q->select('count(distinct t.hotel) as count');
$r = $q->getQuery()->getArrayResult();
echo $r[0]['count'];
exit();
If you need keep the groupBy:
$query = $repository->createQueryBuilder('t')
$query->select('COUNT(t.katId)', 't.hotel', 't.title', 't.desc', 'picture', 'MIN(t.price) AS price');
$query->from(ENTITY STRING, 't', 't.hotel'); //here defined your array result key
$query->where('t.visible = (:visible)')->setParameter('visible', 1);
$query->groupBy('t.hotel');
$query->setMaxResults(10);
echo $query->getQuery()->getScalarResult();
exit();
Edit : New edit works ?
You are only interested in COUNT(t.katId), so you should drop other returned fields 't.hotel', 't.title', etc.
The result will then contain a single return value (single scalar result), so $query->setMaxResults(10) is not needed.

SELECT COUNT(*) returns 1 even if the request should return 0

When I'm trying to get the number of rows on a SQL request and if not, the connection that follow fails.
But in any case (also if the request should return 0), it returns 1.
Here's my code :
$str = 'SELECT count(*) FROM admins WHERE mail = ? AND mdp = ?';
$arr = array($mail, $pass);
$rqt = sendRqt($str, $arr);
$tab = $rqt->fetchColumn();
$cnt = count($tab);
echo $cnt;
I don't understand why there's no time it returns 0
The problem is the use of the php function count().
You already have the correct number in your $tab variable as a string (probably, depends on php configuration / version) so you can echo it or cast it to an integer to make sure it is a number.
However, in php:
count(0) === 1
count('0') === 1
See here for example.
You should remove count($tab).
the SQL "COUNT(*)" allways returns a row with the count (quantity) of values, so if you apply the php count() function, always will return 1 because there is one row that contains the value of the COUNT sql function
I believe COUNT() needs a column name.WRONG!! count(*) should count all rows, but I still reccomend a column name like id or something. You could also use an AS to make life a little easier
$str = 'SELECT count(`COLUMNNAME`) AS cnt FROM table WHERE ....

Filtering MySQL query. "AND" operator

I'm getting excessive rows selected from the db. However, when i try to filter the data with AND lt_num it does not select anything at all. What could be the problem?
$w = $_POST['pal_num'];
$w = "'".implode("','",$_POST['pal_num'])."'";
$r = mysql_query("SELECT * FROM pl_tab WHERE pal_num AND lt_num in (".$weights.");");
My guess is you need to do two comparisons like this:
SELECT
*
FROM
pl_tab
WHERE
pal_num in (".$weights.")
AND lt_num in (".$weights.");
You can't say column1 and column2 = 3 for example. If you want rows where both are equal to 3, you would need to use column1=3 and column2=3 in your query.
Also, you might want to have a read of this Q&A that I wrote a while back which covers off a lot of basic SQL queries and expands from there.
Edit:
If you have $weights in the query, you do set it somewhere, or do you want it to be what is in your variables you show here:
$weights = $_POST['pal_num'];
$weights = "'".implode("','",$_POST['pal_num'])."'";
Is this what you meant?

Categories