Rails Sql Query for MONTH AND YEAR - sql

I'm trying to show sales transactions for the current month and year in my index view.
This is what I've put in my sales controller:
def index
#sales = show_sales_for_current_month(#sales)
which uses this method in the SalesHelper
def show_sales_for_current_month(sales)
sales = Sale.find(:all,
:conditions => ["MONTH(date) = ? AND YEAR(date) =?",
Date.today.month, Date.today.year])
end
where date is a date data type.
but i'm getting the following controller error:
SQLite3::SQLException: no such function: MONTH: SELECT "sales".* FROM "sales" WHERE (MONTH(date) = 4 AND YEAR(date) =2011)
I've looked around at posts and it seems like that is the correct function, so what am I doing wrong? Thanks!

Hey there, I have a gem called by_star which could help you with these kinds of queries. In your case you would only need to do this in your controller:
#sales = Sale.by_month
by_star takes care of working out what month and year it is, as well as the messy SQL.

The functions MONTH and YEAR do not exist in SQLite3. You can take an approach like this (taken from my current project):
model entry.rb:
def self.all_entries_year(year, user_id)
where('entries.user_id = :id', :id => user_id ).
where(':first_day <= entries.date AND entries.date <= :last_day', {
:first_day => Date.new(year, 1, 1),
:last_day => Date.new(year, 12, 31)
}).
order('date desc')
end
EDIT:
Put this in your model: sales.rb (I assume, it has the field date)
def self.show_sales_for_current_month(year, month)
mydate = Date.new(year, month, 1)
where(':first_day <= sales.date AND sales.date <= :last_day', {
        :first_day => mydate,
        :last_day => mydate.at_end_of_month
  }).
order('date')
end

In MySQL this works, but you are using SQLite3 so you need to modify your query to use the SQLite3 version of MONTH and YEAR, which means using the strftime function:
sales = Sale.find(:all,
:conditions => ["strftime('%m', date) = '?' AND strftime('%Y', date) = '?'",
'%02d' % Date.today.month, Date.today.year])

Related

Replicate SQL result in Linq

I have a simple view:
CREATE VIEW [dbo].[ApplicationSummary]
AS
SELECT
CONVERT(VARCHAR(50), NEWID()) AS ID,
ISNULL(AVG(ApplicationTime), 0) AS 'AvgApplicationTime',
ISNULL(AVG(ResponseTime), 0) AS 'AvgResponseTime',
ISNULL(CAST(1.0 * COUNT(CASE WHEN [IsAccepted] = 1 THEN 1 END) / COUNT(*) AS float), 0) AS 'PctAccepted'
FROM
[Application]
WHERE
(IsValid = 1)
AND (CreatedOn < CAST(GETDATE() AS date)
AND CreatedOn >= CAST(GETDATE()-30 AS date))
The idea is that it outputs 3 variables. The first 2 are simple 'averages', whereas the last one, 'PctAccepted' outputs a ratio.
I'm testing a table containing 4 rows — 3 of them are set to IsAccepted = true, so the result is 0.75 (3/4), which converts to 75%.
I'm trying to remove the need for a view and replicate it using Linq over my Entity Framework class.
Here is the important stuff from the query:
var startDate = DateTime.Today;
var endDate = DateTime.Today.AddDays(-30);
var q = r.Find(x => x.IsValid &&
x.CreatedOn < startDate && x.CreatedOn >= endDate)
.GroupBy(x => x)
.Select(g => new
{
AvgApplicationTime = (int)g.Average(i => i.ApplicationTime),
AvgResponseTime = (int)g.Average(i => i.ResponseTime),
ApprovalRatio = g.Count(i => i.IsAccepted == true) / (double)g.Count()
}).First();
return new ApplicationStats(q.AvgApplicationTime, q.AvgResponseTime, q.ApprovalRatio);
So far, I have the two averages outputting correctly, but the ratio is returning 1 (or 100%).
Initially, I thought it may be a rounding issue but I've outputted the result of this line: g.Count(i => i.IsAccepted == true) and it incorrectly returns 1.
I may have grouping in the wrong place, but am currently struggling to make it work.
Any help appreciated.
Found the answer.
Changed .GroupBy(x => x) to .GroupBy(x => 1).
Also g.Count(i => i.IsAccepted == true) can be simplified to g.Count(i => i.IsAccepted)

Rails: Two 'where' queries - each works individually, but not together

I want to write something like:
#meeting_requests = Meeting.where('meeting_time >= ? AND requestee_id IS ?
AND status = ?', Date.today, nil, "Active")
.joins(:requestor)
.where('birthyear >= ? AND birthyear <= ?',
current_user.birthyear - 10,
current_user.birthyear + 10 )
This works:
#meeting_requests = Meeting.where('meeting_time >= ? AND requestee_id IS ?
AND status = ?', Date.today, nil, "Active")
And this works:
#meeting_requests = Meeting.joins(:requestor)
.where('birthyear >= ? AND birthyear <= ?',
current_user.birthyear - 10,
current_user.birthyear + 10 )
And something like this works:
Meeting.joins(:requestor).where('birthyear > ?', 1900).where(status: "Active")
but I need to do a greater than query on the meeting_time, so I need to write it as a string I think?
But together both sql queries produce an error of: ambiguous column name: status: SELECT
I feel like I'm so close... what am I missing here?
This is a message that appears when it is not clear which table the column comes from. This should work:
...rest_of_statement.where('meetings.status' => 'Active')

