SQL Query - Use Like only if no exact match exists? - sql

I'm having an issue with a query that currently uses
LEFT JOIN weblog_data AS pwd
ON (pwd.field_id_41 != ''
AND pwd.field_id_41 LIKE CONCAT('%', ewd.field_id_32, '%'))
However I'm discovering that I need it to only use that if there is no exact match first. What's happening is that the query is double dipping due to the use of LIKE, so if it tests for an exact match first then it will avoid the double dipping issue. Can anyone provide me with any further guidance?

It sounds like you want to join the tables aliased as pwd and ewd in your snippet based first on an exact match, and if that fails, then on the like comparison you have now.
Try this:
LEFT JOIN weblog_data AS pwd1 ON (pwd.field_id_41 != '' AND pwd.field_id_41 = ewd.field_id_32)
LEFT JOIN weblog_data AS pwd2 ON (pwd.field_id_41 != '' AND pwd.field_id_41 LIKE CONCAT('%', ewd.field_id_32, '%'))
Then, in your select clause, use something like this:
select
isnull(pwd1.field, pwd2.field)
however, if you are dealing with a field that can be null in pwd, that will cause problems, this should work though:
select
case pwd1.nonnullfield is null then pwd2.field else pwd1.field end
You'll also have to make sure to do a group by, as the join to pwd2 will still add rows to your result set, even if you end up ignoring the data in it.

you're talking about short circuit evaluation.
Take a look at this article it might help you:
http://beingmarkcohen.com/?p=62

using TSQL, run an exact match, check for num of rows == 0, if so, run the like, otherwise don't run the like or add the like results below the exact matches.

I can only think of doing it in code. Look for an exact match, if the result is empty, look for a LIKE.
One other option is a WHERE within this query such that WHERE ({count from exact match}=0), in which case, it wont go through the comparison with LIKE if the exact match returns more than 0 results. But its terribly inefficient... not to mention the fact that using it meaningfully in code is rather difficult.
i'd go for a If(count from exact match = 0) then do like query, else just use the result from exact match.

Related

Which one of these two SELECT statment are correct?

I am a little bit confusing and have no idea which one of these two SELECT statments are correct
SELECT Value FROM visibility WHERE site_info LIKE '%site_is_down%';
OR
SELECT Value FROM visibility WHERE site_info = 'site_is_down';
SInce I run both of these I get same result, but I am interesting which one is correct since Value column is VARCHAR data type OR both of these SELECT are incorect ?
Result set running first SELECT
Value
1. 0
Result set running second SELECT
Value
1. 0
The two statements do not do the same thing.
The first statement filters on rows whose site_infos contain string 'site_is_down'. The surrounding '%' are wildcards. So it would match on something like 'It looks like site_is_down right now'.
The second query, with the equality condition, filters on site_info whose content is exactly 'site_is_dow'.
Everything that the second query is also returned by the first query - but the opposite is not true.
Which statement is "correct" depends on your actual requirement.
If both queries are useful for you, I'd use the second query, as it is the simplest, and runs faster.

Is there a way to use IF to show NULL values only when no input has been done?

I need help with a IF-statement or similar
This is to let me make a query report from our system but in some cases EXP_RCPT_NO doesnt have a value and i need a code that let me search for a specific value in EXP_RCPT_NO without showing empty results but at the same time shows the empty results when i search only il.lic_no
select il.lic_no,
EXP_RCPT_NO
from inv_lic il
where il.lic_no like nvl ('&Licens', '%')
and EXP_RCPT_NO like nvl ('&Order', '%')
It works if i remove the bottom row, but then it removes the option to search EXP_RCPT_NO
I think you want:
select il.lic_no, il.EXP_RCPT_NO
from inv_lic il
where il.lic_no like coalesce('&Licens', '%') and
(il.EXP_RCPT_NO like '&Order' or '&Order' is null);
You may want the or logic for both conditions, but you only asked about the second one.
Question confusing. It's Oracle right? I'm not completely understanding your question but something like this? The nvl is same as ISNULL is SQL Server. Normally you put this around your column(s).
select il.lic_no,
EXP_RCPT_NO
from inv_lic il
where nvl(il.lic_no, 'Licens') like '%Licens%'
OR nvl(EXP_RCPT_NO, 'Order') like '%Order%'

Why am I getting results that don't match the first WHERE clause?

