SQL Query when where clause could be empty or contain value - mysql

I would like a select query that would be able to select a value that may or may not be present in the where clause. Schema:
----------------------------------
studentid|firstname|lastname|major
My select clause would be
select * from students where studentid?={param} AND firstname?={param} AND lastname?={param} AND major?={param};
I put a question mark because I mean to say I could pass a value in the where clause or I might not. It could be
select * from students where studentid?=34344 AND firstname?="john" AND lastname?="smith" AND major?="";
select * from students where studentid?=34344 AND firstname?="john" AND lastname?="smith" AND major?="english";
Is there a way to do this easily without a stored procedure?

You can do that by using variables and checking null like this:
Declare #StudentId nvarchar(100) --can be null or evaluated
select * from students
where (#StudentId is null or studnetId= #StudentId) AND -- for other properties as well
Another option is using dynamic sql and first you have to build your sql query and then execute it (I don't like it)
You can handle it in the application side if possible:
string query= "select * from students where 1=1 /*trick for adding more conditions*/"
if(numberId is not null)
query += "AND studentId= {numberId} ";
//for other conditions ...

Related

Possible to use IF in a query?

I'm using Grafana to plot data from a MySQL datasource. Is it possible to, in a panel's query editor, use an IF ... THEN ... type statement. I would like to create a variable that I could put in the IF. I want the variable to be a condition, not necessarily to be used directly in the query.
For example:
//IN THE DATA SOURCE:
CREATE TABLE Example (Id INT, ANIMALS VARCHAR(15));
INSERT INTO Example VALUES (1,'Dog'), (2,'Fish'), (3,'Cat'), (4,'Lizard')
For a variable Test with values "Mammal',"Reptile", "Other":
//WHAT I'D LIKE IN GRAFANA QUERY EDITOR:
IF($Test = "Mammal") THEN
SELECT * FROM Example WHERE Id = 1 OR Id =3;
ELSE
SELECT * FROM Example WHERE Id = 2 OR Id =4;
END IF;
Is this kind of condition based query even possible? If so, what is the proper syntax to get it to work? Is there any way I can use Grafana variables to have a similar effect?
Use query. Query starts with SELECT keyword. Don't use any IF ELSE conditions before query, e.g.:
SELECT *
FROM Example
WHERE
Data IN ( ${variable:csv} )
This WHERE condition syntax will work with single value, multi value Grafana dashboard variables and also with All value (no custom All value, but blank=auto). Of course this condition is mainly for INT column types. STRING types may need different one (e.g. with LIKE and regexp matching).
Code all your logic (dependency on the dashboard variable) in the WHERE section. Use query inspector to see SQL which is generated and tweak it to correct SQL syntax.
Instead of an if, you can use or. It's really useful for conditionally checking variables:
select * from Example
where (Id in (1,3) or '$Test' != 'Mammal')
and (Id in (2,4) or '$Test' == 'Mammal')

Where conditions on top of existing sql - alias columns

I am trying to add dynamic filter conditions to existing sql query, I wrapped the query like:
select *
from ( existing sql )
where 1=1 <adding conditions here based on user selection>
In the existing sql I have alias columns on which I am not able to do the query's, can anyone let me know how to do it.
Sample eg:
select *
from (select firstName as "FN", lastName as "LN", city as "c"
from users
)
WHERE 1 = 1 and FN IN ('John');
This is not working, as adding filter on alias column ends in error.
If I dont have alias column in double quotes then its working fine. But I am in need to wrap the existing query and do condition on top of it.
You need an alias for the derived table:
select *
from (select firstName as "FN", lastName as "LN", city as "c"
from users
) x
-------^
WHERE 1 = 1 and FN IN ('John');
Note that in MySQL, derived tables are usually materialized. This can have a big impact on performance, particularly if your query depends on indexes. This route is a bit dangerous from a performance perspective.
Also, your use of in is equivalent to =. The latter is more specific.

sql injection with union

So I am doing a little sql injection challenge because I wanted to learn about it and I have a question. I type 'hi' into the HTML form and I get this back as a response
Error: The following error occurred: [near "hi": syntax error]
Query: SELECT * FROM personnel WHERE name=''hi''
The information we need to get is located in a table called users. I was looking at sql and I see here the union operator which combines the results of 2+ select statements.
So, I try this as input: 1 UNION SELECT * FROM users and I get nothing back so it looks like it searched from that input as a name in table personnel. I thought this would work because the query would look like: SELECT * FROM personnel WHERE name=1 UNION SELECT * FROM users. Am I not understanding how the union operator works or is something else wrong in my input
This one:
set #hi = "'hi'";
select #hi;
SELECT * FROM personnel WHERE name="'hi'"
Simulate:
insert into personnel (`name`) values("'hi'");
insert into personnel (`name`) values("'hello'");
select * from personnel where `name` != "'hi'"
-- you can't use a double '' in sql query
Query: SELECT * FROM personnel WHERE name='hi'
Probably the SQL is invalid because personnel and users have different shape. You need to inject something that is identical to the initial select.
Also your entire problem goes away if you have parameterised queries instead of concatenating into SQL.

MySQL IF on Where clause

Is it possible to make a query that changes the where clause acording to some condition? For instance I want to select * from table1 where data is 19/July/2016 but if field id is null then do nothing, else compare id to something else. Like the query bellow?
Select * from table1 where date="2016-07-19" if(isnull(id),"",and id=(select * from ...))
Yes. This should be possible.
If we assume that date and id are references to columns in (the unfortunately named) table table1, if I'm understanding what you are attempting to achieve, we could write a query like this:
SELECT t.id
, t.date
, t....
FROM table1 t
WHERE t.date='2016-07-19'
AND ( t.id IS NULL
OR t.id IN ( SELECT expr FROM ... )
)
It would also be possible to incorporate the MySQL IF() and IFNULL() functions, if there's some requirement to do that.
As far as dynamically changing the text of the SQL statement after the statement is submitted to the database, no, that's not possible. Any dynamic changes to the SQL text would need to be done when the SQL statement is generated, before it is submitted to the database.
My personal preference would be to use a join operation rather than a IN (subquery) predicate.
I think you're trying too hard. If id is NULL that's equivalent to having a FALSE in the where clause. So:
Select * from table1 where date="2016-07-19" and id=(select * from ...)
Should only match the records you want. If id is NULL you get nothing.

Stored procedure to execute a query and return selected values if the query returns only 1 result

So my query is the following, which may return many results:
SELECT P_CODE, NAME FROM TEST.dbo.PEOPLE
WHERE NAME LIKE '%JA%'
AND P_CODE LIKE '%003%'
AND DOB LIKE '%1958%'
AND HKID = ''
AND (MOBILE LIKE '%28%' OR TEL LIKE '%28%')
I would like to integrate this into a Stored Procedure (or View?) so that it will only return a result if the query results in exactly 1 row. If there's 0 or > 1, then it should return no results.
If you just want to return an empty resultset in cases other than 1:
;WITH x AS
(
SELECT P_CODE, NAME, c = COUNT(*) OVER()
FROM TEST.dbo.PEOPLE
WHERE NAME LIKE '%JA%'
AND P_CODE LIKE '%003%'
AND DOB LIKE '%1958%'
AND HKID = ''
AND (MOBILE LIKE '%28%' OR TEL LIKE '%28%')
)
SELECT P_CODE, NAME FROM x WHERE c = 1;
Otherwise, you'll have to run the query twice (or dump the results to intermediate storage, such as a #temp table) - once to get the count, and once to decide based on the count whether to run the SELECT or not.
Effectively you want something akin to FirstOrDefault() from the Linq-to-SQL implementation but done on the server-side which means you will need to execute the query in a stored procedure, dumping the results into a temp table variable and then access ##ROWCOUNT afterwards to get the number of rows that were returned and then decide whether or not to forward the results on to the caller. If you do, be sure to use TOP 1 in the query from the temp table so that you only get a single result out as you desire.
UPDATE:
I described the alternate solution from what Aaron describes in his answer (which I like better).
Removed unnecessary TOP specifier in solution specification.