Update using Active Record - ruby-on-rails

How to update field using other fields in a single query ?
sql:
"UPDATE table_name SET field1=fields2 + 1 where id=123"
Tried these things, but didn't work
Model.update(123, :field => "field2 + 1")
Model.update(123, :field => :field2 + 1)
Model.update(123, "field = field2 + 1")
Don't want to do it in 2 query.

Model.update_all("field = field2 + 1", {:id => 123})
The second argument is conditions, on which the update should happen

Related

missing attributes name in rails association

I have the following query in my rails app but its result does not have attributes name so I am not able to use it in amcharts
EmployeeDepartment.joins(:states).group
("employee_departments.name").count
the result is : {"Academic Support":1}
how to make it like this {"department_name":"Academic Support","department_count":1}
Let's say you have a hash like this:
hash = { "Academic Support" => 1, "Another Department" => 3, "Something Else" => 4 }
You can just use map to transform it into an array of hashes containing what you need.
hash.map { |k, v| { "department_name" => k, "department_count" => v } }
=> [{"department_name"=>"Academic Support", "department_count"=>1},
{"department_name"=>"Another Department", "department_count"=>3},
{"department_name"=>"Something Else", "department_count"=>4}]
If your hash only ever contains one key/value pair and you just want another hash, you could try this:
Hash[[["department_name", "department_count"], hash.first].transpose]
Or even simpler...
{ "department_name" => hash.keys.first, "department_count" => hash.values.first }
I solved my problem by this steps:
the following code:
EmployeeDepartment.joins(:states).group
("employee_departments.name").count
generates this in rails console
SELECT COUNT(*) AS count_all, employee_departments.name AS employee_departments_name FROM "employee_departments" INNER JOIN "tickets" ON "tickets"."employee_department_id" = "employee_departments"."id" INNER JOIN "states" ON "states"."id" = "tickets"."state_id" GROUP BY employee_departments.name
i used what was generated in the console in the following:
#department_count = EmployeeDepartment.find(:all,
:select => 'employee_departments.name AS employee_departments_name, COUNT(*) AS department_counter',
:joins => 'INNER JOIN tickets ON tickets.employee_department_id = employee_departments.id INNER JOIN states ON states.id = tickets.state_id',
:group => 'employee_departments.name')
Now the result in amcharts is:
var chartData = [{"department_counter":1,"employee_departments_name":"Academic Support"}];

Ruby on Rails accessing MySQL results error

In my view I send an ajax request to get the device_ports of a particular device.
Previously I used
def get_device_ports
if params[:id] != ''
#device_ports = Device.find(params[:id]).device_ports.all(:order => 'id ASC')
output = '<option value="">Select Device Port...</option>'
#device_ports.each do |device_port|
output = output + '<option value="' + device_port.id.to_s + '">' + device_port.name + '</option>'
end
render :text => output
else
render :text => '0'
end
end
Which worked one but now having changed my query I get an error undefined method 'name' for [268, "test-1"]:Array with 268 and test-1 being the id and name of the first row of results.
This is my updated code:
def get_device_ports
if params[:id] != '' and params[:device_id] != ''
# #device_ports = Device.find(params[:id]).device_ports.all(:order => 'id ASC')
device_id = params[:device_id]
# Need a list of ports that aren't in use or are multiuse
#device_ports = ActiveRecord::Base.connection.execute('SELECT DISTINCT d.id, d.name FROM device_ports d LEFT OUTER JOIN circuits c ON c.physical_port_id = d.id WHERE (c.physical_port_id IS NULL AND d.device_id = ' + device_id + ') OR (d.multiuse = 1 AND d.device_id = ' + device_id + ') ORDER BY d.id ')
output = '<option value="">Select Device Port...</option>'
#device_ports.each do |device_port|
output = output + '<option value="' + device_port.id.to_s + '">' + device_port.name + '</option>'
end
render :text => output
else
render :text => '0'
end
end
I'm just not sure why I'm getting the error, I imagine it's something trivial but due to the amount of different NoMethodError questions it's hard to find an answer.
You are having this problem because you aren't using ActiveRecord as an ORM to wrap the object, but rather executing a query and working on the resulting series of arrays. I would recommend changing your query like so:
#device_ports = Device.find(device_id).device_ports.includes(:circuits).
where('device_ports.multiuse = 1 OR circuits.id IS NULL').
order('device_ports.id').distinct
If you absolutely want to avoid ActiveRecord, then don't use id and name, but rather treat each record as an array:
output << %Q{<option value="#{device_port.first}">#{device_port.last}</option>}
UPDATE
I just noticed that you're using RoR-2. Although more painful, you can still use an ActiveRecord query like so:
#device_ports = DevicePort.all(
:joins => "LEFT JOIN circuits c ON device_ports.id = c.devic_port_id",
:conditions => ['device_ports.device_id = ? AND (device_ports.multiuse = 1 OR c.id IS NULL)', device_id],
:order => 'device_ports.id',
:select => 'DISTINCT device_ports.id, device_ports.name')

How to use multiple on clause in joining in zend framework 2

