Symfony 2 Multiple Selects with counts on the same table? - php

Ok I have a flag field on one table, open or closed which is boolean. I am trying to build one query that would take that field and count them based on that flag. Then I will need to group them by account ID
Here is what I am working with now,
$GetTest1 = $GetRepo->createQueryBuilder('s') <- I had 'w' in here but all that did was add an index and not a second alias?
->select(' (count(s.open_close)) AS ClosedCount, (count(w.open_close)) AS OpenCount ')
->where('s.open_close = ?1')
//->andWhere('w.open_close = ?2')
->groupBy('s.AccountID')
->setParameter('1', true)
//->setParameter('2', false)
->getQuery();
Is what I want do-able? I know (or at lest think) that I can build a query with multiple table alias? - Please correct me if I am wrong.
All help most welcome.
Thanks

This DQL query will group the rows in table by accountId and for each of them it will give you count for yes (and you can get count for no by substracting that from total).
BTW I found writing straight DQL queries much more straightforward than writing QueryBuilder queries (which i use only when i need to dynamically construct the query)
$results = $this->get("doctrine")->getManager()
->createQuery("
SELECT t.accountId, SUM(t.openClose) as count_yes, COUNT(t.accountId) as total
FROM AppBundle:Table t
GROUP BY t.accountId
")
->getResult();
foreach ($results as $result) {
//echo print_r($result);
//you can get count_no as $result["total"] - $result["count_yes"];
}

Related

Yii2 query with all() returns less data than expected

I want to find all data from database with this query:
$dataSearch = House::find()
->select(["number","DATE(reg_date) AS date","((value) > 100) AS result","info"])
->join("INNER JOIN", Owner::tableName(), Owner::tableName().'.number_ow= '.House::tableName().'.number_ow')
->andWhere([House::tableName().'.number_ow'=>$house->number_ow])
->andFilterWhere([House::tableName().'.space'=>$space])
->orderBy([House::tableName().'.reg_date'=>SORT_DESC,'info'=>SORT_ASC]);
$data = $dataSearch->asArray()->all();
If I perform the query with all() return only a few part of the results, but if I perform the raw SQL query on the database, I get all the results. I don't understand where the problem is.
This is raw SQL:
SELECT `number`, DATE(reg_date) AS date, ((value) > 100) AS result, `info`
FROM `ho_house` INNER JOIN `ho_owner` ON ho_owner.number_ow = oh_ho_house.number_ow
WHERE (`ho_house`.`number_ow`=2100174106) AND (`ho_house`.`space`='m')
ORDER BY `ho_house`.`reg_date` DESC, `info`
That is because ActiveQuery tries to remove duplicated models from results of query with JOIN (for example if house have 2 owners, house data will be repeated twice in results set). In your case, if you want to raw data from database, you should use Query:
$dataSearch = (new Query())
->select(["number","DATE(reg_date) AS date","((value) > 100) AS result","info"])
->from(House::tableName())
->join("INNER JOIN", Owner::tableName(), Owner::tableName().'.number_ow= '.House::tableName().'.number_ow')
->andWhere([House::tableName().'.number_ow'=>$house->number_ow])
->andFilterWhere([House::tableName().'.space'=>$space])
->orderBy([House::tableName().'.reg_date'=>SORT_DESC,'info'=>SORT_ASC]);
$data = $dataSearch->all();
But if you really need ActiveQuery, you should configure $indexBy to return unique ID for every row.
Replace andWhere with Where Check this code
$dataSearch = House::find()
->select(["number","DATE(reg_date) AS date","((value) > 100) AS result","info"])
->join("INNER JOIN", Owner::tableName(), Owner::tableName().'.number_ow= '.House::tableName().'.number_ow')
->where([House::tableName().'.number_ow'=>$house->number_ow])
->andFilterWhere([House::tableName().'.space'=>$space])
->orderBy([ClosedOperation::tableName().'.reg_date'=>SORT_DESC,'info'=>SORT_ASC]);
$data = $dataSearch->asArray()->all();

Counting multiple vote choices in Doctrine with createQueryBuilder

I'm working on a voting system, where user will be able to vote either yes, no or not sure to a given question.
These are being stored in a voteOptions table as choice with a value of either 0, 1 and 2 which is attached to a vote table that stores additional information. I struggling to work out how to count up the votes in a single function.
The function below counts a single choice, which I currently call once for each voteOption. I'm counting distinct users as they can appear more than once, but only their last vote counts:
public function countPollVotesByIsCurrentAndChoice(Poll $poll, $choice) {
return $this->createQueryBuilder('v')
->leftJoin('v.voteOptions', 'vo')
->andWhere('v.poll = :poll')
->andWhere('v.isCurrentVote = :isCurrentVote')
->andWhere('vo.choice = :choice')
->setParameters(['isCurrentVote' => true, 'poll' => $poll, 'choice' => $choice])
->select('COUNT(DISTINCT v.user) AS vote_count')
->getQuery()
->getSingleScalarResult();
}
But I'd prefer to make a single call to get all 3 counts. I would really appreciate any advice about how best to do this.
You have to use COUNT() together with GROUP BY which will aggregate the choices to three possible answers and the COUNT function counts the number of records for each answer.
$query = $em->createQuery('SELECT count(vo.choice) AS cnt FROM AppBundle:Vote v JOIN v.voteOptions vo WHERE ... GROUP BY vo.choice');
$result = $query->getResult();
This query will not return an array with Vote objects but just an array with rows that contains columnames and values. print_r() will help you

Laravel - Merge two Queries?

I am building a Twitter-like application which has a feed in it. In this feed I need to display shares with this properties:
-Shares from user, who I am following
-Shares from user, which are sorted by the positive rating, but only the top10%
These two queries I need somehow to merge, so it will become an array in the end, which has all the shares which are applying to this criteria, without any duplicates and ordered by ID, desc
My tables are looking like this:
User, Shares, Follows
Shares:
-user_id
-positive
Follows:
-follower_id
-user_id
-level
What I already tried:
$follower_shares = Share::join('follows', 'shares.user_id', '=', 'follows.user_id')
->where('follows.follower_id', Auth::user()->id)
->where('follows.level', 1)
->get(array('shares.*'));
//count = 10% of total shares
$count = Share::count()/10;
$count = round($count);
$top10_shares = Share::orderBy('positive', 'DESC')
->take($count)
->get();
//sorts by id - descending
$top10_shares = $top10_shares->sortBy(function($top)
{
return -($top->id);
});
//merges shares
$shares = $top10_shares->merge($follower_shares);
The problem is now, that I was told that there is a better way to solve this.
Also, $shares is giving me the result which applies to the criteria, but the shares have duplicates (rows, which are applying to both criteria) and arent ordered by id desc in total.
I would be very happy, if you could tell me, how to do this the right way.
Thanks very much!
I found this to be a pretty clean solution:
// Instead of getting the count, we get the lowest rating we want to gather
$lowestRating = Share::orderBy('positive', 'DESC')
->skip(floor(Share::count() * 0.1))
->take(1)->lists('positive');
// Also get all followed ids
$desiredFollow = Auth::user()->follows()->lists('user_id');
// Select where followed or rating higher than minimal
// This lets the database take care of making them distinct
$shares = Share::whereIn('user_id', $desiredFollow)
->orWhere('positive', '>=', $lowestRating[0])
->get();

codeigniter select distinct function not working

I have a table with this structure:
opinion_id, author_id, title, content
I would like to get all the latest records from the table, one record for author, that means the latest record for every author...
My distinct function does not seem to be working...
function getOpinions() {
$data = array();
$this->db->select('*');
$this->db->from('opinions');
$this->db->join('authors', 'opinions.author_id = authors.author_id');
$this->db->order_by('date', 'desc');
$this->db->distinct('author_id');
$Q = $this->db->get();
if ($Q->num_rows() > 0) {
foreach($Q->result_array() as $row) {
$data[] = $row;
}
}
$Q->free_result();
return $data;
}
In Codeigniter, distinct does not work the way you expect it by field name. If you look at the manual - there is no argument for distinct. If you look at the code, it only takes a boolean, which defaults to true. It just adds the DISTINCT keyword to the query after the SELECT keyword. That's it.
In your case, I think it would be better to use a GROUP BY as in
$this->db->group_by('opinions.author_id');
Hopefully the order by would work as per your need in this instance by ordering before the grouping.
Cheers!
EDIT - update after OP comments
I know the ordering can be messed up - I sort of mentioned it :)
Anyway, I might be assuming some of your table structure here, but this would force the GROUP BY to pick the rows on the top. I assume that the date is on the opinions table and you only want the latest row from that with author details.
SELECT * FROM `authors`
JOIN (
SELECT * FROM opinions
ORDER BY `date` DESC
) AS ops ON ops.author_id = authors.author_id
GROUP BY ops.author_id
You will not be able to construct this query on active record though. Hope this helps.

