Is there another style for writing LIKE queries? - sql

I write some queries like this:
SELECT *
FROM Sample.dbo.Temp
WHERE
Name LIKE '%ab%'
OR Name LIKE '%fg%'
OR NAME LIKE '%asd%'
OR ...
OR ...
OR Name LIKE '%kj%'
Is there anyway I can rewrite this query like this:
SELECT *
FROM Sample.dbo.Temp
WHERE
Name LIKE (
'%ab%'
OR '%fg%'
OR '%asd%'
OR ...
OR ...
OR '%kj%'
)
Just looks more comfortable both from a readability point of view and manageability. If the column Name changes, I can always make one change instead of a hundred (or using Find and Replace). Any suggestions?

No, you have to keep repeating the LIKE
Although you could probably fool around a bit to make it work something like that, it won't be prettier or more readable.
Perhaps you should generate the query programmatically instead of manually writing this?
PS: perhaps a fulltext index is a better idea here?

You can put the values in a table, perhaps a CTE, and semijoin to your table e.g.
WITH params
AS
(
SELECT *
FROM (
VALUES ('at'),
('fg'),
('asd'),
('kj')
) AS T (param)
)
SELECT *
FROM Sample.dbo.Temp T
WHERE EXISTS (
SELECT *
FROM params P
WHERE T.Name LIKE '%' + P.param + '%'
);
That looks long winded but if the CTE was instead a base table them the query could be data-driven i.e. if the list of parameter values need to change in the future then it would involve merely updating a table rather than amending hard-coded values (possibly in multiple objects).

Related

Improve the performance of query containing upper and nvl fucntion

