EDIT THE PACKAGE NO LONGER WORKS DUE TO AN UPGRADE FROM LARAVEL. THIS COMES STRAIGHT FROM THE DEVELOPER OF THE PACKAGE "ELOQUENCE"
I have done nothing to my code. All of a sudden I try to use the search field and get this error
QueryException in Connection.php line 636:
SQLSTATE[HY000]: General error: 2031 (SQL: select count(*)
as aggregate from
(select `posts`.*, max(case when `posts`.`title` = ? then 15 else 0
end + case when `posts`.`title` like ? then 5 else 0 end + case
when `posts`.`title` like ? then 1 else 0 end + case when
`posts`.`subtitle` = ? then 15 else 0 end + case when
`posts`.`subtitle` like ? then 5 else 0 end + case when
`posts`.`subtitle` like ? then 1 else 0 end + case when
`posts`.`content_raw` = ? then 15 else 0 end + case when
`posts`.`content_raw` like ? then 5 else 0 end + case when
`posts`.`content_raw` like ? then 1 else 0 end + case when
`tags`.`title` = ? then 15 else 0 end + case when `tags`.`title` like
? then 5 else 0 end + case when `tags`.`title` like ? then 1 else 0
end) as relevance from `posts` left join `taggables` on
`taggables`.`taggable_id` = `posts`.`id` left join `tags` on
`taggables`.`tag_id` = `tags`.`id` where (`posts`.`title` like ? or
`posts`.`subtitle` like ? or `posts`.`content_raw` like ? or
`tags`.`title` like ?) group by `posts`.`id`) as `posts` where
`relevance` >= 1)
I don't know whats going on. While writing this I updated the jarektkaczyk/eloquence package that i use for searches and nothing atill has happened. Was working fine and now this
Related
I have a SQL Server database and I am trying to pull specific data. I need a count of all the non-null columns in each row, a subtraction of one column from another, and data from other table columns (joins).
This is where I am, could someone please look at the code and tell me what I am doing wrong (ignore the hard-coded dates, they are there solely for testing)?
SELECT
((CASE WHEN TC.Time0 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time1 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time2 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time3 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time4 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time5 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time6 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time7 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time8 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time9 IS NOT NULL THEN 1 ELSE 0 END)) AS [Time Punches]
,SUM(CASE WHEN TC.Odometer0 IS NOT NULL THEN 1 ELSE 0 END) AS MileageStart
,SUM(CASE WHEN TC.Odometer1 IS NOT NULL THEN 1 ELSE 0 END) AS MileageEnd
,SUM(CASE WHEN MileageEnd >= 0 THEN 1 ELSE 0 END) -
SUM(CASE WHEN MileageStart < 0 THEN 1 ELSE 0 END) AS [Total Miles]
,D.DriverID AS [Driver ID]
,W.FirstName +' '+W.LastName AS [Driver Name]
,TC.PunchDate AS [DATE]
FROM tblTimeClock TC WITH (NOLOCK)
INNER JOIN tblDrivers D WITH (NOLOCK)
ON D.DriverID = TC.PunchID
INNER JOIN tblWorker W WITH (NOLOCK)
ON W.WorkerID = D.DriverID
WHERE TC.PunchID IS NOT NULL
AND TC.PunchDate BETWEEN '2017-05-01' AND '2017-06-01'
ORDER BY TC.PunchDate
With the above I am getting this error:
> Column 'tblTimeClock.Time0' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
But I do not know how to include this in a GROUP BY clause - every time I try it causes other errors to pop up (different, depending on where I place the clause).
The reason I asked if someone can "tell me what I am doing wrong" is so that once I fix what is broken, I don't have to come back and say "help" again and again for each subsequent issue. I know the code is bad, this is why I need help.
I figured it out on my own:
SELECT
TC.PunchDate AS [Date]
,D.DriverID AS [Driver ID]
,W.FirstName +' '+W.LastName AS [Driver Name]
,((CASE WHEN TC.Time0 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time1 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time2 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time3 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time4 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time5 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time6 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time7 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time8 IS NOT NULL THEN 1 ELSE 0 END)
+ (CASE WHEN TC.Time9 IS NOT NULL THEN 1 ELSE 0 END)) AS [Time Punches]
,TC.Odometer0 AS [Starting Mileage]
,TC.Odometer1 AS [Ending Mileage]
,SUM(CASE WHEN TC.Odometer1 IS NOT NULL AND TC.Odometer1 >= 0 THEN TC.Odometer1 ELSE 0 END) -
SUM(CASE WHEN TC.Odometer0 IS NOT NULL AND TC.Odometer0 >= 0 THEN TC.Odometer0 ELSE 0 END) AS [Total Miles]
FROM tblTimeClock TC WITH (NOLOCK)
INNER JOIN tblDrivers D WITH (NOLOCK)
ON CAST(D.DriverID AS VARCHAR(50)) = TC.PunchID
INNER JOIN tblWorker W WITH (NOLOCK)
ON W.WorkerID = D.DriverID
WHERE TC.PunchID IS NOT NULL
AND TC.PunchDate BETWEEN #StartDate AND #EndDate
GROUP BY TC.Time0, TC.Time1, TC.Time2, TC.Time3, TC.Time4, TC.Time5, TC.Time6, TC.Time7, TC.Time8, TC.Time9, TC.Odometer0,TC.Odometer1, D.DriverID, W.FirstName, W.LastName, TC.PunchDate
ORDER BY TC.PunchDate
Given the following select:
SELECT
COUNT(*) AS total,
SUM(CASE approved WHEN 't' THEN 1 ELSE 0 END) AS num_approved,
SUM(CASE soft_delete WHEN 't' THEN 1 ELSE 0 END) AS num_deleted
FROM model_name;
How might I translate this into something in my class ModelName definition, so that I can retrieve the values of total, num_approved, and num_deleted in my Rails application? I'm happy with any output (array, hash, accessors on a ModelName object) that puts the numbers at the Ruby level.
result = ModelName.select("
COUNT(*) AS total,
SUM(CASE approved WHEN 't' THEN 1 ELSE 0 END) AS num_approved,
SUM(CASE soft_delete WHEN 't' THEN 1 ELSE 0 END) AS num_deleted
").first
irb(main):278:0> result.total
=> 3041
irb(main):279:0> result.num_approved
=> 199464763
irb(main):280:0> result.num_deleted
=> #<BigDecimal:7fa0b7d79fd8,'0.19365329E8',9(18)>
In my model I have a couple of queries that can be used (and re-used) one after another. One of those should aggregate amounts. This works fine on SQLite and throws an error on Postgres:
ActiveRecord::StatementInvalid (PGError: ERROR: column "entries.date" must appear in the GROUP BY clause or be used in an aggregate function
: SELECT sum(case when joint = false then amount else amount / 2 end) as total, sum(case when joint = false then amount else 0 end) as sum_personal, sum(case when joint = true and user_id = 1 then amount / 2 else 0 end) as sum_user_joint, sum(case when joint = true and user_id = 2 then amount / 2 else 0 end) as sum_partner_joint FROM "entries" WHERE (1 = entries.user_id OR (2 = entries.user_id AND entries.joint = 't')) AND ('2011-04-01' <= entries.date AND entries.date <= '2011-04-30') AND (amount_calc > 0 AND compensation = 'f') ORDER BY date asc)
Relevant part of Model.rb
# all entries of one month
def self.all_entries_month(year, month, user_id, partner_id)
mydate = Date.new(year, month, 1)
where(':user_id = entries.user_id OR (:partner_id = entries.user_id AND entries.joint = :true)', {
:user_id => user_id,
:partner_id => partner_id,
:true => true
}).
where(':first_day <= entries.date AND entries.date <= :last_day', {
:first_day => mydate,
:last_day => mydate.at_end_of_month
})
end
def self.income
where('amount_calc > 0 AND compensation = ?', false)
end
def self.cost
where('amount_calc <= 0 AND compensation = ?', false)
end
def self.order_by_date
order('date asc')
end
# group by tag and build sum of groups named group_sum
def self.group_by_tag(order)
group('tag').
select('tag, ' +
'sum(case when joint = "f" then amount else amount / 2 end) as tag_sum'
).
order('tag_sum ' + order)
end
def self.multiple_sums(user_id, partner_id)
case ActiveRecord::Base.connection.adapter_name
when 'SQLite'
select('sum(case when joint = "f" then amount else amount / 2 end) as total, ' +
'sum(case when joint = "f" then amount else 0 end) as sum_personal, ' +
'sum(case when joint = "t" and user_id = ' + user_id.to_s + ' then amount / 2 else 0 end) as sum_user_joint, ' +
'sum(case when joint = "t" and user_id = ' + partner_id.to_s + ' then amount / 2 else 0 end) as sum_partner_joint '
)
when 'PostgreSQL'
select('sum(case when joint = false then amount else amount / 2 end) as total, ' +
'sum(case when joint = false then amount else 0 end) as sum_personal, ' +
'sum(case when joint = true and user_id = ' + user_id.to_s + ' then amount / 2 else 0 end) as sum_user_joint, ' +
'sum(case when joint = true and user_id = ' + partner_id.to_s + ' then amount / 2 else 0 end) as sum_partner_joint '
)
else
raise 'Query not implemented for this DB adapter'
end
end
Controller
# get all entries of given month
#cost = Entry.all_entries_month(#year, #month, current_user.id, current_partner.id).cost
# group cost by categories
#group_cost = #cost.group_by_tag('asc')
# still need to sort by date
#cost = #cost.order_by_date
#calc_cost = #cost.multiple_sums(current_user.id, current_partner.id)[0]
How can I change my query multiple_sums without breaking the other queries? Or do I need to implement multiple_sums from ground without using the existing ones?
Remove order by clause, which is useless as far as I can see anyway because you're grouping into single row.
And please - reformat your queries so that they will be visible and readable.
Heroku throws an error on my Postgres-Query stating:
ActiveRecord::StatementInvalid
(PGError: ERROR: syntax error at or
near "date" 2011-05-03T13:58:22+00:00
app[web.1]: LINE 1: ...2011-05-31')
GROUP BY EXTRACT(YEAR FROM TIMESTAMP
date)||EXT...
The SQLite query in development works as expected. Here is the code:
def self.calculate(year, month, user_id, partner_id)
case ActiveRecord::Base.connection.adapter_name
when 'SQLite'
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('entries.date, ' +
'sum(case when joint = "f" then amount_calc else 0 end) as sum_private, ' +
'sum(case when joint = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_joint, ' +
'sum(case when joint = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_joint, ' +
'sum(case when compensation = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_compensation, ' +
'sum(case when compensation = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_compensation '
).
group("strftime('%Y-%m', date)")
when 'PostgreSQL'
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('entries.date, ' +
'sum(case when joint = "f" then amount_calc else 0 end) as sum_private, ' +
'sum(case when joint = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_joint, ' +
'sum(case when joint = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_joint, ' +
'sum(case when compensation = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_compensation, ' +
'sum(case when compensation = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_compensation '
).
group("EXTRACT(YEAR FROM TIMESTAMP date)||EXTRACT(MONTH FROM TIMESTAMP date)")
else
raise 'Query not implemented for this DB adapter'
end
end
I would really appreciate any hints. And as I am already asking a question here, I am uncertain about the case when joint = "t" in the sums in both queries too, is there a better way to do this?
UPDATE
Thanks to both peufeu and a horse with no name the code now looks like:
when 'PostgreSQL'
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('min(entries.date) as date, ' +
'sum(case when joint = false then amount_calc else 0 end) as sum_private, ' +
'sum(case when joint = true and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_joint, ' +
'sum(case when joint = true and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_joint, ' +
'sum(case when compensation = true and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_compensation, ' +
'sum(case when compensation = true and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_compensation '
).
group('EXTRACT(YEAR FROM "date"), EXTRACT(MONTH FROM "date")')
...and works like expected.
Another of my statement runs into troubles now and I edit it here as it seems related to the answer of peufeu. Model/Controller:
def self.all_entries_month(year, month, user_id, partner_id)
mydate = Date.new(year, month, 1)
where(':user_id = entries.user_id OR (:partner_id = entries.user_id AND entries.joint = :true)', {
:user_id => user_id,
:partner_id => partner_id,
:true => true
}).
where(':first_day <= entries.date AND entries.date <= :last_day', {
:first_day => mydate,
:last_day => mydate.at_end_of_month
})
end
# group by tag and build sum of groups named group_sum
def self.group_by_tag
group('tag').
select('entries.*, sum(amount_calc) as group_sum')
end
controller:
#income = Entry.all_entries_month(#year, #month, current_user.id, current_partner.id).income
#cost = Entry.all_entries_month(#year, #month, current_user.id, current_partner.id).cost
# group cost by categories
#group_income = #income.group_by_tag.order('group_sum desc')
#group_cost = #cost.group_by_tag.order('group_sum')
The error is:
ActionView::Template::Error (PGError: ERROR: column "entries.id" must appear in the GROUP BY clause or be used in an aggregate function
2011-05-03T18:35:20+00:00 app[web.1]: : SELECT entries.*, sum(amount_calc) as group_sum FROM "entries" WHERE (1 = entries.user_id OR (2 = entries.user_id AND entries.joint = 't')) AND ('2011-04-01' <= entries.date AND entries.date <= '2011-04-30') AND (amount_calc <= 0 AND compensation = 'f') GROUP BY tag ORDER BY group_sum):
2011-05-03T18:35:20+00:00 app[web.1]: 6: </thead>
2011-05-03T18:35:20+00:00 app[web.1]: 7: <tbody>
2011-05-03T18:35:20+00:00 app[web.1]: 8: <% if categories %>
2011-05-03T18:35:20+00:00 app[web.1]: 9: <% categories.each do |category| %>
2011-05-03T18:35:20+00:00 app[web.1]: 10: <tr>
2011-05-03T18:35:20+00:00 app[web.1]: 11: <td class="align-left"><%= category.tag %></td>
2011-05-03T18:35:20+00:00 app[web.1]: 12: <td class="align-right"><%= my_number_to_percentage (category.group_sum.to_f / total_cost) * 100 %></td>
UPDATE 2: I found the solution
# group by tag and build sum of groups named group_sum
def self.group_by_tag
group('tag').
select('tag, sum(amount_calc) as group_sum')
end
Problem is there :
EXTRACT(YEAR FROM TIMESTAMP date)||EXTRACT(MONTH FROM TIMESTAMP date)
Solution :
EXTRACT(YEAR FROM "date"), EXTRACT(MONTH FROM "date")
The word "TIMESTAMP" is a bit out of place there ;) ... also :
Since DATE is a reserved SQL keyword, it is a very bad idea to use it as a column name. Here, I quoted it using " so postgres doesn''t get confused.
And you don't need to waste CPU time building a string concatenating your two EXTRACTs, just remember GROUP BY can use several parameters.
Then of course the query will fail because you SELECT "date" but your dont' GROUP BY date. postgres has no way to know which date you want from all the rows which have the same Year-Month. MySQL will return a value at random from the rows, postgres likes correctness so it will throw an error. You could SELECT min(date) for instance, which would be correct.
I am uncertain about the case when joint = "t"
That depends on how you want to get your results, nothing wrong there.
Maybe using several queries would scan a smaller portion of the table (I don't know about your dataset) or maybe not.
I don't know ruby/heroku, but the expression
joint = "t"
refers to a column t in PostgreSQL because object names are quoted with double quotes. So unless Ruby/Heroku is replacing those double quotes with single quotes that will be an invalid condition.
If t should be string literal (the character t) then you need to use single quotes: joint = 't'
If joint is of type boolean then you should use joint = true in PostgreSQL
Suppose I have a collection of Pages that are ordered by a column called :sibling_order. Is there a more efficient way to update the ordering of the collection than what I'm doing below:
class Page < ActiveRecord::Base
...
def update_order(order)
if (order < self.sibling_order)
siblings = Page.where("parent_id = ? AND sibling_order < ? AND sibling_order >= ?",new_parent_id,self.sibling_order,order)
siblings.collect{|s|s.update_attribute(:sibling_order,s.sibling_order + 1)}
elsif (order > self.sibling_order)
siblings = Page.where("parent_id = ? AND sibling_order > ? AND sibling_order <= ?",new_parent_id,self.sibling_order,order)
siblings.collect{|s|s.update_attribute(:sibling_order,s.sibling_order - 1)}
end
self.update_attribute(:sibling_order, order) if self.sibling_order != order
end
...
end
Try this:
def update_order(order)
k = (order < self.sibling_order) ? 1 : -1
sql = "parent_id = ? AND " + ( k == 1 ?
"sibling_order < ? AND sibling_order >= ?" :
"sibling_order > ? AND sibling_order <= ?")
sql = Page.send(:sanitize_sql_array, [sql, new_parent_id, sibling_order,order])
Page.update_all("sibling_order = sibling_order + (#{k})", sql)
self.update_attribute(:sibling_order, order) if self.sibling_order != order
end