Displaying data within a date range from a rails scaffold - mysql

This is in my controller:
#somethings = Something.find(:all)
And my count variable would display a break up of this data based on 'rca' as such:
#something_to_count = Something.count( :conditions => { :rca => "4X32W"})
#something_to_count2 = Something.count( :conditions => { :rca => "6X36W"})
..... etc to other count variables...
Simple as this is, I was trying to get it to display these stats/counts for all entries made from the 'Something' table within the last thirty days in one view and within a date range in another view. I have the default timestamps added by the scaffold function (created_at and updated_at) in my model.
My view would have to be split up into two pages. One page to accept a date range and display the count variables along with the data in a table and the other page to display just the same for the last thirty days. (i.e. def last_30_days and def within_dates would be the actions). I found a solution on line which has a date variable subtracted from 30 etc which did not seem to work. A full fledged working solution would be greatly appreciated. Please help. Also, how would I convert a simple date range entry into the same format as the datestamps?

You can create scopes to your querys, so you can reuse them.
My approach to your problem would be something like this:
scope :last_30_days, where("created_at between ? and ?", Date.today - 30, Date.today)
scope :within_dates, lambda { |start_date, end_date|
where("created_at between ? and ?", start_date, end_date)
}
scope :based_on_rca, lambda { |rca|
where(:rca => rca)
}
#something_to_count = Something.based_on_rca("4X32W").last_30_days.count
#something_to_count = Something.based_on_rca("4X32W").within_dates(Date.today - 4.month, Date.today - 1.month).count
Various scopes with small queries that can be chained to reflect on a larger one.
Regards.

Related

How to generate a list of unique years between several dates in Lumen/Laravel

i'm creating an API in Lumen and i need to create a method that will get dates from two colums on the same table and return any years that occur between those dates, returning them all as a single array.
So for instance, imagine a table column named start_date and another named end_date
start_date | end_date getAllYears() should return =>
[1983, 1984, 1985, 1986, 1987,
1999, 2000, 2001, 2002, 2003,
..., 2016]
1999-05-09 | 2002-04-03
1983-03-12 | 1987-09-23
2001-02-12 | 2016-11-27
Currently i have a method that manages to do this on other types of more specific queries, the major problem with this attempt, is that due to the sheer mass of SQL records that i'm retrieving, a method like this causes my request to time out every single time.
MY INEFFICIENT METHOD that makes little use of Lumen/Laravel
public function getAllYears(){
$dates = $this->model->select('data_ini', 'data_fim')->get();
$results = [];
foreach ($dates as $obj){
$carbonBegin = Carbon::createFromDate($obj->data_ini->year);
$carbonEnd = Carbon::createFromDate($obj->data_fim->year);
if($carbonEnd->year === 9999){
$carbonEnd->year = date('Y');
}
$carbonEnd->year++;
// Simple method that runs a DatePeriod method
$dateRange = $this->helper->createDateRange($carbonBegin, $carbonEnd);
$results = array_merge($results, $dateRange);
}
sort($results);
$cleanYears = array_unique($results);
if ($cleanYears == null)
return response()->json(['error' => true, 'errorCode' => '1008', 'message' => "No years found!"]);
else
return response()->json(['error' => false, 'years' => $cleanYears]);
}
So, the question is, how can i do this in a less expensive way so that my server doesn't time out on every request? Thank in advance for your help :)
NOTE: DB:raw is a no-go as my TL has forbidden me from using it anywhere on the API
Looks like you need the whereBetween:
$between = DB::table('theTable')->whereBetween('data_ini', ["str_to_date('2011-05-06','%Y-%m-%d')", "str_to_date('2011-05-06','%Y-%m-%d')"])->get();
With models:
$between = $this->model->whereBetween('data_ini', ["str_to_date('2011-05-06','%Y-%m-%d')", "str_to_date('2011-05-06','%Y-%m-%d')"])->get();
In the above, I am utilizing MySQL's built-in str_to_date
Hope this helps!

Using .where condition for results greater than an integer

Trying to do something pretty straightforward in a Rails controller. Right now, I am showing a user all results matching their user_id from a Scoreboard table.
I now want to adjust that to show results from their user_id but also only scores greater than the integer 0. So I changed my controller to:
def index
#scoreboards = Scoreboard.where(user_id: current_user.id, "score >= 0").order(score: :desc)
end
This receives a syntax error, so my comparison in the .where is probably wrong. How should I add this second condition?
I try to avoid putting these kind of database operations directly in the controller, because the model is the more appropriate place. I'd write three scopes in the model:
class Scoreboard < ActiveRecord::Base
#...
scope :for_user_id, ->(user_id) { where(user_id: user_id) }
scope :with_scores, -> { where('score > 0') }
scope :by_descending_score, -> { order(score: :desc) }
#...
end
...then in the controller, you'd merely write this:
Scoreboard.for_user_id(current_user.id).with_scores.by_descending_score
This keeps your controller thinner (and more readable) while potentially supporting re-use of these lookups in the most atomic fashion and keeping the database logic wholly contained in the model.
I would try this:
#scoreboards = Scoreboard.where(user_id: current_user.id)
.where("category >= 0").order(score: :desc)