ActiveRecord execute raw transaction typecasting?

ActiveRecord::Base.connection.execute(sql)
The results are not typecasted so they are all strings as an example
ActiveRecord::Base.connection.execute(sql).entries
=> [{"id" => "1", "length" => "120", "content" => "something"},{"id" => "2", "length" => "200", "content" => "blahblah"}]
Is it possible to execute raw transactions in activerecord and return typecasted results?
Consider manifesting your SQL statement as a view, and creating a new record to interface with the view.
Here's a project where I'm backing AR with a view:
https://github.com/michaelkirk/household-account-mgmt/blob/develop/app/models/monthly_report.rb
class CreateMonthlyReports < ActiveRecord::Migration
def up
sql = <<-SQL
create view monthly_reports as
select date_part('year', created_at) as year, date_part('month', created_at) as month, sum(purchase_amount) as purchases_amount, sum(investment_amount) as investments_amount
from (
select * from transactions
left join
(select id as purchase_id, amount as purchase_amount from transactions where credit = false)
as purchases on transactions.id = purchases.purchase_id
left join
(select id as investment_id, amount as investment_amount from transactions where credit = true)
as investments on transactions.id = investments.investment_id)
as classified_transactions
group by year, month
order by year, month
SQL
execute(sql)
end
def down
sql = <<-SQL
drop view monthly_reports
SQL
execute(sql)
end
Then, since you've abstracted your complexity into a database view, which for all AR's intents/purposes works like a table your model and controller look completely vanilla.
class MonthlyReport < ActiveRecord::Base
MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
def time_period
"#{month} #{year}"
end
def month
MONTHS[self[:month] - 1]
end
def year
self[:year].to_i
end
end
Then you can do things like
class MonthlyReportsController < ApplicationController
def index
#monthly_reports = MonthlyReport.all
end
end
Note that because this is a DB view, you won't be able to do inserts. I'm not sure what would happen if you tried.
I think you are referring to ORM (Object relational mapping)
First of all, connection.execute will return a Mysql adapter where you can just iterate over the rows
You cant convert array of strings(the result you have) to ActiveRecord objects just like that ( I guess this is what you referred as typecasting)
What you can do is use find_by_sql.
Ex:
Blog.find_by_sql("select * from blog")
# => [#<Blog id: 1, name: "first blog", description: nil, record_status_id: 1>]
Using this method you can get ActiveRecord Objects fro raw SQL
Actually, you can get results cast either as ActiveRecord objects (see the find_by_sql method here) or as native Ruby types (see this StackOverflow answer).

Rails3/ActiveRecord: select sum with parameters?

Give the following (already simplified) query in SQLite:
def self.calculate(year, month, user_id, partner_id)
where(':user_id = entries.user_id OR :partner_id = entries.user_id', {
:user_id => user_id,
:partner_id => partner_id
}).
where('entries.date <= :last_day', {
:last_day => Date.new(year, month, 1).at_end_of_month
}).
select('sum(case when joint = "f" and user_id = :user_id then amount_calc else 0 end) as sum_single' , {
:user_id => user_id
}).
group("strftime('%Y-%m', date)")
end
The full query has more sums with different case when statements and some of them depend on whether it is user_id oder partner_id. Unfortunately, Rails complains as select does not take the second parameter with the substitutions like where does. Is there any way to achieve what I want without running two queries, one for user_id and one for partner_id?
One can be so blind....instead of:
select('sum(case when joint = "f" and user_id = :user_id then amount_calc else 0 end) as sum_single' , {
:user_id => user_id
}).
just build the string:
select('sum(case when joint = "f" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_single').
As nobody answered, this is for the archives :)
Edit: Sorry, beware of that: as noted below, this is vulnerable.

Converting SQL containing top, count, group and order to LINQ (2 Entities)

Some LINQ queries still puzzle me.
for a table 'Hits' containing two columns, 'Page' and 'Date', I want to find the most Pages with the most rows in a defined slice of time.
In SQL I would use this:
SELECT TOP 10
[Page]
,COUNT([Page]) as Number
FROM dbo.[Hits]
WHERE [Date] >= CONVERT(datetime,'14 Jan 2009')
AND [Date] < CONVERT(datetime,'15 Jan 2009')
Group BY [Page]
Order by Number DESC
In LINQ I got no idea how to approach this, can anyone help me here? I tried to convert it using linqer, but it just shows an error for this expression.
Something like this should work:
(from p in DataContext.Hits
where (p.Date >= minDate) && (p.Date < maxDate)
group p by p.Page into g
select new { Page = g.Key, Number = g.Count() }).OrderByDescending(x => x.Number).Take(10);
var top10hits = objectContext.Hits
.Where(h => minDate <= h.Date && h.Date < maxDate)
.GroupBy(h => h.Page)
.Select(g => new { Page = g.Key, Number = g.Count() })
.OrderByDescending(x => x.Number)
.Take(10);