WHERE [SOTR_CUST_CODE] = 'O004'
AND [SOTD_STRC_CODE] LIKE 'PC%'
OR [SOTD_STRC_CODE] LIKE 'PD%'
This returns records of customers that are not 'O004', and I'm not sure why. Is there also a better way to search for a string that could start with 2 different sets of characters without using the LIKE function twice?
Using SQL Server 2012
You need to use parentheses in your clause. Without parentheses, it means:
(A and B) OR C
Therefore you will get all records matching condition C regardless of condition A or B.
I know it already has very good answer (thanks to Laposhasu Acsa), but wanted to clarify for future readers:
Below code (without parentheses) is the same as (A and B) or C
WHERE [SOTR_CUST_CODE] = 'O004'
AND [SOTD_STRC_CODE] LIKE 'PC%'
OR [SOTD_STRC_CODE] LIKE 'PD%'
First solution is to use parentheses:
WHERE [SOTR_CUST_CODE] = 'O004'
AND ([SOTD_STRC_CODE] LIKE 'PC%'
OR [SOTD_STRC_CODE] LIKE 'PD%')
Second solution, which suits this particular case is:
WHERE [SOTR_CUST_CODE] = 'O004'
AND [SOTD_STRC_CODE] LIKE 'P[CD]%'

Regex: does a SQL statement include a WHERE clause?

I need a regex that will determine if a given SQL statement has a WHERE clause. My problem is that the passed SQL statements will most likely be complex, so I can not rely on just the existence of the word WHERE in the statement.
For example this should match
SELECT Contacts.ID
, CASE WHEN (Contacts.Firstname IS NULL) THEN ''
ELSE CAST(Contacts.Firstname AS varchar)
END AS Firstname
, CASE WHEN (Contacts.Lastname IS NULL) THEN ''
ELSE CAST(Contacts.Lastname AS varchar)
END AS Lastname
, CASE WHEN (tbl_ContactExtras.Prequalified=-1 OR
tbl_ContactExtras.Prequalified IS NULL) THEN ''
WHEN tbl_ContactExtras.Prequalified=0 THEN 'No'
WHEN tbl_ContactExtras.Prequalified=1 THEN 'Yes - Other'
WHEN tbl_ContactExtras.Prequalified=2 THEN 'Yes'
ELSE CAST(tbl_ContactExtras.Prequalified AS varchar)
END AS Prequalified
FROM contacts
LEFT JOIN tbl_ContactExtras
ON tbl_ContactExtras.ContactID = Contacts.ID
WHERE (Contacts.Firstname LIKE 'Bob%')
and this should not match:
SELECT Contacts.ID
, CASE WHEN (Contacts.Firstname IS NULL) THEN ''
ELSE CAST(Contacts.Firstname AS varchar)
END AS Firstname
, CASE WHEN (Contacts.Lastname IS NULL) THEN ''
ELSE CAST(Contacts.Lastname AS varchar)
END AS Lastname
, CASE WHEN (tbl_ContactExtras.Prequalified=-1 OR
tbl_ContactExtras.Prequalified IS NULL) THEN ''
WHEN tbl_ContactExtras.Prequalified=0 THEN 'No'
WHEN tbl_ContactExtras.Prequalified=1 THEN 'Yes - Other'
WHEN tbl_ContactExtras.Prequalified=2 THEN 'Yes'
ELSE CAST(tbl_ContactExtras.Prequalified AS varchar)
END AS Prequalified
FROM contacts
LEFT JOIN tbl_ContactExtras
ON tbl_ContactExtras.ContactID = Contacts.ID
Those are examples of some of the simpler statements: a statement could have up to 30 CASE statements in it, or it could have none at all.
I need to programmatically add WHERE parameters, but doing this correctly requires knowing whether a WHERE clause is already present.
Any idea on a regex that would work for this? If not, any other ideas on how to tell the two apart?
Thanks,
This is not possible, since a WHERE clause may be arbitrarily nested inside the FROM clause.
This may not catch all cases but you may find you can catch most of them just by finding the last from and the last where in the statement.
if the where is after the from, then it has a where clause. If the where is before the from (or there is no where at all), then no where clause exists.
Sometimes, it's okay to leave restrictions or limitations in your code, as long as they're properly documented.
For example, I've worked on a project before that parsed SQL and we discovered it didn't handle things like between:
where recdate between '2010-01-01' and '2010-12-31'
Rather than spend a bucket-load of money fixing the problem (and probably introducing bugs on the way), we simply published it as a restriction and told everyone they had to change it to:
where recdate >= '2010-01-01'
and recdate <= '2010-12-31'
Problem solved. While it's good to keep customers happy, you don't have to cater to every whim :-)
Other than that, you need an SQL parser, and SQL is not a pretty language to parse, trust me on that one.
Are all of the joins the same? If so you could find the index of all or part of the FROM statement (perhaps using a regex to be tolerant of slight differences in syntax and whitespace) and then look for the occurrence of the word WHERE after that index.
In general you would be better off using a parser. But if this is just a one off thing and the statements are all fairly similar then the above approach should be okay.
Regex is not designed to do this. Parsing SQL properly requires matching balanced parentheses (and other matching pairs, such as quotes), something regex is not designed to do (and pure regex isn't even equipped to; PCRE can but it's not pretty).
Instead, just write a basic state machine or something to parse it.
What's the problem you're trying to solve? Are you trying to determine if it's safe to add constraints to these existing queries?
For example, if you've got this query
...
where foo = 'bar'
then you know it's safe to add
and bat = 'quux'
but if you don't have a WHERE clause already, then you have to do it as
where bat = 'quux'
Is that the problem you're trying to solve? If so, can you make every SQL query you're working with have a WHERE clause by adding a "WHERE 0=0" to those queries that don't have one? Then you know in your post-process phase that every query already has one.
This is just a guess, of course. Your question sounded like that might be the larger issue.