How get sum of field in the related table in one query in Yii2

Everyone,
I have 2 tables, that related via third:
Structure looks like below:
Playlist: id, channel_id, name, date
Files: id, name, duration
Playlist-rel-Files: list_id, file_id
I'm using ActiveRecord in my app. When I use such query to get last playlist:
$pl = Playlist::find()
->where(['channel_id' => $model->channel_id])
->orderBy(['date' => SORT_DESC])
->limit(1)
->one();
Then I use such code to get all files, that related with last playlist:
$f = $pl->files;
All is OK, and I get Array of files, but I don't need the list of them, I need only SUM of files duration, to get this SUM, I will need to run through array in foreach, my question is:
how can I modify my first query to get Playlist data AND sum of duration of this playlist's files in one query ? is it possible ?
What you are looking for is a statistical query. You can define a new relation for your count e.g
public function getFileCount() {
return $this->hasMany(Files::className(), ['id' => 'file_id'])
->viaTable('playlist-rel-files', ['playlist_id' => 'id'])
->sum('duration');
}
This is then accessible using the magic property $fileCount and can be added to the query via with for eager loading i.e:
$pl = Playlist::find()
->with('fileCount')
->where(['channel_id' => $model->channel_id])
->orderBy(['date' => SORT_DESC])
->one();

How to lazy-load or eager-load the sum of associated table?

We build some object in our controller:
#sites = Site.find(:all, :conditions => conditions, :order => 'group_id ASC')
And then in the view (currently), we are doing something like:
#sites.each do |site|
%p Site Name
= site.name
- actual = site.estimates.where('date<?', Date.today).sum(:actual_cost)
%p
= actual
end
Basically like that.
And of course this fires off a query for the Sites and then a query for N sites returned. I know about eager-loading associations with #sites = Site.find(:all, :include => :estimates) but in this case it didn't matter because we're doing the SUM on the query which is special it seems.
How would I eager load the SUMs in such that I don't get all the crazy queries? Currently it's over 600...
provide your conditions & ordering in this query only, which will push the result into a Hash.
sites = Site.includes(:estimates).where('estimates.date < ? ', Date.today)
.order('sites.id ASC')
actual = 0
sites.map { |site|
site.estimates.map { |estimate| actual = actual + estimate.actual_cost }
}
From your explanation, I am assuming actual_cost is a column in estimate table.

CakePHP Model with "Between dates"

I have a large data set (over a billion rows). The data is partitioned in the database by date. As such, my query tool MUST specify an SQL between clause on every query, or it will have to scan every partition.. and well, it'll timeout before it ever comes back.
So.. my question is, the field in the database thats partitioned is a date..
Using CakePHP, how can I specify "between" dates in my form?
I was thinking about doing "start_date" and "end_date" in the form itself, but this may bring me two a second question.. how do I validate that in a model which is linked to a table?
If I am following you correctly:
The user must specify start/end dates for find queries generated from a form
You need to validate these dates so that, for example:
end date after start date
end date not centuries away from start date
You want validation errors appearing inline within the form (even though this isn't a save)
Since you want to validate these dates they will be harder to grab when they are tucked away inside your conditions array. I suggest trying to pass these in separately and then dealing with them later:
$this->Model->find('all', array(
'conditions' => array(/* normal conditions here */),
'dateRange' => array(
'start' => /* start_date value */,
'end' => /* end_date value */,
),
));
You should hopefully be able to handle everything else in the beforeFind filter:
public function beforeFind() {
// perform query validation
if ($queryData['dateRange']['end'] < $queryData['dateRange']['start']) {
$this->invalidate(
/* end_date field name */,
"End date must be after start date"
);
return false;
}
/* repeat for other validation */
// add between condition to query
$queryData['conditions'][] = array(
'Model.dateField BETWEEN ? AND ?' => array(
$queryData['dateRange']['start'],
$queryData['dateRange']['end'],
),
);
unset($queryData['dateRange']);
// proceed with find
return true;
}
I have not tried using Model::invalidate() during a find operation, so this might not even work. The idea is that if the form is created using FormHelper these messages should make it back next to the form fields.
Failing that, you might need to perform this validation in the controller and use Session::setFlash(). if so, you can also get rid of the beforeFind and put the BETWEEN condition array in with your other conditions.
if you want to find last 20 days data .
$this->loadModel('User');
//$this->User->recursive=-1;
$data=$this->User->find('all', array('recursive' => 0,
'fields' => array('Profile.last_name','Profile.first_name'),'limit' => 20,'order' => array('User.created DESC')));
other wise between two dates
$start = date('Y-m-d') ;
$end = date('Y-m-d', strtotime('-20 day'));
$conditions = array('User.created' =>array('Between',$start,$end));
$this->User->find("all",$conditions)
You could write a custom method in your model to search between the dates:
function findByDateRange($start,$end){
return $this->find('all',array('date >= '.$start,'data >= .'$end));
}
As far as validating, you could use the model's beforeValidate() callback to validate the two dates. More info on this here.
function beforeValidate(){
if(Validation::date($this->data['Model']['start_date'])){
return false;
}
if(Validation::date($this->data['Model']['end_date'])){
return false;
}
return parent::beforeValidate();
}
Does that answer your question?