CodeIgniter db select returns different result than SQL query

I wanted to use the CI Active Record methods to perform this query, but it gives me different results than if I execute the query in plain SQL. This is the query:
SELECT B.id as id
FROM default_log_workout A, default_log_workout B
WHERE A.id=$id AND B.completed < A.completed AND A.workout_id=B.workout_id
ORDER BY B.completed desc
LIMIT 1
CodeIgniter:
$this->db->select("B.id as id");
$this->db->from("log_workout A, log_workout B");
$this->db->where(array("A.id" => $id, "B.completed < A.completed", "A.workout_id=B.workout_id"));
$this->db->order_by("B.completed desc");
$this->db->limit(1);
$res = $this->db->get();
The query should return the next older row based on a given "id". The plain SQL works, the CI calls end up returning the OLDEST row, not the next oldest. I figure it's just a syntax error in my CI calls... but I can't figure it out. I've since moved on to solve the problem with $this->db->query("the SQL") but this is still bugging me.
Anyone know why the CI version doesn't work?
The ORDER BY clause should have two parameters:
$this->db->order_by('B.completed', 'DESC');
I'm not sure if Active Record will parse your comma separated table string in your from() clause. I suggest rewriting it in an ANSI standard from ... join ... on syntax.
$this->db->select("B.id as id");
$this->db->from("log_workout A");
$this->db->join("log_workout B","A.workout_id = B.workout_id AND B.completed < A.completed");
$this->db->where("A.id" => $id);
$this->db->order_by("B.completed", "desc");
$this->db->limit(1);
$res = $this->db->get();
And, as Yan pointed out, the order by should have two parameters.

Categories