INSERT INTO tab2 NOLOGGING
SELECT
ID,
ORG_NAME
FROM tab3
WHERE (( upper(NVL(org_name,company_given)) LIKE '%MSOFT%'
OR upper(NVL(org_name,company_given)) LIKE 'M SOFT'
OR upper(NVL(org_name,company_given)) LIKE '%MISOFT%'
OR upper(NVL(org_name,company_given)) LIKE 'MSN %'
OR upper(NVL(org_name,company_given)) LIKE '%N APP%'
OR upper(NVL(org_name,company_given)) LIKE '%NAPP%'
OR upper(NVL(org_name,company_given)) LIKE '%NAPPE%'
OR upper(NVL(org_name,company_given)) LIKE '%NAPPS%'
OR upper(NVL(org_name,company_given)) LIKE '%NEK%APPLIANCE%'
the above coding is taking too much time. Table tab3 is very huge.
The above is dynamic. Any alternatives for nvl?
The line below
OR upper(NVL(org_name,company_given)) LIKE 'M SOFT'
could be replaced with
OR ((orgname is not null and upper(org_name) LIKE 'M SOFT')
OR ((orgname is null and upper(company_given) LIKE 'M SOFT')
Not sure it's faster.
Also you can try to run it once with subquery
SELECT *
FROM (
SELECT
ID,
ORG_NAME,
upper(NVL(org_name,company_given)) as name_for_filter
FROM tab3)
WHERE name_for_filter LIKE '%MSOFT%'
OR name_for_filter LIKE 'M SOFT'
...
The best way would be to introduce a name_for_filter column in the table and fill it once with a trigger. Then the column could be used for the filtering
This query is going to execute a full table scan of your table. You say that table is huge, so it's going to take a long time.
A normal index won't help because there are two columns in play. Even a function-based index like this ...
create index fbi3 on tab3( upper(NVL(org_name, company_given) ))
... won't help because indexes are useless against a like filter with a wildcard at the front, and you have those:
LIKE '%NEK%APPLIANCE%'
If this is a one-time exercise I would suggest you swallow the time and wait for the statement to finish. But let's assume you want to do this kind of query often. If so, it's worth building infrastructure to support it.
A new column for the search criteria. Basically a column which is pre-populated with the arguments used in the functions. For 11g or higher make this a virtual column:
alter table tab3 add search_name as ( upper(NVL(org_name, company_given)));
If using an older version of the database you will have to build a normal column and populate it with triggers.
Build a Text index on the search_name column. As it is short you can use a CTXCAT index, which will be maintained transactionally.
Then you need to rewrite the query to use catsearch() syntax instead of like operator. Find out more
As already suggested, it's probably best to create a prepared search column. You could even remove the spaces to avoid a search for both 'N APP' and 'NAPP' for example (but that could lead to false positives in some cases).
On top of that you can remove the check for %NAPPE% and %NAPPS% because you already include records containing %NAPP%
It should be faster when using:
pseudocode:
'MSN %'
or ('%SOFT%' and ('M SOFT' or '%MSOFT%' or '%MISOFT%'))
or ('%APP%' and ('%N APP%' or '%NAPP%' or '%NEK%APPLIANCE%'))
If SOFT or APP is not found there is no need to check for the others containing the same word - the and will avoid that if the first part is already false.
If this is just an example and those parameters are variable, you could write some code to optimize those search terms (unless the SQL server already does that).

Sequence restrictions of SELECT statement

In my table I have a column of type nVarchar or nText
Suppose value of a this col is like this '... xxx yyy zzz ...'
I use SELECT to search table with this column
SELECT * FROM tbl_name WHERE col_name like '%xxx yyy%'
Since the I can't force users to enter words with my sequence I want to give same result with this query too:
SELECT * FROM tbl_name WHERE col_name like '%yyy xxx%'
It looks to me like you might want to consider normalizing your table. Specifically, if "yyy xxx" is the same as "xxx yyy", you're storing an array (or list or whatever you want to call it) in one column, which breaks first normal form. Normalize it so that each of those atoms (i.e. "xxx", "yyy") are stored separately and are related back to your original record. Then it's almost trivial to do the kind of thing you're looking to do.
You have to tokenize the user input into separate words inside your application code (not in sql) and then construct queries like
SELECT * FROM tbl_name WHERE
(col_name like '%yyy%') AND (col_name like '%xxx%') AND ...
Your best bet is to use both criteria, and "OR".
SELECT *
FROM tbl_name
WHERE (col_name like '%xxx yyy%')
OR (col_name like '%yyy xxx%');

Use a LIKE statement on SQL Server XML Datatype

If you have a varchar field you can easily do SELECT * FROM TABLE WHERE ColumnA LIKE '%Test%' to see if that column contains a certain string.
How do you do that for XML Type?
I have the following which returns only rows that have a 'Text' node but I need to search within that node
select * from WebPageContent where data.exist('/PageContent/Text') = 1
Yet another option is to cast the XML as nvarchar, and then search for the given string as if the XML vas a nvarchar field.
SELECT *
FROM Table
WHERE CAST(Column as nvarchar(max)) LIKE '%TEST%'
I love this solution as it is clean, easy to remember, hard to mess up, and can be used as a part of a where clause.
This might not be the best performing solution, so think twice before deplying it to production. It is however very usefull for a quick debug session, which is where I mostly use it.
EDIT: As Cliff mentions it, you could use:
...nvarchar if there's characters that don't convert to varchar
You should be able to do this quite easily:
SELECT *
FROM WebPageContent
WHERE data.value('(/PageContent/Text)[1]', 'varchar(100)') LIKE 'XYZ%'
The .value method gives you the actual value, and you can define that to be returned as a VARCHAR(), which you can then check with a LIKE statement.
Mind you, this isn't going to be awfully fast. So if you have certain fields in your XML that you need to inspect a lot, you could:
create a stored function which gets the XML and returns the value you're looking for as a VARCHAR()
define a new computed field on your table which calls this function, and make it a PERSISTED column
With this, you'd basically "extract" a certain portion of the XML into a computed field, make it persisted, and then you can search very efficiently on it (heck: you can even INDEX that field!).
Marc
Another option is to search the XML as a string by converting it to a string and then using LIKE. However as a computed column can't be part of a WHERE clause you need to wrap it in another SELECT like this:
SELECT * FROM
(SELECT *, CONVERT(varchar(MAX), [COLUMNA]) as [XMLDataString] FROM TABLE) x
WHERE [XMLDataString] like '%Test%'
This is what I am going to use based on marc_s answer:
SELECT
SUBSTRING(DATA.value('(/PAGECONTENT/TEXT)[1]', 'VARCHAR(100)'),PATINDEX('%NORTH%',DATA.value('(/PAGECONTENT/TEXT)[1]', 'VARCHAR(100)')) - 20,999)
FROM WEBPAGECONTENT
WHERE COALESCE(PATINDEX('%NORTH%',DATA.value('(/PAGECONTENT/TEXT)[1]', 'VARCHAR(100)')),0) > 0
Return a substring on the search where the search criteria exists

MySQL question about "reverse LIKEs"

Well given I have a value I want to check for potential matches in a database (in one varchar field) so I write something like:
SELECT * FROM table WHERE column LIKE "%value%"
Which will work if the value is something like "test" and the column has a value of "this is a test" however if it is reversed then I will not get a match I have tried things along the lines of:
SELECT * FROM table WHERE CONCAT("%",column,"%") LIKE "value"
but don't know exactly how to phrase this to Google to get a response I need, please help!
You can reverse a like statement. Just using the same syntax as a regular like query:
select
*
from
table
where
'value' like concat('%', column, '%')
Of course, if you felt wild and crazy, you could also use instr:
select * from table where instr('value', column) > 0
I don't know which one is faster, since I don't have a MySQL instance to test against, but it's worth trying both to see which wins.
SELECT *
FROM table
WHERE 'value' LIKE CONCAT('%', column, '%')

How to implement a Keyword Search in MySQL?

I am new to SQL programming.
I have a table job where the fields are id, position, category, location, salary range, description, refno.
I want to implement a keyword search from the front end. The keyword can reside in any of the fields of the above table.
This is the query I have tried but it consist of so many duplicate rows:
SELECT
a.*,
b.catname
FROM
job a,
category b
WHERE
a.catid = b.catid AND
a.jobsalrange = '15001-20000' AND
a.jobloc = 'Berkshire' AND
a.jobpos LIKE '%sales%' OR
a.jobloc LIKE '%sales%' OR
a.jobsal LIKE '%sales%' OR
a.jobref LIKE '%sales%' OR
a.jobemail LIKE '%sales%' OR
a.jobsalrange LIKE '%sales%' OR
b.catname LIKE '%sales%'
For a single keyword on VARCHAR fields you can use LIKE:
SELECT id, category, location
FROM table
WHERE
(
category LIKE '%keyword%'
OR location LIKE '%keyword%'
)
For a description you're usually better adding a full text index and doing a Full-Text Search (MyISAM only):
SELECT id, description
FROM table
WHERE MATCH (description) AGAINST('keyword1 keyword2')
SELECT
*
FROM
yourtable
WHERE
id LIKE '%keyword%'
OR position LIKE '%keyword%'
OR category LIKE '%keyword%'
OR location LIKE '%keyword%'
OR description LIKE '%keyword%'
OR refno LIKE '%keyword%';
Ideally, have a keyword table containing the fields:
Keyword
Id
Count (possibly)
with an index on Keyword. Create an insert/update/delete trigger on the other table so that, when a row is changed, every keyword is extracted and put into (or replaced in) this table.
You'll also need a table of words to not count as keywords (if, and, so, but, ...).
In this way, you'll get the best speed for queries wanting to look for the keywords and you can implement (relatively easily) more complex queries such as "contains Java and RCA1802".
"LIKE" queries will work but they won't scale as well.
Personally, I wouldn't use the LIKE string comparison on the ID field or any other numeric field. It doesn't make sense for a search for ID# "216" to return 16216, 21651, 3216087, 5321668..., and so on and so forth; likewise with salary.
Also, if you want to use prepared statements to prevent SQL injections, you would use a query string like:
SELECT * FROM job WHERE `position` LIKE CONCAT('%', ? ,'%') OR ...
I will explain the method i usally prefer:
First of all you need to take into consideration that for this method you will sacrifice memory with the aim of gaining computation speed.
Second you need to have a the right to edit the table structure.
1) Add a field (i usually call it "digest") where you store all the data from the table.
The field will look like:
"n-n1-n2-n3-n4-n5-n6-n7-n8-n9" etc.. where n is a single word
I achieve this using a regular expression thar replaces " " with "-".
This field is the result of all the table data "digested" in one sigle string.
2) Use the LIKE statement %keyword% on the digest field:
SELECT * FROM table WHERE digest LIKE %keyword%
you can even build a qUery with a little loop so you can search for multiple keywords at the same time looking like:
SELECT * FROM table WHERE
digest LIKE %keyword1% AND
digest LIKE %keyword2% AND
digest LIKE %keyword3% ...
You can find another simpler option in a thread here: Match Against.. with a more detail help in 11.9.2. Boolean Full-Text Searches
This is just in case someone need a more compact option. This will require to create an Index FULLTEXT in the table, which can be accomplish easily.
Information on how to create Indexes (MySQL): MySQL FULLTEXT Indexing and Searching
In the FULLTEXT Index you can have more than one column listed, the result would be an SQL Statement with an index named search:
SELECT *,MATCH (`column`) AGAINST('+keyword1* +keyword2* +keyword3*') as relevance FROM `documents`USE INDEX(search) WHERE MATCH (`column`) AGAINST('+keyword1* +keyword2* +keyword3*' IN BOOLEAN MODE) ORDER BY relevance;
I tried with multiple columns, with no luck. Even though multiple columns are allowed in indexes, you still need an index for each column to use with Match/Against Statement.
Depending in your criterias you can use either options.
I know this is a bit late but what I did to our application is this. Hope this will help someone tho. But it works for me:
SELECT * FROM `landmarks` WHERE `landmark_name` OR `landmark_description` OR `landmark_address` LIKE '%keyword'
OR `landmark_name` OR `landmark_description` OR `landmark_address` LIKE 'keyword%'
OR `landmark_name` OR `landmark_description` OR `landmark_address` LIKE '%keyword%'