Rails sql query difference between two dates more then 2 weeks - mysql

I have a table that has 2 columns "date_from" and "date_to". I have to make a query which should return results where "date_to" - "date_from" == "2 weeks".
Something like this:
User.where('date_to - date_from == 2.weeks').

Define this on user:
def self.two_weeks
where(date_to: date_from + 2.weeks)
end
Then you can just call User.two_weeks
Can also be done as a scope, if you prefer:
scope :two_weeks, -> { where(date_to: date_from + 2.weeks) }
It's good to keep these kinds of things on the model anyway, so that you can change them in one place if they need to be tweaked

This should work, for mysql at least
User.where('1 + DATEDIFF(date_to, date_from) >= 14')

Related

How to combine like and between in same statement (laravel Query Builder/model)

Following are the HDD column from the Computer model (I know it's not a good format to store data but it's already stored like that)
HDD
4x2TBSATA2
2x2TBSATA2
8x2TBSATA2
4x1TBSATA2
2x120GBSSD
4x480GBSSD
I want to fetch the range out of HDD column where storage is in a specific range, for example, fetch storage between 120GB to 1TB should output
4x1TBSATA2
2x120GBSSD
4x480GBSSD
I was wondering if it's possible to combine like and between in the same statement?
I tried the following which doesn't work.
select * from `server_details` where `HDD` between '%120GB%' and '%10TB%'
select * from `server_details` where `HDD` between "Like '%120GB%'" and "LIKE '%10TB%'"
If you need to do just in SQL, extract the size part, convert it to a number, and compare.
select *,
cast(`HDD` as unsigned)*
cast(substr(`HDD`,LOCATE('x',`HDD`)+1) as unsigned)*
(case when`HDD` LIKE '%TB%' then 1000 else 1 end) as GB
from `server_details`
where
cast(`HDD` as unsigned)*
cast(substr(`HDD`,LOCATE('x',`HDD`)+1) as unsigned)*
(case when`HDD` LIKE '%TB%' then 1000 else 1 end)
between 120 and 10000;
You can't use between with wildcard queries. You might be able to write a regular expression to match what you need, for example:
select * from `server_details` where `HDD` regexp '1[2-9]\dGB|[2-9]\d\dGB|\dTB|10TB'
but as you can see this is a very specific expression based on what you've written and each different limit will need a different expression.
There's some python code to generate such an expression but no PHP code that I could find (with some very basic googling)
Another solution (and what I would personally recommend) is to add the capacity as a separate column:
Migrate your current table:
class AddCapacityColumnMigration extends Migration {
public function up()
{
Schema::table('computers', function (Blueprint $table) {
$table->bigInt('capacityMB')->nullable();
});
Computer::chunk(100, function ($computers) {
foreach ($computers as $computer) {
if (preg_match('/(\d+)x(\d+)(M|G|T)B/',$computer->HDD,$m) {
$capacity = $m[1];
$capacity *= $m[3] === 'M' ? 1 : ($m[3] === 'G' ? 1000 : 1000000 );
$computer->capacityMB = $capacity * $m[2];
$computer->save();
}
}
});
}
Then you might want to add a creating and updating event in your model to ensure you always set the new capacityMB column. When all this is done your query is as simple as:
select * from `server_details` where `capacityMB` between 120000 and 10000000
In the database, in HDD column, you should not store alpha-numeric values like 120GB, 10TB, you should store numeric values like 120, 10000. Please try with the following query.
$hdds = DB::table('server_details')
->whereBetween('HDD', [120, 10000])
->get();

Using Peewee ORM with MySQL date math

I'm trying to convert an application that uses MySQLdb to use Peewee instead. Most of the SELECTs and INSERTs are no problem, but one class of query has me puzzled.
The original code contains:
sql = "SELECT * FROM {tbl} WHERE tail='{tail}' AND flight="+\
"'{flight}' AND dest='{dest}' AND orig='{orig}' AND "+\
"oooi='{oooi}' AND report_time > ('{time}' - INTERVAL 2 HOUR) "+\
"AND report_time < ('{time}' + INTERVAL 2 HOUR)"
cmd = sql.format(tbl = self.table, tail=tail, flight=flight, dest=dest,
orig=orig, time = report_time, oooi=oooi)
c.execute(cmd)
return c.fetchone()
Trying to rewrite that to use Peewee I've come up with:
oooi_rec = Oooi_rec.select().where(Oooi_rec.tail == self.tail,
Oooi_rec.flight == self.flight,
Oooi_rec.dest == self.dest, Oooi_rec.orig == self.orig,
Oooi_rec.oooi=self.oooi,
Oooi_rec.report_time.between(low, high))
The bit that replaces "low" and "high" is what has me mystified for now. I'm trying to puzzle out how to use Peewee's fn() but it's slow going.
Perhaps:
low - SQL('interval 2 hour')
Also, so much sql injection in the way you had previously...yeesh.

How to find the sum of multiple columns for each individual record in rails?

I am looking to find the sum of the values of multiple columns in my database. This is for a Fantasy Basketball app that provides statistical analysis of all the players in a Fantasy League. I am trying to add together 7 columns for each record in the DB. I am not sure whether it would be better to use the ActiveRecord querying methods or to use an actual SQL query. Here is the method that I was attempting to use within the SeasonPlayer model:
def self.find_player_value_totals
player_values = []
query = <<-SQL
SELECT id,( free_throw_value + three_pointer_value +
rebounds_value + assists_value + steals_value +
blocks_value + points_value )
AS value_total
FROM season_players
SQL
totals = find_by_sql(query)
totals.each do |total|
player_values << total.value_total
end
end
Using this query in my MySQL DB I can find the totals without any issues:
select id,( free_throw_value + three_pointer_value + rebounds_value + assists_value + steals_value + blocks_value + points_value ) AS value_total FROM season_players
Any advice would be greatly appreciated as I am not a Rails expert.
I don't quite get your question. But I think this is what you want to implement:
class SeasonPlayers
# assuming that you only want to aggregate from one player
def find_player_value_totals
[free_throw_val, three_pointer_val, rebound_val, assist_val, steals_val, blocks_val, points_val].sum
end
end
Hope that helps

Counting age at date in mysql, with rails methods

I'm looking to get a .count based format, by grouping a method on the model.
What I've tried is along the lines of
Model.all.group(:age_at_x).count
and
Model.all.group_by { |v| v.age_at_x }.count
With age_at_x defined in the model. I know that this tries to group in MySQL with a method, but I can't seem to figure out to actually group by the method?
def age_at_x
date = Time.new(2016, 6, 25)
dob = self.birth
date.year - dob.year - ((date.month > dob.month || (date.month == dob.month && date.day >= dob.day)) ? 0 : 1)
end
I'm looking to count the different ages, and see how many of each - not the number of ages.
Furthermore in the future, to count by 18+ and under 18.
If I understand your question, you are trying to count people by diferentes ages, try that:
Model.select(:birth).distinct.count

Error undefined local variable or method `created_at'

I write query to find out how much the user has reduced calories in a week, but I have this error.
How to avoid mistakes?
def self.calories_burned(current_user)
week = ((created_at - current_user.first_program_started_at.utc.beginning_of_day) / 86400 / 7).ceil.to_i || 1
find_by_sql("
SELECT
count(*) as cnt,
WEEK(#{week}) as week_number
FROM
user_daily_updates
WHERE
user_id=#{current_user.id}
GROUP BY
week_number
")
end
When you write this:
def self.calories_burned(current_user)
etc...
end
it's a method that can only be called like this:
MyUserModel.calories_burned(some_user)
In this case you are running the code on the class before it is instantiated, this means that the model hasn't even attached itself to a connection to the database and because of that you will not be able to access attributes that pertain to your model.
On the other hand, if you write this:
def calories_burned
etc...
end
You don't need to pass the user to the method, you simply call it on the controller after instantiating your model, like this:
id = 123
current_user = MyUserModel.find(id)
current_user.calories_burned
where current_user.calories_burned will return the value you are looking for based on the current user.
After taking a closer look at your method
it should look more like this:
def calories_burned
week = ((created_at - first_program_started_at.utc.beginning_of_day) / 86400 / 7).ceil.to_i || 1
UserDailyUpdate.where(["user_id = ?", id]).where(["week = ?",week]]).count(:all, :group => 'week_number')
end
If I understood correctly what you were trying to do with your query, this should give you the same result. Now, I should mention I am assuming that when you created the table user_daily_updates, you also created a model UserDailyUpdate.
You should take a look at ActiveRecord's documentation for searching using conditions (all of section 2) in order to have a better understanding of what I just did.
Apneadiving is right, but I would also look to refactor your code:
def self.calories_burned(current_user)
week = ((##created_at## - current_user.first_program_started_at.utc.beginning_of_day) / 86400 / 7).ceil.to_i || 1
select("#{self.table_name}.*, count(*) as cnt, WEEK(#{week}) as week_number").where(user_id: current_user.id).group(:week_number)
end
You'll need to pull created_at from the db, as it won't be present unless you have an instance of a class already loaded