Override column value in WHERE clause? - mysql

I was wondering if it was possible to override column value in the Where clause of a SQL query (MySQL in my case).
To be more clear, here is an example :
Suppose a basic query is :
SELECT lastname, firstname FROM contacts WHERE lastname = "Doe";
Is it possible to force lastname and firstname to return value from an other table, just by modifying what is after the WHERE part ? Something like
SELECT lastname, firstname FROM contacts WHERE lastname = (SELECT name FROM companies);
I am currently testing a web application, and I found a SQL Injection flaw where I can change Doe to whatever I want, but I'm limited with only one query (mysql_query restriction of PHP) and addslashes (so no " and ').

possible could be
SELECT lastname, firstname FROM contacts WHERE lastname = "{0}" UNION SELECT {1} --
where {0} non existed value and {1} data from other tables
UPDATE from wiki example
$res = mysql_query("SELECT author FROM news WHERE id=" . $_REQUEST['id'] ." AND author LIKE ('a%')");
become
SELECT author FROM news WHERE id=-1 UNION SELECT password FROM admin/* AND author LIKE ('a%')

The syntax that you used in your SELECT ... WHERE clause is a standard SQL feature called a subquery.
In the context of your example there is a restriction on the subquery to return just single value. Otherwise your query is a valid SQL and you can change subquery to return multiple values (with implicit OR) using IN operator like this:
SELECT lastname, firstname FROM contacts
WHERE lastname IN (
SELECT name FROM companies
);
You can dig deeper into this subject to uncover correlated subquery.

Related

MySQL/ORACLE performance using LIKE and CONCAT

Which one of these two queries would perform better in MySQL and/or ORACLE?
SELECT *
FROM User
WHERE name LIKE "%searchTerm%"
OR lastname LIKE "%searchTerm%"
OR email LIKE "%searchTerm%";
or
SELECT *
FROM User
WHERE CONCAT(name, " ", lastname, " ", email) LIKE "%searchTerm%";
I have a strong feeling that the second one, but I'd like to be sure.
SELECT *
FROM User
WHERE name LIKE "%searchTerm%"
OR lastname LIKE "%searchTerm%"
OR email LIKE "%searchTerm%";
This query is better then second one. Because if you want to use different filter on all the Fields then you can use them easily.
SELECT *
FROM User
WHERE CONCAT(name, " ", lastname, " ", email) LIKE "%searchTerm%";
In this query you can not use different filter. You have to use common filter on all the fields.
First of all the two queries are not equivalent. In case that a blank (the concatenation delimiter) may be used in the searchTerm, the first query may not match, but the second can, because the match spans two or three columns.
Performance will be very same as both queries make full table scan and the difference is only in the filter condition
First Query
1 - filter("NAME" IS NOT NULL AND "NAME" IS NOT NULL AND "NAME" LIKE
'%searchTerm%' OR "LASTNAME" IS NOT NULL AND "LASTNAME" IS NOT NULL AND
"LASTNAME" LIKE '%searchTerm%' OR "EMAIL" IS NOT NULL AND "EMAIL" IS
NOT NULL AND "EMAIL" LIKE '%searchTerm%')
Second Query
1 - filter("NAME"||' '||"LASTNAME"||' '||"EMAIL" LIKE '%searchTerm%')
So basically neither query is suitable for a customer search on non trival tables.
You typically want to limit the search to column LIKE 'xxxxxx%' which can use an index.
(MySQL Answer)
Either way will involve a full table scan, so neither will be fast. (Fetching rows is more costly than evaluating expressions.) Furthermore, the leading wildcard implies that the string(s) must be fully scanned; no INDEX usable possible.
If your "serachTerm" is always word(s), then use FULLTEXT(name, lastname, email) and MATCH(name, lastname, email) AGAINST ("+searchTerm" IN BOOLEAN MODE); it will be a lot faster.

Provide prepared statement in rails

I am using rails-4.2.1 and is trying to fetch data from two tables subjects and elective_subjects table in a single query. As rails 4 does not support UNION , I wrote a raw sql query. I want to search by name in both tables. My code is given below
query = "(SELECT id as id, name as name, reference as reference from subjects where name like '#{search}') UNION (SELECT id as id, name as name, null as reference from elective_subjects where name like '#{search}')"
#subjects = ActiveRecord::Base.connection.execute(query)
It is working but when I provide ' in my search the query breaks. So how can I make it a prepared statement. So that sql injection can be avoided
This question is super old and no cares anymore, but I think it is a valid question, so here's the answer:
query = "(SELECT id as id, name as name, reference as reference from subjects where name like $1)
UNION
(SELECT id as id, name as name, null as reference from elective_subjects where name like $1)"
binds = [ActiveRecord::Relation::QueryAttribute.new('name', search, ActiveRecord::Type::Text.new)]
result = ApplicationRecord.connection.exec_query(query, 'SQL', binds, prepare: true)
#subjects = result.rows
That's how you create and use a prepared statement in rails.
I have solved the issue by escaping the search string using following statement.
search = Mysql2::Client.escape(search)

Nesting an "SQL SELECT STATEMENT" as/inside the REGEX operator

Say I have a table called People with columns PersonID and Name and I can select a Person's Name like:
SELECT Name FROM People WHERE PersonID = 1
which for this example will return 'John'.
I also have another table called ForumPosts with the fields ForumPostID and PostContent where PostContent is just TEXT which for the purpose of this example can be something like "My Name is John" or "John likes football"
Now I want to perform a Query which based on a given initial PersonID will return all rows from ForumPosts where the Person's Name matches a word contained in the PostContent field.
A regex which will match single words (or in this case the person's name) is:
[[:<:]]*Person'sNameHere*[[:>:]]
So ideally I want my SQL logic to be something like:
Select * FROM ForumPosts WHERE PostContent
REGEX [[:<:]](SELECT Name FROM People WHERE PersonID = '1') [[:>:]]
However I am not sure if this is even possible or how I would structure the query.
Sounds like you want to create a regex dynamically. Regexes are just strings in MySQL, so you can just use CONCAT to create the string you want.
SELECT *
FROM ForumPosts
WHERE PostContent
REGEXP CONCAT('[[:<:]]',(SELECT Name FROM People WHERE PersonID = '1'),'[[:>:]]')
Even better, you can use a JOIN instead of a subquery
SELECT ForumPosts.*
FROM ForumPosts
JOIN People ON PersonID = 1
WHERE PostContent REGEXP CONCAT('[[:<:]]',People.Name,'[[:>:]]')
DEMO: http://sqlfiddle.com/#!2/40828/1
You can implement this logic using an exists subquery. If I understand your logic correctly:
select fp.*
from ForumPosts fp
where exists (select 1
from people p
where personid = '1' and
fp.PostContent regex concat('[[:<:]]', name, '[[:>:]]')
)

Sql query returns only one row (LIKE)

SELECT *
FROM customers
WHERE Firstname LIKE 'George'
The problem is that i have more than 1 rows in the table with tha name Geoge and the result of the query shows only one row
You will want to include the wildcard % character to include the rows the have George present in the name:
SELECT *
FROM customers
WHERE Firstname LIKE '%George%';
If George will always appear at the beginning, then you can include the wildcard on the end:
SELECT *
FROM customers
WHERE Firstname LIKE 'George%';
you need to add a wildcard character % to match any value that contains george
SELECT *
FROM customers
WHERE Firstname LIKE '%George%'
MySQL LIKE Operator
the statement
WHERE Firstname LIKE 'George'
is equivalent with
WHERE Firstname = 'George'
that is why you are only getting one record which firstname is george.
UPDATE 1
SQLFiddle Demo
try
LOWER(Firstname) LIKE '%george%'
handles partial values and avoids case sensietivity issues.

mysql SELECT * FROM table WHERE column+space+column LIKE '%query%'

For a search function, I need to be able to get the mysql function to show up results by combining two columns.
To be more precise: I have a table in the database called "members" in that there is a column called "firstname" and one called "lastname" which I need to use for this. When someone types in a whole name, eg. Jane Doe, I want the query to look whether combining firstname + a space(&nbsp?) + lastname brings forth any results.
My code so far is this:
$poster = mysql_query("SELECT id FROM members WHERE (firstname'&nbsp'lastname) LIKE '%$search%'") or die(mysql_error());
$poster = mysql_fetch_object($poster);
I know that's probably wrong, it's the most recent I've tried after trying with brackets around the firstname + lastname bits, etc, etc... But yes, any help would be greatly appreciated.
Use the CONCAT function:
SELECT id FROM members
WHERE CONCAT(firstname, ' ', lastname) LIKE ?
Try CONCAT
SELECT id FROM members WHERE concat(firstname, ' ', lastname) LIKE '%$search%'
SELECT id FROM members WHERE (firstname + ' ' + lastname) LIKE '%$search%'
I believe what you are looking for is:
$name_var = 'John Doe';
// Example 1 - using concatenation
$sql = "SELECT id FROM members WHERE CONCAT(firstname, ' ', lastname) = '$name_var'";
The above statement will search for everything where the first name is John and the last name is Doe
This is rather ineficcient as it will have to evaluate the CONCAT everytime in mysql I believe
I would reccomend validating in PHP that the string is two words as you expect e.g.
$name_var = 'John Doe';
// this will split the string based on spaces
$names = explode(' ', $name_var);
$first_name = $names[0];
$last_name = $names[1];
// Example 2 - searching each field
$sql = "SELECT id FROM members WHERE firstname = '$first_name' AND lastname = '$last_name'";
The above statement will still search for everything where the first name is John and the last name is Doe
In the above statment you are actually just searching based on the exact values so it is much more efficient. If this query is going to be ran regularly, you should also add indexes to the firstname and lastname fields in your mysql table as it will greatly increase the speed!
hope this helps!
Tom