SQL produced by Entity Framework for string matching

Given this linq query against an EF data context:
var customers = data.Customers.Where(c => c.EmailDomain.StartsWith(term))
You’d expect it to produce SQL like this, right?
SELECT {cols} FROM Customers WHERE EmailDomain LIKE #term+’%’
Well, actually, it does something like this:
SELECT {cols} FROM Customer WHERE ((CAST(CHARINDEX(#term, EmailDomain) AS int)) = 1)
Do you know why?
Also, replacing the Where selector to:
c => c.EmailDomain.Substring(0, term.Length) == term
it runs 10 times faster but still produces some pretty yucky SQL.
NOTE: Linq to SQL correctly translates StartsWith into Like {term}%, and nHibernate has a dedicated LikeExpression.
I don't know about MS SQL server but on SQL server compact LIKE 'foo%' is thousands time faster than CHARINDEX, if you have INDEX on seach column. And now I'm sitting and pulling my hair out how to force it use LIKE.
http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/1b835b94-7259-4284-a2a6-3d5ebda76e4b
The reason is that CharIndex is a lot faster and cleaner for SQL to perform than LIKE. The reason is, that you can have some crazy "LIKE" clauses. Example:
SELECT * FROM Customer WHERE EmailDomain LIKE 'abc%de%sss%'
But, the "CHARINDEX" function (which is basically "IndexOf") ONLY handles finding the first instance of a set of characters... no wildcards are allowed.
So, there's your answer :)
EDIT: I just wanted to add that I encourage people to use CHARINDEX in their SQL queries for things that they didn't need "LIKE" for. It is important to note though that in SQL Server 2000... a "Text" field can use the LIKE method, but not CHARINDEX.
Performance seems to be about equal between LIKE and CHARINDEX, so that should not be the reason. See here or here for some discussion. Also the CAST is very weird because CHARINDEX returns an int.
charindex returns the location of the first term within the second term.
sql starts with 1 as the first location (0 = not found)
http://msdn.microsoft.com/en-us/library/ms186323.aspx
i don't know why it uses that syntax but that's how it works
I agree that it is no faster, I was retrieving tens of thousands of rows from our database with the letter i the name. I did find however that you need to use > rather than = ... so use
{cols} FROM Customer WHERE ((CAST(CHARINDEX(#term, EmailDomain) AS int)) > 0)
rather than
{cols} FROM Customer WHERE ((CAST(CHARINDEX(#term, EmailDomain) AS int)) = 1)
Here are my two tests ....
select * from members where surname like '%i%' --12 seconds
select * from sc4_persons where ((CAST(CHARINDEX('i', surname) AS int)) > 0) --12 seconds
select * from sc4_persons where ((CAST(CHARINDEX('i', surname) AS int)) = 1) --too few results