I have this line of code in Rails
#people = User.where("firstname ## :query or lastname ## :query", query: #query )
This works fine finding a user called Joe Smith if searching for Joe or Smith seperatly.
How can I change this line to fetch users if searching for Joe Smith?
#people = User.where("firstname ## :query OR lastname ## :query OR ((firstname || " " || lastname) = ?) ## :query", query: #query )
See How to concat two fields in Postgresql
Related
I have a table of email addresses:
CREATE TABLE contacts(
email VARCHAR(255)
)
INSERT INTO contacts VALUES
('example.person#gmail.com'),
('example.person2#gmail.com'),
('example.person3#gmail.com');
How can I find and replace the email format so example.person#gmail.com -> example.person_gmailcom#test.com?
E.g:
UPDATE contacts
SET email = REGEXP_REPLACE(email, '#', '#test.com');
Results in example.person#test.comgmail.com
Playground here: https://dbfiddle.uk/GnIfomiO
This is probably most simply done by splitting the email address in two on the #, keeping the part before it and replacing . in the part after it with nothing. Then you can just append #test.com to the result:
UPDATE contacts
SET email = SPLIT_PART(email, '#', 1) || '_' || REPLACE(SPLIT_PART(email, '#', 2), '.', '') || '#test.com';
Output for your demo:
email
example.person_gmailcom#test.com
example.person2_gmailcom#test.com
example.person3_gmailcom#test.com
Demo on dbfiddle
demo: https://dbfiddle.uk/0KWPVeAI
UPDATE contacts
SET email = REGEXP_REPLACE(email, '#', '_gmailcom#');
UPDATE contacts
SET email = REGEXP_REPLACE(email, '#.*$', '#test.com');
The regex pattern is # follow all the chars to end of string
Pretty simple, trying to do this
SELECT (artist_name || ' ' || name) as full_name FROM "songs" WHERE "songs"."working" = 't' AND (full_name ILIKE('%Jack Beats%')) AND (full_name ILIKE('%Epidemic%')) AND (full_name ILIKE('%Dillon Francis%')) ORDER BY songs.published_at asc LIMIT 1
But I get
ActiveRecord::StatementInvalid: PG::Error: ERROR: column "full_name" does not exist
I've tried adding the table name before the stations with no effect.
As sub_stantial mentions in the comments, you can't reference an alias from a SELECT in your WHERE clause. You can use a derived table as dwurf suggests but derived tables in Rails are a bit messy. You could expand your concatenation inside your WHERE instead:
Song.where(:working => true)
.where("artist_name || ' ' || name ILIKE ?", '%Jack Beats%')
.where("artist_name || ' ' || name ILIKE ?", '%Epidemic%')
.where("artist_name || ' ' || name ILIKE ?", '%Dillon Francis%')
.order('songs.published_at asc')
.limit(1)
And if you're doing this sort of thing a lot, a named scope might be useful:
class Song < ActiveRecord::Base
#...
def self.full_name_like(name)
where("artist_name || ' ' || name ILIKE ?", "%#{name}%")
end
end
and then:
Song.where(:working => true)
.full_name_like('Jack Beats')
.full_name_like('Epidemic')
.full_name_like('Dillon Francis')
.order('songs.published_at asc')
.limit(1)
If your application is going to be doing a lot of ILIKE searches like this then you might want to look into a full-text search system: LIKE queries lead to table scans and table scans lead to sadness.
You can't reference a column alias in a where clause. The correct way to write this query is:
SELECT
(artist_name || ' ' || name) AS full_name
FROM "songs"
WHERE "songs"."working" = 't'
AND ((artist_name || ' ' || name) ILIKE('%Jack Beats%'))
AND ((artist_name || ' ' || name) ILIKE('%Epidemic%'))
AND ((artist_name || ' ' || name) ILIKE('%Dillon Francis%'))
ORDER BY songs.published_at ASC
limit 1
;
sub_stantial's approach would look more like this:
select full_name
from (
SELECT
(artist_name || ' ' || name) AS full_name
FROM "songs"
WHERE "songs"."working" = 't'
ORDER BY songs.published_at ASC
)
WHERE (full_name ILIKE('%Jack Beats%'))
AND (full_name ILIKE('%Epidemic%'))
AND (full_name ILIKE('%Dillon Francis%'))
LIMIT 1
;
Performance of these two queries is about the same (pretty rubbish) as they both have to do a full table scan to build the full_name column then sort the results. You might be able to add an index to "working" to speed up these queries.
Here's an sql fiddle in postgresql
Programatic Arel version
NOTE: This has not been fully tested for SQL injection.
class ApplicationRecord < ActiveRecord::Base
scope :fields_sentence_ilike, -> (*fields, term) {
sanitized_term = connection.quote("%#{term}%")
# InfixOperation.new(operator, left, right) => left operator right => concatenated_fiels ILIKE '%word%'
# NamedFunction.new(name, expression_nodes) => name(node0, node1, ...nodeN) => CONCAT_WS("columnA", "columnB", "columnC")
where(
Arel::Nodes::InfixOperation.new(
Arel::Nodes::SqlLiteral.new('ILIKE'),
Arel::Nodes::NamedFunction.new(
'CONCAT_WS', # CONCAT_WS concatenates strings using the first argument. In this case, an empty space.
[
Arel::Nodes::SqlLiteral.new("' '"), # CONCAT by empty space
*fields.map { |field|
# CONCATING any NULL fields results NULL (like multiplying any number by 0 equals 0). COALESCE to empty string.
Arel::Nodes::NamedFunction.new('COALESCE', [arel_attribute(field), Arel::Nodes::SqlLiteral.new("''")])
}
]
),
Arel::Nodes::SqlLiteral.new(sanitized_term)
)
)
}
end
Then a specific implementation for your Songs model
class Song < ApplicationRecord
scope :full_name_like, -> (full_name) { fields_sentence_ilike(:artist_name, :name, full_name) }
end
Usage
Song.full_name_like('Jack Beats')
.full_name_like('Epidemic')
.full_name_like('Dillon Francis')
I have a TComboBox containing a list of names gathered from my database. Next to it is a TEdit that I intend on using for the purposes for displaying the ID number associated to each person.
Since firstName and lastName are separate fields within the table, but displayed together in the TCombobox I have written a small section to split the firstName and lastName into two separate variables:
pos := AnsiPos(' ', cbStudents.Text);
firstName := Copy(cbStudents.Text, 0, pos-1);
lastName := Copy(cbStudents.Text, pos+1, Length(cbStudents.Text));
Then I execute the SQL code:
try
query.Open;
query.SQL.Add('Select studentID');
query.SQL.Add('From student');
query.SQL.Add('Where firstName = ' + StrToQuote(firstName));
query.SQL.Add('And lastName = ' + StrToQuote(lastName));
editID.Text := query
finally
query.Free;
end;
Note: StrToQuote encapsulates the variable firstName and lastName with double quotes (" ")
The error that I am receiving is:
Argument out of range
What am I doing wrong? Thank you in advanced for the help.
Your code can not work. It opens the query first, then it sets the SQL query string. Instead of
try
query.Open;
query.SQL.Add('Select studentID');
query.SQL.Add('From student');
query.SQL.Add('Where firstName = ' + StrToQuote(firstName));
query.SQL.Add('And lastName = ' + StrToQuote(lastName));
finally
query.Free;
end;
use
// create or reset query here
query := ...
try
query.SQL.Add('SELECT studentID');
query.SQL.Add('FROM student');
query.SQL.Add('WHERE firstName = :firstname');
query.SQL.Add('AND lastName = :lastName');
// set parameter values here
query.Open;
// now transfer data from fields to the user interface (TEdit)
finally
query.Free;
end;
Your approach does not seam optimal for me (splitting Displayed Name), but your problem here would be accessing query.Fields[0].AsString after freeing the query.
I've implemented an autosuggest search using jQuery, php and SQL server 2008. I'm looking for people by their name. The name of the person is divided in three fields 'nombres, 'apellido_paterno' and 'apellido_materno'. My autosuggest matches results where one of the three fields looks like the pattern in the input text.
$values = array(':x'=>$data['term'].'%',':y'=>$data['term'].'%',':z'=>$data['term'].'%');
$sql = "SELECT TOP 10 id_persona, nombres +' '+ apellido_paterno +' '+ apellido_materno AS value
FROM personas WHERE nombres LIKE :x OR apellido_paterno LIKE :y OR apellido_materno LIKE :z";
So my query is working fine if you search by name or lastname, however if you search by full name there are no matches. So, how do I add criteria to my query in order to bring full name matches?
Assuming all three are varchar fields, and you are querying a somewhat limited number of records, I would just do:
$values = array(':x'=>'%'.$data['term'].'%');
$sql = "SELECT TOP 10 id_persona, nombres +' '+ apellido_paterno +' '+ apellido_materno AS value
FROM personas
WHERE
nombres + ' ' + apellido_paterno + ' ' + apellido_materno LIKE :x";
I have a users table with name and surname and a search form with only a full_name field. How can I get it working?
I tried with this simple code:
where_clause = "(name + ' ' + surname) LIKE ?"
# page is a will_paginate method
#users = User.where([where_clause, "%#{params[:full_name]}%"]).page params[:page]
The above code produces:
SELECT "users".* FROM "users" WHERE ((name + ' ' + surname) LIKE '%test%') LIMIT 30 OFFSET 0
... which gives no results! Why?
I think the problem relies in the concatenation, but I can't see any alternatives. In fact I can't modify the select clause (i.e. "*, (name + ' ' + surname) AS full_name") because the page method doesn't work with active record find, and will_paginate methods (i.e. page or paginate) seems not to support conditions any more.
Try using:
where_clause = "(name || ' ' || surname) LIKE ?"