using sum() in yii2 along with group by a related field - yii2

I have a table bills which have 3 column - partyname, billdate, billamount. And there is another table parties which have columns partyname and district. Both are related with partyname. I can join them and display the data in gridview. What I want is to show the data as below
District Billdate Billamount
Kolkata 2016-03-02 20000
Malda 2016-03-02 30000
Jalpaiguri 2016-03-02 30000
The Billamount column is the sum(amount) of all the parties belong to the particular district. I've read Yii2 - getting sum of a column, Yii 2 ActiveDataProvider query with ->all() gives "Call to a member function andFilterWhere() on array" error, Yii2 Query does not return column sum, Yii2 - getting sum of a column. But could not make it out. Please help.
My Search is
public function search($params)
{
$query = Bills::find();
$query->joinWith(['parties']);
//$command = Yii::$app->db->createCommand("SELECT sum(billamount) FROM bills");
//$sum = $command->queryScalar();
//echo $sum;
//$sum = $query->sum('billamount');
//echo $sum;
// $sum = (new \yii\db\Query())->from('bills');
// $sum = $sum->sum('billamount');
// echo $sum;
//$sum = $query->sum('billamount');
//echo $sum;
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);

Extend you model with a var for sum alias
class YourModel extends \yii\db\ActiveRecord
{
public $my_sum;
.....
then
$query = Bills::find()->select('partyname, billdate, sum(billamount) as my_sum)';
$query->joinWith(['parties']);
In your dataProvider you can then refer to the sum with my_sum

Related

How to optimize a laravel query, to find the average from another joined table

My query (DB::raw(AVG('pt.progress'))) this part is throwing error at the moment
$query = DB::table('projects as p')
->leftJoin('projects_tasks as pt','pt.project_id', 'p.id')
->select(
'p.id', 'p.project_name', DB::raw(AVG('pt.progress')) //this is where I need the average
);
$query->orderBy($order, $dir);
if ($limit != -1) {
$query->skip($start)->take($limit);
}
$records = $query->get();
Table structure:
projects:
========
id
project_name
...
...
projects_tasks:
===============
id
project_id,
parent, //0 or 1
progress //exmaple value 0.00 to 1.00
How to get the average of progress, where parent_id = 0 and project_id is the same?
The following query does work if I create a function and pass it in a loop, however, I want to optimize it and run it by joining on above query.
$data_row = DB::table('projects_tasks')
->select(DB::raw('(SUM(progress)*100)/count(progress) as project_progress'))
->where(['project_id' => $project_id, 'parent' => 0])
->get();
Seems that you have a syntax error on your query, the problem is in here DB::raw(AVG('pt.progress')).
Since you're using a raw query, the parameter there should be a string, so you must enclose that in quotes / double qoute.
$query = DB::table('projects as p')
->leftJoin('projects_tasks as pt','pt.project_id', 'p.id')
->select(
'p.id', 'p.project_name', DB::raw("AVG('pt.progress')")
);
$query->orderBy($order, $dir);
if ($limit != -1) {
$query->skip($start)->take($limit);
}
$records = $query->get();

How to convert sql query to codeigniter query

Can somebody help me convert this Sql Query
SELECT *
FROM customer c
LEFT JOIN customer_order co
ON c.customer_number = co.customer_number
AND co.order_status IN ('preparing', 'prepared')
WHERE c.customer_status='unpaid'
AND c.order_status = 'unserve'
AND co.cus_ord_no IS null
into Codeigniter query just like the image below for example
When query statements do not have clauses that need to change conditionally then using $this->db-query() is the way to go.
$sql = "SELECT * FROM customer c LEFT JOIN customer_order co
ON c.customer_number=co.customer_number AND co.order_status IN ('preparing', 'prepared')
WHERE c.customer_status='unpaid' AND c.order_status='unserve' AND co.cus_ord_no IS null";
$query = $this->db->query($sql)->result();
echo json_encode($query);
It might be wise to include a check on the return from query() though because if it fails (returns false) then the call to result() will throw an exception. One way that can be handled is like this.
$query = $this->db->query($sql);
if($query !== FALSE)
{
echo json_encode($query->result());
return;
}
echo json_encode([]); // respond with an empty array
Query Builder (QB) is a nice tool, but it is often overkill. It adds a lot of overhead to create a string that literally is passed to $db->query(). If you know the string and it doesn't need to be restructured for some reason you don't need QB.
QB is most useful when you want to make changes to your query statement conditionally. Sorting might be one possible case.
if($order === 'desc'){
$this->db->order_by('somefield','DESC');
} else {
$this->db->order_by('somefield','ASC');
}
$results = $this->db
->where('other_field', "Foo")
->get('some_table')
->result();
So if the value of $order is 'desc' the query statement would be
SELECT * FROM some_table WHERE other_field = 'Foo' ORDER BY somefield 'DESC'
But if you insist on using Query Builder I believe this your answer
$query = $this->db
->join('customer_order co', "c.customer_number = co.customer_number AND co.order_status IN ('preparing', 'prepared')", 'left')
->where('c.customer_status','unpaid')
->where('c.order_status','unserve')
->where('co.cus_ord_no IS NULL')
->get('customer c');
//another variation on how to check that the query worked
$result = $query ? $query->result() : [];
echo json_encode($result);
You can do
public function view_customers()
{
$sql = "SELECT * FROM customer c LEFT JOIN customer_order co ON c.customer_number = co.customer_number AND co.order_status IN ('preparing', 'prepared') WHERE c.customer_status='unpaid' AND c.order_status = 'unserve' AND co.cus_ord_no IS null";
return $this->db->query($sql)->result();
}
You can use row() for one output to object, or row_array() if one output but array. result() is multiple objects and result_array() is multiple arrays.
My way do usually is like this:
Controller:
public function view()
{
$this->load->model('My_Model');
$data = new stdclass;
$data->user_lists = $this->my_model->view_users(array('nationality'=>'AMERICAN'));
}
Model:
public function view_users($param = null) //no value passed
{
$condition = '1';
if (!empty($param)) { //Having this will trap if you input an array or not
foreach ($param as $key=>$val) {
$condition .= " AND {$key}='{$val}'"; //Use double quote so the data $key and $val will be read.
}
}
$sql = "SELECT * FROM users WHERE {$condition}"; //Use double quote so the data $condition will be read.
// Final out is this "SELECT * FROM users WHERE 1 AND nationality='AMERICAN'";
return $this->db->query($sql)->result();
}

How to retrieve value of data from database with SUM in one column

I have a function that retrieve values from database as json.I need some data from database and a value of SUM for certain column.When i not retrieve SUM from those value i get all required data but when i also include SUM function to get total of values from certain column i get only single object of value
MODEL
public function search_tickets($data){
$ticketDate = $data['ticket_date'];
$ticketBus = $data['ticket_bus'];
$ticketAgentId = $data['agent_id'];
$user = $this->db->select("
CONCAT_WS(' ', tp.firstname, tp.lastname) AS passenger_name,
tb.seat_numbers AS seat_number,
tb.id_no AS ticket_number,
fr.reg_no AS bus_number,
DATE_FORMAT(ta.start_date, '%d/%m/%Y') as ticket_date,
tb.price AS fare_paid,
tb.pickup_trip_location AS boarding,
tb.drop_trip_location AS dropping,
SUM(tb.price) AS total_fare
")
->from('tkt_booking AS tb')
->join('tkt_passenger AS tp', 'tb.tkt_passenger_id_no = tp.id_no' ,'full')
->join('trip_assign AS ta', 'ta.id = tb.trip_id_no' ,'full')
->join('trip_route AS tr', 'tr.id = tb.trip_route_id','full')
->join('agent AS a', 'a.agent_id = tb.agent_id','full')
->join('fleet_registration AS fr', 'fr.id = ta.fleet_registration_id','full')
->where('ta.start_date', $ticketDate )
->where('fr.reg_no', $ticketBus )
->where('tb.agent_id', $ticketAgentId )
->get()
->result();
return $user;
}
CONTROLLER
public function searchTickets(){
$data = $_POST;
$ticket=$this->booking_model->search_tickets($data);
if($ticket){
$result = array('tickets'=>$ticket);
} else {
$result = NULL;
}
print json_encode($result);
}
You should GROUP BY passenger id to get the total sum for each passenger:
$this->db->group_by('tb.tkt_passenger_id_no');

Mysql PDO (Getting total from all colums) [duplicate]

I'm new to php and I've searched for the past hour and read all the documentation I could find and nothing is helping. I have a table that has a bunch of rows of data. I'm trying to pick one column from the whole table and add them all together. Here is what I got. All this tells me is how many rows there are that match my query, not the total sum of column I want. Any help is appreciated.
$res1 = $db->prepare('SELECT sum(distance) FROM trip_logs WHERE user_id = '. $user_id .' AND status = "2"');
$res1->execute();
$sum_miles = 0;
while($row1 = $res1->fetch(PDO::FETCH_ASSOC)) {
$sum_miles += $row1['distance'];
}
echo $sum_miles;
You're only returning one row in this instance. Modify your summed column to have an alias:
SELECT SUM(distance) AS totDistance FROM trip_logs ....
Now you can can fetch the row -
$row = $res1->fetch(PDO::FETCH_ASSOC);
echo $row['totDistance'];
No need to loop.
You can use SUM() without explicitely grouping your rows because if you use a group function in a statement containing no GROUP BY clause, it is equivalent to grouping on all rows.
If however you want to use the SUM() function for something slightly more complicated you have to group your rows so that the sum can operate on what you want.
If you want to get multiple sums in a single statement, for example to get the distance for all users at once, you need to group the rows explicitely:
$res1 = $db->prepare("
SELECT
SUM(distance) AS distance,
user_id
FROM trip_logs WHERE status = '2'
GROUP BY user_id
");
$res1->execute();
while ($row = $res1->fetch(PDO::FETCH_ASSOC))
{
echo "user $row[user_id] has runned $row[distance] km.\n";
}
This will return the sum of distances by user, not for all users at once.
Try this if you are using a Class :
class Sample_class{
private $db;
public function __construct($database) {
$this->db = $database;
}
public function GetDistant($user_id,$status) {
$query = $this->db->prepare("SELECT sum(distance) FROM trip_logs WHERE user_id =? AND status =?");
$query->bindValue(1, $user_id);
$query->bindValue(2, $status);
try{ $query->execute();
$rows = $query->fetch();
return $rows[0];
} catch (PDOException $e){die($e->getMessage());}
}
}
$dist = new Sample_class($db);
$user_id = 10;
$status = 2;
echo $dist->GetDistant($user_id,$status);

MySQL - select missing dates from table?

Suppose you have the following table values:
date | value
2012-01-01 | 8
2012-01-02 | 3
2012-01-03 | 17
2012-01-09 | 100
2012-01-12 | 2
Now suppose you want to select all the dates between 2012-01-02 and 2012-01-12 and show their values if present. If you simply query the table for the appropriate date range, the dates that don't have values are going to be absent, for obvious reasons. Is there a way to fill in those dates in the query?
The obvious solution is to create a table dates that just stores a list of all dates that may come up, and then to select from the dates table and join values to it, but I'd like to have a solution that doesn't rely on creating a single-column table if I can.
Of note: there are existing questions on SO on this topic, but they are all from 2010 (at least the ones I found when searching were), and MySQL features have grown in that time; there may be a dynamic solution now. If that's not the case, and the dates table is still the best solution, then this question should be closed as a duplicate.
The lack of answers from others suggests to me that at the current time, it is not possible to traverse a range of dates in MySQL without a table that holds those dates. I have, however, written some code in PHP that I'm using to fill in the missing dates after the fact:
function formatResults($inbound, $from, $to) {
$results = array();
$count = 0;
// In order not to lose any results, we have to change how the results are referenced
$indexes = array();
$stats = array();
foreach ($inbound as $stat) {
// ['listindex'] is the date, renamed in the query
$stats[$stat['listindex']] = $stat;
}
// In a function in case you want to pop it out
function dateArray($from, $to) {
$begin = new DateTime($from);
$end = new DateTime($to);
$interval = DateInterval::createFromDateString('1 day');
$days = new DatePeriod($begin, $interval, $end);
$baseArray = array();
foreach ($days as $day) {
$dateKey = $day->format("Y-m-d");
$baseArray[] = $dateKey;
}
$baseArray[] = $to;
return $baseArray;
}
$indexes = dateArray($from, $to);
// Now all the rows we need to return are uniquely identified in $indexes
// So we traverse $indexes to create our results array, rather than relying on $inbound
foreach($indexes as $index) if ($index != '') {
$data = array();
// Make sure we do not run into any 'missing index' problems
if (!isset($stats[$index]))
$stats[$index] = array(
'listindex' => $index,
// ... populate full list of empty fields
);
foreach ($stats[$index] as $key => $value) {
$data[] = $value;
}
$results[$count] = $data;
$count++;
}
return $results;
}