I am doing like this sql into zend framework sql pattern.
SELECT
jobs . *,
c.id AS cid,
c.name AS name,
c.companyImage AS companyImage,
c.logo AS logo,
count(app.userId) AS t_app,
app.applyStatus AS applyStatus,
app.userId AS appUserId
FROM
jobs
LEFT JOIN
companies AS c ON jobs.companyName = c.id
LEFT JOIN
applicants AS app ON jobs.id = app.jobId AND app.applyStatus = 1
WHERE
jobs.ownerId = 16 AND jobs.draftId != 0
GROUP BY jobs.id
ORDER BY jobs.id DESC
LIMIT 3
For this sql I already write this code for zend framework 2
$adapter = $this->tableGateway->getAdapter();
$sql = new Sql($adapter);
$select = $sql->select();
$select->from('jobs')
->join(array('c' => 'companies'), 'jobs.companyName = c.id', array('cid' => 'id', 'name', 'companyImage', 'logo'), 'left')
->join(array('app' => 'applicants'), ' jobs.id = app.jobId AND app.applyStatus = 1', array('t_app' => new Expression('count(app.userId)'), 'applyStatus', 'appUserId' => 'userId'), 'left')
->where("jobs.ownerId ={$userId} AND jobs.draftId != 0")
->group('jobs.id')
->order('jobs.id DESC')
->limit(3);
$statement = $sql->getSqlStringForSqlObject($select);
$results = $adapter->query($statement, $adapter::QUERY_MODE_EXECUTE);
but does not work properly and its give a message like below.
SQLSTATE[42S22]: Column not found: 1054 Unknown column '1' in 'on clause'
The issue is this part:
app.applyStatus = 1
The framework is escaping 1 as if it were a column name, 1.
You need to enclose this part in an Expression too
new Expression('jobs.id = app.jobId AND app.applyStatus = 1')
I think the use of Expressions in the 'ON' parameter of the join method may depend on the version of ZF2 you are using, I think it was added 2.1+
Building on this answer. If you also want your table & column identifiers to be escaped, use this syntax:
use Zend\Db\Sql\Expression;
...
$onExpression = new Expression('? = ? AND ? = ?',
['jobs.id', 'app.jobId', 'app.applyStatus', 1],
[Expression::TYPE_IDENTIFIER, Expression::TYPE_IDENTIFIER,
Expression::TYPE_IDENTIFIER, Expression::TYPE_LITERAL]
);
$select->from('jobs')
->join(array('app' => 'applicants'), $onExpression, array('t_app' => new Expression('count(app.userId)'), 'applyStatus', 'appUserId' => 'userId'), 'left');
The Expression constructor accepts the string, then arguments, then argument types.
public function __construct($expression = '', $parameters = null, array $types = [])
This will create a security issue. Zf2 changes your query to this:
Select * from tableA inner join tableB
on `tableA`.`column` = `tableB`.`column`
AND `tableB`.`column` = `1`
It adds
`
to each part for security issues! By using new Expression you are bypassing it and if you get applyStatus from user entry, get sure about its filtering!

How do you set a condition based on the month of the 'created_at' field

controller code that doesn't work:
#mailings = Mailing.find(:all, :conditions => ["created_at.month = ?", m])
Error msg:
Mysql::Error: Unknown column 'created_at.month' in 'where clause': SELECT * FROM `mailings` WHERE (created_at.month = 10)
thanks!
The :conditions argument of find is interpolated directly into the resulting SQL. Thus, you can use SQL commands.
You could try the following if you're using MySQL:
#mailings = Mailing.find(:all, :conditions => ["MONTH(created_at) = ?", m])
Or this, for SQLite:
#mailings = Mailing.find(:all, :conditions => ["strftime('%m', created_at) = ?", m])
Try:
#mailings = Mailing.find(:all, :conditions => ["MONTH(created_at) = ?", m])
Assuming you're using MySQL you can find more in the Docs: MySQL Date and Time Functions

Ruby on Rails: Sum table column row values based on other column data

I have a table with columns 'id', 'resource_id', 'read_time', 'value' where 'value' is a float
What I am trying to accomplish is to return a list of records such that the 'value' of each record is the sum of all the records at a specific 'read_time' but having differing 'resource_id' values.
I am wondering if there is a clever way (ie not looping through all the entries) to accomplish this. Currently I am implementing something along these lines:
#aggregate_meters = []
#res_one_meters = Meter.find(:all, :conditions => ["resource_id = ?", 1])
#res_one_meters.each do |meter|
read_time = meter.read_time
value = meter.value
if res_two_meter = Meter.find(:first, :conditions => ["resource_id = ? AND read_time = ?", 2, read_time ])
value = value + res_two_meter.value
end
aggregate_meter = Meter.new(:read_time => read_time, :value => value, :resource_id => 3)
#aggregate_meters.push(aggregate_meter)
end
Thank you.
ActiveRecord::Calculate is your friend here. Letting you do exactly what you want with one database call. It returns a hash using the unique values in the column used in the group as keys.
Here's the code you wrote, rewritten to use sum.
values = Meter.sum(:value, :group => :read_time)
values.each do |read_time, value|
aggregate_meter = Meter.new(:read_time => read_time, :value => value, :resource_id => 3)
#aggregates_meter.push(aggregate_meter)
end

Resources