Rails botches the SQL on a complex save - sql

I am doing something seemingly pretty easy, but Rails is messing up the SQL. I could just execute my own SQL, but the framework should be able to handle this.
Here is the save I am trying to perform:
w = WhipSenVote.find(:first, :conditions => ["whip_bill_id = ? AND whip_sen_id = ?", bill_id, k])
w.votes_no = w.votes_no - 1
w.save
My generated SQL looks like this:
SELECT *
FROM "whip_sen_votes"
WHERE (whip_bill_id = E'1' AND whip_sen_id = 7)
LIMIT 1
And then:
UPDATE "whip_sen_votes"
SET "votes_yes" = 14, "updated_at" = '2009-11-13 19:55:54.807000'
WHERE "id" = 15
The first select statement is correct, but as you can see, the Update SQL statement is pretty wrong, though the votes_yes value is correct.
Any ideas? Thanks!

It would help to see the WhipSenVote model.
you can also use decrement!

Related

SQL select distinct value

I'm trying to select the following data with the limited information. The problem is that when I have added the .select distinct section it has killed my query.
#activities = Availability.select.("DISTINCT user_id").where("team_id = ? and schedule_id = ?", current_user[:team_id], #next_game).last(5)
There's one too many dot's in there as the 'DISTINCT user_id' is the arguments for the select method call.
So:
Availability.select("DISTINCT user_id").where("team_id = ? and schedule_id = ?", current_user[:team_id], #next_game).last(5)
Also be aware that you're now only selecting one attribute and you'll get a partial representation of the classes back. To circumvent this just select the attributes you need later in the code.
Availability.select("DISTINCT(`user_id`), `team_id`").where("team_id = ? and schedule_id = ?", current_user[:team_id], #next_game).last(5)
etc.
Hope this helps.

CodeIgniter - Grouping where clause

I have this following query for CodeIgniter:
$q = $this->db->where('(message_from="'.$user_id.'" AND message_to="'.$this->auth_model->userdata['user_id'].'")')
->or_where('(message_from="'.$this->auth_model->userdata['user_id'].'" AND message_to="'.$user_id.'")')
->get('messages');
I want to write this query with completely active record.
I have tried something like this:
$from_where = array('message_from'=>$user_id, 'message_to'=>$this->auth_model->userdata['user_id']);
$to_where = array('message_from'=>$this->auth_model->userdata['user_id'],'message_to'=>$user_id);
$q = $this->db->where($from_where)
->or_where($to_where)
->get('messages');
die($this->db->last_query());
The above code produces this query:
SELECT * FROM (`messages`) WHERE `message_from` = '2' AND `message_to` = '1' OR `message_from` = '1' OR `message_to` = '2'
But this is what I want to produce:
SELECT * FROM (`messages`) WHERE (message_from="2" AND message_to="1") OR (message_from="1" AND message_to="2")
There are similar questions here and here, but thosedid not provide a real solution for me.
How's this possible, If not via core libraries, is there an extension which allows writing such queries?
Thanks,
You can use sub query way of codeigniter to do this for this purpose you will have to hack codeigniter. like this
Go to system/database/DB_active_rec.php Remove public or protected keyword from these functions
public function _compile_select($select_override = FALSE)
public function _reset_select()
Now subquery writing in available And now here is your query with active record
$this->db->where('message_from','2');
$this->db->where('message_to','1');
$subQuery1 = $this->db->_compile_select();
$this->db->_reset_select();
$this->db->where('message_from','1');
$this->db->where('message_to','2');
$subQuery2 = $this->db->_compile_select();
$this->db->_reset_select();
$this->db->select('*');
$this->db->where("$subQuery1");
$this->db->or_where("$subQuery2");
$this->db->get('messages');
Look at this answer of mine. This shows how to use sub queries. This will help
Using Mysql WHERE IN clause in codeigniter
EDITES
Yes i have done it
Rewrite the query this way exactly you want
$this->db->where('message_from','2');
$this->db->where('message_to','1');
$subQuery1 = $this->db->_compile_select(TRUE);
$this->db->_reset_select();
$this->db->where('message_from','1');
$this->db->where('message_to','2');
$subQuery2 = $this->db->_compile_select(TRUE);
$this->db->_reset_select();
$this->db->select('*');
$this->db->where("($subQuery1)");
$this->db->or_where("($subQuery2)");
$this->db->get('messages');
Compile select is with true parameter. Will not produce select clause. This will produce
SELECT * FROM (`messages`) WHERE (`message_from` = '2' AND `message_to` = '1') OR (`message_from` = '1' AND `message_to` = '2')

rails/activerecord: how to get SQL COUNT and SUM to work with Postgres (at heroku)

My rails 3 app needs to use a SELECT DISTINCT which (as far as I know) cannot be done with activerecord queries. So I have been executing direct SQL and it is running fine locally on sqllite -- But it is failing at Heroku (postgres).
In my local (sqllite) app, this works fine:
r = ActiveRecord::Base.connection.execute("my query string")
But on heroku, using ActiveRecord::Base.connection.execute ALWAYS returns an empty dataset
#<PGresult:0x0000000xxxxxxxxx>
even for very simple queries such as
r = ActiveRecord::Base.connection.execute("SELECT numeric_score FROM beeps WHERE store_id = '132' AND survey_num = '2'")
So I'm using heroku console to debug some very basic SQL queries to try to understand how to re-format my SQL to work at Heroku/Postgres.
SELECT column_name WORKS: the heroku console, selecting postgres records is no problem, for example this works fine:
n = Beep.find_by_sql("SELECT numeric_score FROM beeps WHERE store_id = '132' AND survey_num = '2'")
gives the three values expected:
[#<Beep numeric_score: 10>, #<Beep numeric_score: 9>, #<Beep numeric_score: 8>]
But SELECT COUNT fails?? When I try to COUNT them in the SQL
n = Beep.find_by_sql("SELECT COUNT(*) FROM beeps WHERE store_id = '132' AND survey_num = '2'")
it fails, giving:
[#<Beep >]
And SELECT SUM(column) fails too?? When I try to SUM them
n = Beep.find_by_sql("SELECT SUM(numeric_score) FROM beeps WHERE store_id = '132' AND survey_num = '2'")
it also fails, giving:
[#<Beep >]
How do I execute direct SQL with Postgres... SUM(columnname) and COUNT(*) should work, right?
There's a couple of things here.
Firstly, find_by_sql will return initialised objects based on the data coming back, which is why you're not seeing anything coming back from your counts.
In order to do this with AR you can do:
Beep.where(:store_id => 123).where(:survey_num => 2).count
=> 5
This will return a number. It's the same with sums:
Beep.where(:store_id => 123).where(:survey_num => 2).sum(:numeric_score)
=> 5
You can also use distinction with AR, but it's not as clean:
Beep.select("DISTINCT *").where(:store_id => 123).where(:survey_num => 2)
=> [<Beep>, <Beep>...etc]
In order to query the db directly, this is still possible, and you were almost there:
conn = ActiveRecord::Base.connection
sql = "SELECT DISTINCT * FROM beeps WHERE store_ID = 123"
res = conn.execute sql
# res is now a PGResult object
res.each do |row|
puts row["id"]
puts row["numeric_score"]
end
There's a uniq method that you can add on to your ActiveRecord relation that adds DISTINCT to the SELECT.
Beep.where(:store_id => 123).where(:survey_num => 2).uniq
See also: http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-uniq

Add up value using database field value with savefield in cakephp

my question is pretty simple but hard to find an answer for though search engines.
I simply want to update a field in the database, using that fields old value to add another value. I'm using the following at the moment:
$this->Advertisement->saveField('total_views', '(total_views + 1)', false);
But this gives me the next query:
UPDATE `advertisement` SET `total_views` = '(total_views +1)', `modified` = '2011-08-26 10:44:58' WHERE `advertisement`.`id` = 16
This is wrong and it should be:
UPDATE `advertisement` SET `total_views` = (total_views +1), `modified` = '2011-08-26 10:44:58' WHERE `advertisement`.`id` = 16
The problem is where it puts (total_views +1) between quotes.
Does anyone have an idea on how to get this working?
$this->Advertisement->updateAll(
array('Advertisement.total_views' => 'Advertisement.total_views + 1'),
array('Advertisement.id' => 1)
);
Just in case anyone else gets stuck with this, the spaces in the query are important.
works: Advertisement.total_views + 1
does not work: Advertisement.total_views+1
$this->Advertisement->updateAll(array('Advertisement.total_views'=>'Advertisement.total_views+1'), array('Advertisement.id'=>$id));
I found that for a similar issue I was able to resolve it in the following manner,
$this->Advertisement->updateAll(
array('Advertisement.total_views = Advertisement.total_views + 1'),
array('Advertisement.id' => 1)
);

Having problems converting conditional where clause in LINQ back over to SQL

I've got myself in a bit of a pickle!
I've done a snazzy LINQ statement that does the job in my web app, but now I'd like to use this in a stored procedure:
var r = (from p in getautocompleteweightsproducts.tblWeights
where p.MemberId == memberid &&
p.LocationId == locationid
select p);
if (level != "0")
r = r.Where(p => p.MaterialLevel == level);
if (column == "UnitUserField1")
r = r.Where(p => p.UnitUserField1 == acitem);
if (column == "UnitUserField2")
r = r.Where(p => p.UnitUserField2 == acitem);
return r.OrderBy(p => p.LevelNo).ToList();
However, I can't for the life of me get the conditional where clause to work!!
If someone can point me in the right direction, I'd be most grateful.
Kind regards
Maybe something like this?
SELECT *
FROM dbo.weights
WHERE member_id = #memberid
AND location_id = #locationid
AND material_level = CASE WHEN #level = '0' THEN material_level
ELSE #level END
AND #acitem = CASE #column WHEN 'UnitUserField1' THEN unit_user_field_1
WHEN 'UnitUserField2' THEN unit_user_field_2
ELSE #acitem END
ORDER BY level_no
Have you tried LinqPAD, I'm pretty sure last time I played with that you could enter "LINQ to SQL" code and see the resulting SQL that produced. Failing that, place a SQL trace/profiler on your code running the LinqTOSQL and find the query being executed in the trace.
LukeH's answer will give you the correct rows, but there is something lost when you try to replace a query-generating-machine with a single query. There are parts of that query that are opaque to the optimizer.
If you need the original queries as-would-have-been-generated-by-linq, there are two options.
Generate every possible query and control which one runs by IF ELSE.
Use Dynamic sql to construct each query (although this trades away many of the benefits of using a stored procedure).
If you do decide to use dynamic sql, you should be aware of the curse and blessings of it.