ActiveRecord: PostgreSQL-specific update with join - ruby-on-rails

The line like this (split into multiple lines for readability):
ValueMapping
.joins(:variation)
.where(
:mapper_id => 1,
:variations => {:name => 'de'}
)
.update_all(:value => 'Beispiel')
produces following SQL statement:
UPDATE value_mappings
SET value = 'Beispiel'
WHERE value_mappings.id IN (
SELECT value_mappings.id
FROM
value_mappings
INNER JOIN variations ON variations.id = value_mappings.variation_id
WHERE
value_mappings.mapper_id = 1
AND variations.name = 'de'
)
But I would like to get more PostgreSQL-specific request:
UPDATE value_mappings
SET value = 'Beispiel'
FROM
variations
WHERE
variations.id = value_mappings.variation_id
AND value_mappings.mapper_id = 1
AND variations.name = 'de'
)
Is it possible with ActiveRecord?

Related

Views hook_views_query_alter JOIN

In hook_views_query_alter i build join query like this:
$join = new views_join();
$join->table = 'table_2';
$join->field = 'field_2';
$join->left_table = 'table_1';
$join->left_field = 'field_1';
$join->type = 'LEFT';
$join->extra = array(
array(
'table' => 'table_2',
'field' => 'field_3',
'value' => 'table_1.field_4',
),
);
The query should looks like this:
LEFT JOIN {table_2} table_2 ON table_1.field_1 = table_2.field_2 AND table_1.field_3 = table_2.field_4
My problem is here 'value' => 'table_1.field_4'. I can't set value as field. It treat it as string value. So at the and in my query i have unwanted single quotes.
So at the end my query looks like this:
LEFT JOIN {table_2} table_2 ON table_1.field_1 = table_2.field_2 AND table_1.field_3 = 'table_2.field_4'
I'm not sure if it's the best solution but this:
$join = new views_join();
$join->table = 'table_2';
$join->field = 'field_2';
$join->left_table = 'table_1';
$join->left_field = 'field_1';
$join->type = 'LEFT';
$join->extra = 'table_1.field_3 = table_2.field_4';
works for me.

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"}];

Rails scope returns duplicates

I have a Rails scope query that is returning duplicates. Can anyone see what's the problem with it?
Here's the code:
scope :visible_to_user, lambda { |user|
{
:joins => 'LEFT JOIN SCHEMA.groups ON groups.id = uploaded_files.group_id
LEFT JOIN uploaded_file_references ON uploaded_files.id = uploaded_file_references.uploaded_file_id
LEFT JOIN message_threads ON message_threads.id = uploaded_file_references.thread_id
LEFT JOIN thread_participants ON thread_participants.message_thread_id = message_threads.id',
:conditions => [
%{
uploaded_files.in_private_conversation = false
AND ( ( NOT COALESCE(groups.private, false) )
OR uploaded_files.group_id IN (?)
OR ( thread_participants.referenced_id = '?'
AND thread_participants.referenced_type = 'User')
)
}, user.group_ids, user.id
]
}
}
In rails >= 3:
You can add .uniq call to any scope to not return duplicates, like so:
MyTable.a_scope.where(something).uniq
In rails < 3:
you have to add it by hand with a select configuration like so:
MyTable.find(:all, :select => "distinct my_table.*")

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!

sql distinct statement and use of more than one table on rails

How would you write this line in the "rails way"?
unique_attendees = CourseSessionsUser.count_by_sql(["SELECT count(DISTINCT csu.user_id) AS count_user_id FROM course_sessions_users csu, course_sessions cs WHERE cs.course_id = ? AND csu.course_session_id = cs.id"], #course.id)
The query itself is:
SELECT count(DISTINCT csu.user_id) AS count_user_id
FROM course_sessions_users csu,
course_sessions cs
WHERE cs.course_id = ?
AND csu.course_session_id = cs.id
Use count method of the rails
count = CourseSessionsUser.count('csu.user_id',
:distinct => true, :conditions => ["cs.course_id = ?", #course.id]
:joins => "cs LEFT JOIN course_sessions_users csu ON cs.id = csu.course_session_id")
This will return directly non zero integer if condition matches otherwise return zero

Resources