Sequel SELECT count(id) FROM tablename WHERE username = "alpha" - ruby

I can't seem to get this query right. Sequel implementation of:
SELECT count(id) FROM users WHERE username = "alpha" AND status = "new"
Here's what I have so far:
db = Sequel.connect('postgres://me:pw#0.0.0.0:5432/dbname)
u = db[:users]
puts "Total users: #{u.count}" # this is correct
puts db.where(:username => "alpha", :status => "new").count
I've tried various direct SQL and that doesn't seem to work either. It smells like this is remedial, but the connectivity is fine, and I can replicate the exact SQL which doesn't come back the same.

You forgot to select the table. You want this:
db[:users].where(username:'alpha', status:'new').count
For SQLite3, this produces the query:
SELECT count(*) AS 'count'
FROM `users`
WHERE ((`username` = 'alpha')
AND (`status` = 'new'))
LIMIT 1
The error message you should have been getting was:
NoMethodError: undefined method `where' for #<Sequel::SQLite::Database: ...>
If you saw this error and read it, it should have let you know that calling db.where was not right.
In addition to making this work with the native Sequel DSL, you can also always run raw sql commands in Sequel.

Related

Valid unprepared query not functioning in migrations

(Laravel 5.6)
I'm having issues executing DB::unprepared query in a laravel migration. The query is valid, as I can copy-paste it directly in my db management app. But within an unprepared query, nada. I'm not getting any errors whatsoever.
I've tried using DB::raw and a combination of the two, as seen in other posted questions, to no avail. Anybody spot any obvious errors that I'm missing? TIA.
DB::unprepared(
'Update protocols AS P
INNER JOIN (
SELECT auditable_id, updated_at
FROM logs
WHERE auditable_type = \'App\\Protocol\'
AND event = \'updated_status\'
AND new_values LIKE \'%pending irb specialist review%\'
OR
auditable_type = \'App\\Protocol\'
AND event = \'updated_status\'
AND new_values LIKE \'%pending faculty sponsor approval%\'
)
AS L ON L.auditable_id = P.id
SET P.submitted_on = L.updated_at
WHERE P.id = L.auditable_id'
);

How to sanitize raw SQL in a Ruby script

I'm trying to write a script that connects with a database using Sequel.
If I have a SQL query like this:
record_values = csv_row.to_h.values.join(', ')
sql_query = "INSERT INTO table (#{ COLUMN_NAMES.join(', ') }) VALUES (#{ record_values })"
and the array record_values is dangerous.
How can I sanitize it?
I tried to sanitize using
ActiveRecord.sanitize_sql_array(sql_query)
but I kept getting the error
NoMethodError: undefined method 'sanitize_sql_array' for ActiveRecord:Module
I don't know Sequel, but did you try standard insert method?
connection = Sequel.connect('...')
table_name = connection.from(:table_name)
# OR
# table_name = DB.from(:table_name)
# table_name = DB[:table_name]
table_name.insert(csv_row.to_h)
It's more reliable I believe, because you avoid difference between COLUMN_NAMES and record_values.

Is there a way to see the raw SQL that a Sequel expression will generate?

Say I have a Sequel expression like:
db.select(:id).from(:some_table).where(:foo => 5)
Is there a way to get the SQL string that this will generate (i.e. "SELECT id FROM some_table WHERE foo = 5")? I notice that calling inspect or to_s on the result of the above expression includes that generated SQL, but not sure how to access it directly.
And how about Sequel expressions that do not return a dataset, like:
db.from(:some_table).update(:foo => 5)
Is it possible to see the SQL from this before it's executed?
You can call sql on dataset:
db.select(:id).from(:some_table).where(:foo => 5).sql # => "SELECT `id` FROM `some_table` WHERE (`foo` = 5)"
For update queries you can do this:
db.from(:some_table).update_sql(:foo => 5) # => "UPDATE `some_table` SET `foo` = 5"
Some similar useful methods:
insert_sql
delete_sql
truncate_sql

SEQUEL - Subquery count syntax

Can anyone help me with the sequel syntax for the following the ruby orm sequel:
SELECT *, (SELECT COUNT(*) FROM todos WHERE reference_email_id = "emails".id) todo_count
FROM "emails"
INNER JOIN "email_participants"
ON ("email_participants"."email_id" = "emails"."id")
WHERE ("user_id" = 1)
I cannot quite get the syntax, I have this so far:
scope = Email.inner_join(:email_participants, {email_id: :id})
.where(user_id: query.user_id)
.select_append {
Attachment.where(reference_email_id: Sequel.qualify(:emails, :id))
.count(:id)
.exists
.as(:attachment_count)
}
I get the following error:
missing FROM-clause entry for table "emails" LINE 1: ... FROM
"attachments" WHERE ("reference_email_id" = "emails"."...
My guess is you should remove the .exists line. It's hard to say conclusively since you didn't post the SQL produced.

How to find records that have duplicate data using Active Record

What is the best way to find records with duplicate values in a column using ruby and the new Activerecord?
Translating #TuteC into ActiveRecord:
sql = 'SELECT id,
COUNT(id) as quantity
FROM types
GROUP BY name
HAVING quantity > 1'
#=>
Type.select("id, count(id) as quantity")
.group(:name)
.having("quantity > 1")
Here's how I solved it with the AREL helpers, and no custom SQL:
Person.select("COUNT(last_name) as total, last_name")
.group(:last_name)
.having("COUNT(last_name) > 1")
.order(:last_name)
.map{|p| {p.last_name => p.total} }
Really, it's just a nicer way to write the SQL. This finds all records that have duplicate last_name values, and tells you how many and what the last names are in a nice hash.
I was beating my head against this problem with a 2016 stack (Rails 4.2, Ruby 2.2), and got what I wanted with this:
> Model.select([:thing]).group(:thing).having("count(thing) > 1").all.size
=> {"name1"=>5, "name2"=>4, "name3"=>3, "name4"=>2, "name5"=>2}
With custom SQL, this finds types with same values for name:
sql = 'SELECT id, COUNT(id) as quantity FROM types
GROUP BY name HAVING quantity > 1'
repeated = ActiveRecord::Base.connection.execute(sql)
In Rails 2.x, select is a private method of AR class. Just use find():
klass.find(:all,
:select => "id, count(the_col) as num",
:conditions => ["extra conditions here"],
:group => 'the_col',
:having => "num > 1")
Here is a solution that extends the other answers to show how to find and iterate through the records grouped by the duplicate field:
duplicate_values = Model.group(:field).having(Model.arel_table[:field].count.gt(1)).count.keys
Model.where(field: duplicate_values).group_by(&:field).each do |value, records|
puts "The records with ids #{records.map(&:id).to_sentence} have field set to #{value}"
end
It seems a shame this has to be done with two queries but this answer confirms this approach.

Resources