I am working in the Rails console. I would like to select the SUM of a same named column in two different tables.
Here is my ActiveRecord code:
Computer.joins(:services, :repairs)
.select("computers.id, SUM(services.cost) as SCOST, SUM(repairs.cost) as RCOST")
.group("computers.id")
This works well and returns the following correct SQL:
`SELECT computers.id, SUM(services.cost) as SCOST, SUM(repairs.cost) as RCOST
FROM "computers" INNER JOIN "services" ON "services"."computer_id" = "computers"."id"
INNER JOIN "repairs" ON "repairs"."computer_id" = "computers"."id"
GROUP BY computers.id `
But it gives the following result in the Rails console:
=> [#<Computer id: 36>, #<Computer id: 32>]
Shouldn't I be able to access my SUM values as well? I ran the above SQL query in postgres and it gave the desired output.
Any help would be appreciated.
Thanks.
The Rails console uses the inspect method to display the object content. This method doesn't display the values for the custom fields. You will be able to print the value of a custom attribute at the console by explicitly referring to it.
Computer.joins(:services, :repairs)
.select("computers.id, SUM(services.cost) as scost, SUM(repairs.cost) as rcost")
.group("computers.id").each do |c|
puts c.scost
puts c.rcost
end
Edit
Example based on comment:
Create a member variable in your controller:
#computers = Computer.joins(:services, :repairs)
.select("computers.id, SUM(services.cost) as scost, SUM(repairs.cost) as rcost")
.group("computers.id")
Iterate over the variable in your views
- #computers.each do |computer|
= computer.scost
= computer.rcost
Edit2
You need to use LEFT OUTER JOIN to get values for computers with missing repairs or services.
join_sql = "LEFT OUTER JOIN services ON services.computer_id = computers.id
LEFT OUTER JOIN repairs ON repairs.computer_id = computers.id"
sum_sql = "SUM(COALESCE(services.cost, 0)) as scost,
SUM(COALESCE(repairs.cost, 0)) as rcost"
#computers = Computer.joins(join_sql)
.select("computers.id, #{sum_sql}")
.group("computers.id")
try as follow,
#computer_list = Computer.joins(:services, :repairs).select("computers.id, SUM(services.cost) as SCOST, SUM(repairs.cost) as RCOST").group("computers.id")
#computer_list.last.SCOST
Related
Can someone identify why this multi-table join is not accepted? When I bring in the third table, it then fails with invalid table alias. I am not seeing what is wrong:
This works (two table):
select
a.ri as `R_ID`
,oc3.name as `RET`
,a.rch as `RC`
from dev.sl a join dev.codes oc3
on (a.pk_business = oc3.pk_business
and a.pk_data_source = oc3.pk_data_source
and a.pk_frequency = oc3.pk_frequency
and oc3.pk_data_state = '123'
and oc3.code = a.ri and oc3.codeset = 'xyz')
Then add a third table and it fails:
(Three table):
select
a.ri as `R_ID`
,oc3.name as `RET`
,a.rch as `RC`
from dev.sl a join dev.codes oc3
on (a.pk_business = oc3.pk_business
and a.pk_data_source = oc3.pk_data_source
and a.pk_frequency = oc3.pk_frequency
and oc3.pk_data_state = '123'
and oc3.code = a.ri and oc3.codeset = 'xyz') join dev.items b
on (b.pk_business = a.pk_business
and b.pk_data_source = a.pk_data_source
and b.pk_frequency = a.pk_frequency
and b.pk_data_state = '123'
and a.ii = b.item_id
and a.cc = b.country_code)
SemanticException [Error 10009]: Line 1:2920 Invalid table alias 'a':
I have an update - it seems that this was caused by having one table created as an updatable table (TBLPROPERTIES ('transactional'='true')), and one without, and with my session settings of:
SET hive.txn.manager=org.apache.hadoop.hive.ql.lockmgr.DbTxnManager;
SET hive.support.concurrency=true;
SET hive.enforce.bucketing=true;
SET hive.exec.dynamic.partition.mode=nonstrict;
This caused the problem. On another session without the settings AND repointing to an identical table "a" created as a non-ACID type table, the multi-table join worked fine. I don't know enough about HIVE to know why - I suspect that a transactional and non-transactional table cannot be joined in the same "transaction" (select statement).
One more update - It may not be due to the transactional table. With additional testing, I now also see it happens with non-transactional tables as well. It seems that the three table join works when I execute it from a putty session directly on the server, but when I use SQL Developer, it will produce the aforementioned error. It appears to be an issue with SQL Developer, but why still is unknown.
I use laravel 5.2 I'm trying to get latest intervention of each foreing key pmt_id like bellow:
$res = $this->model->with('typo', 'nro', 'ctr', 'cad', 'pm')
->join('a2t_intervention',function ($join) use ($salar){
$join->on('a2t_intervention.pmt_id','=','a2t_pmt.id');
$join->whereRaw('a2t_intervention.pmt_id = (select max(`pmt_id`) from a2t_intervention)');
$join->where('a2t_intervention.etat_intervention','like','nok');
$join->whereIN('a2t_intervention.id_equipe_stt',$salar);
});
but I get this ERROR :
Call to undefined method Illuminate\Database\Query\JoinClause::whereRaw()
I try other ways but nothig work for me.
for each pmt_id in the table intervention we have at least one record ,I'am looking for get the last intervention foreach single pmt_id before make join with table PMT.
how to select id from table intervention in latest pmt_id like bellow in sql query:
SELECT t.*
FROM ( SELECT pmt_id
, MAX(id) AS id
FROM a2t_intervention
WHERE etat_intervention = 'nok'
AND `id_equipe_stt` IN ('" . implode(',', $id_equipe_stt) . "')
GROUP
BY pmt_id ) AS m
INNER JOIN a2t_intervention AS t
ON t.pmt_id = m.pmt_id
AND t.id = m.id
Error is pretty self-explanatory - there is no whereRaw method. You could try to replace:
$join->whereRaw('a2t_intervention.pmt_id = (select max(`pmt_id`) from a2t_intervention)');
with
$join->where('a2t_intervention.pmt_id', '=', \DB::raw("(select max(`pmt_id`) from a2t_intervention)"));
In Later Laravel versions JoinClause extends Builder so whereRaw method is available but for Laravel 5.2 it isn't.
I am using ruby interpolation which is working perfectly as per ActiveRecord but as per Brakeman its Sql Injection warning. My code:
user_room_ids = [10000,20000,30000]
tmp_query = "left outer join users u on u.emp_id = emps.id join user_rooms ur on (ur.id IN (#{user_room_ids})) join practices_user_rooms pwr on (pur.user_room_id = ur.id)"
all_emps = Employee.joins(tmp_query)
due to interpolation #{user_room_ids} I am getting Sql Injection warning, which I want to resolve.
Try this,
user_room_ids = [10000,20000,30000]
tmp_query = "left outer join users u on u.emp_id = emps.id join user_rooms ur on (ur.id IN (#{user_room_ids})) join practices_user_rooms pwr on (pur.user_room_id = ur.id)"
all_emps = Employee.joins(ActiveRecord::Base.send(:sanitize_sql_array, "#{tmp_query}"))
This should be working fine. For more details, please refer my Q&A here
I have this, but it loads each and every ebay row individually, generating thousands of SQL statements:
$products = \app\models\Product::find()
->joinWith('ebay', false, 'inner join')
->indexBy(function($row){return $row->ebay->epid;})
->all();
I tried this, but it gave an error: 'Getting unknown property: app\models\Product::ebay.epid'
$products = \app\models\Product::find()
->joinWith('ebay', false, 'inner join')
->indexBy('ebay.epid')
->all();
Setting eager loading = true doesn't help either. It still loads each row individually then loads them again at the end.
How can I efficiently join a table in Yii and index by a value in the joined table?
You won't be able to do it with indexBy. However, ArrayHelper::index can index an array on a related model field. So here's how it can be done:
$products = \app\models\Product::find()
->with('ebay')
->all();
ArrayHelper::index($products, 'ebay.epid');
The code will run two queries, one to get all products, one to get all related ebay products. Then the array will be indexed with no DB queries at all.
I ended up doing it manually for a subset of the ids and it only uses 2 queries. I'd still be interested in the indexBy though.
$products = Product::find()->joinWith('ebay', true, 'inner join')->where(['ebay.epid' => $productIds])->all();
$ebayProducts = array();
foreach ($products as $p) {
$ebayProducts[$p->ebay->epid] = $p;
}
If you want index by relation recods via joinWith() or with() results you can use following:
->with(['relationName' => function($q) {
$q->indexBy('field_name');
}])
Right now I am in the middle of migrating from SQLite to Postgresql and I came across this problem. The following prepared statement works with SQLite:
id = 5
st = ActiveRecord::Base.connection.raw_connection.prepare("DELETE FROM my_table WHERE id = ?")
st.execute(id)
st.close
Unfortunately it is not working with Postgresql - it throws an exception at line 2.
I was looking for solutions and came across this:
id = 5
require 'pg'
conn = PG::Connection.open(:dbname => 'my_db_development')
conn.prepare('statement1', 'DELETE FROM my_table WHERE id = $1')
conn.exec_prepared('statement1', [ id ])
This one fails at line 3. When I print the exception like this
rescue => ex
ex contains this
{"connection":{}}
Executing the SQL in a command line works. Any idea what I am doing wrong?
Thanks in advance!
If you want to use prepare like that then you'll need to make a couple changes:
The PostgreSQL driver wants to see numbered placeholders ($1, $2, ...) not question marks and you need to give your prepared statement a name:
ActiveRecord::Base.connection.raw_connection.prepare('some_name', "DELETE FROM my_table WHERE id = $1")
The calling sequence is prepare followed by exec_prepared:
connection = ActiveRecord::Base.connection.raw_connection
connection.prepare('some_name', "DELETE FROM my_table WHERE id = $1")
st = connection.exec_prepared('some_name', [ id ])
The above approach works for me with ActiveRecord and PostgreSQL, your PG::Connection.open version should work if you're connecting properly.
Another way is to do the quoting yourself:
conn = ActiveRecord::Base.connection
conn.execute(%Q{
delete from my_table
where id = #{conn.quote(id)}
})
That's the sort of thing that ActiveRecord is usually doing behind your back.
Directly interacting with the database tends to be a bit of a mess with Rails since the Rails people don't think you should ever do it.
If you really are just trying to delete a row without interference, you could use delete:
delete()
[...]
The row is simply removed with an SQL DELETE statement on the record’s primary key, and no callbacks are executed.
So you can just say this:
MyTable.delete(id)
and you'll send a simple delete from my_tables where id = ... into the database.