SQL NOT LIKE - Not working - sql

UPDATE 2:
In one of the rows, in the column closed_by, it contains a null. If I replace the null with text, the query starts working, but it will not work with null. So it seems null is the problem, but the query should return rows which have null too, as pqr does not equal null.
UPDATE 1:
I have also tried set #user = 'pqr', but it makes no difference. It still returns 0 rows, when it should be returning 1 row, as 1 of the rows does not contain pqr.
ORIGINAL QUESTION:
I am trying to return rows which do not contain the id provided:
declare #user varchar(3)
set #user = 'pqr'
select * from table1 where not( closed_by like #user )
closed_by contains data like
abc,def,ghi,jkl,mno
But this gives me no errors and returns no data, it should be returning a single row as pqr is not in 1 row.
Not sure what I am doing wrong.

You may want to check the syntax of the LIKE operator - it accetps a pattern and so you would need to use something like this instead:
declare #user varchar(5)
set #user = '%pqr%'
The '%' is a wildcard and matches any string of zeor or more chracters.
FYI - SQL Server won't be able to use indexes with a a LIKE pattern that starts with a wildcard and so you may find that your query performs badly with large data sets.

NULL is unknown and therefore it is unknown whether it is like your pattern or not. You can solve this easily by using:
DECLARE #user VARCHAR(5) = '%pqr%';
SELECT ... WHERE COALESCE(closed_by, '') NOT LIKE #user;
And in fact to be more accurate you probably want:
DECLARE #user VARCHAR(7) = '%,pqr,%';
SELECT ... WHERE COALESCE(',' + closed_by + ',', '') NOT LIKE #user;
This way, if you later have data like 'trv,pqrs,ltt' it won't return a false positive.
However, having said all of that, having you considered storing this in a more normalized fashion? Storing comma-separated lists can be very problematic and I can assue you this won't be the last challenge you face with dealing with data structured this way.

You need to include a wildcard character:
declare
#user varchar(5)
set #user = '%pqr%'
select *
from table1
where isnull(closed_by, '') not like #user

You need to use the % and _ wildcard characters when using LIKE. Without them you actually just have WHERE NOT (closed_by = #user).
Also, be careful of accidental matches. For example LIKE '%a%' would match your example record. For such cases, I tend to ensure that the comma delimitered lists also have commas at the start and end. Such as; ',abc,def,ghi,jkl,mno,' LIKE '%,ghi,%'
But, more over, you're using a relational database. You would be better off with each entry as it's own record in a normalised structure. Although this give 1:many relationships, rather than 1:1 relationships, you get the benefits of INDEXes and much more flexibility in your queries. (Your LIKE example can't use an index.)
REPLY TO UPDATE 2:
Be careful of how you assume NULL logic works.
The result of NULL LIKE '%pqr%' is NULL
The result of NOT (NULL) is NULL
You need to change your code to use WHERE NOT (ISNULL(closed_by, '') LIKE '%pqr%')

Try this:
select * from table1 where closed_by not like #user
And you may need to add the appropriate '%' characters to tell SQL Server which portion of the value to search. For example 'pqr%'

It sounds to me that you really are looking for equivalence, and not wildcard matches. Try this:
select * from table1 where closed_by <> #user

Related

Query to output results using "like"

I might be over-analyzing this but I have 13,000 records stored in a temp table that only has one column.
I'm trying to determine if those records exist in another database/table but there's no key between the two other than the one column.
The query I run has to use LIKE so something like this...
declare #string Varchar(25) = (select top 1 * from accts)
select content from db2..[mc3] where content like '%'#string+'%'
But I have check to see which one's are in there but I don't want to do it manually one at a time.
Is there a way to have it programmatically go through all of my accounts to see which one's are in that database/table?
This may take a while, but you can get the matching ones using:
select a.??
from accts a
where exists (select 1
from db2..mc3
where mc3.content like '%' + a.?? +'%'
);
This gets accounts that are in mc3 according to your rule.
I should note: performance will be pretty bad. Better than a cursor but that's not saying much.

SQL WHERE is anything

I'm working on a database query via a search bar and would like it to sometimes yield all results (depending on what is inputted)
I know that for SELECT you can use * in order to select all columns. Is there similar SQL syntax: i.e. WHERE name IS * to essentially always be true?
Edit to clarify:
The nature of the clause is that a variable is used to set the name (I'm actually not able to change the clause, that was made clear). i.e. WHERE name IS [[inputName]] (inputName is the decided by the search bar)
WHERE ISNULL(name, '') = ISNULL(name, '')
(assuming that 'name' is of a string type)
Just make the column reference itself. However, if this is the only goal of your query, why are you against omitting the WHERE clause?
If you want to return all results in a SQL statement, you can simply omit the WHERE clause:
SELECT <* or field names> FROM <table>;
You should use WHERE only when you want to filter your data on a certain field. In your case you just don't want to filter at all.
Actually you don't need WHERE clause at all in this situation. But if you insist then you should write your predicate so it always returns true. This can be done many ways:
Any predicate like:
WHERE 1=1
With column:
WHERE name = name OR name is null
With LIKE:
WHERE name LIKE '%' OR name is null
With passed parameter:
WHERE name = #name OR #name is null
You can think of more of course. But I think you need the last one. Pass NULL from app layer if you want all rows.

Selecting everything in a table... with a where statement

I have an interesting situation where I'm trying to select everything in a sql server table but I only have to access the table through an old company API instead of SQL. This API asks for a table name, a field name, and a value. It then plugs it in rather straightforward in this way:
select * from [TABLE_NAME_VAR] where [FIELD_NAME_VAR] = 'VALUE_VAR';
I'm not able to change the = sign to != or anything else, only those vars. I know this sounds awful, but I cannot change the API without going through a lot of hoops and it's all I have to work with.
There are multiple columns in this table that are all numbers, all strings, and set to not null. Is there a value I can pass this API function that would return everything in the table? Perhaps a constant or special value that means it's a number, it's not a number, it's a string, *, it's not null, etc? Any ideas?
No this isn't possible if the API is constructed correctly.
If this is some home grown thing it may not be, however. You could try entering YourTable]-- as the value for TABLE_NAME_VAR such that when plugged into the query it ends up as
select * from [YourTable]--] where [FIELD_NAME_VAR] = 'VALUE_VAR';
If the ] is either rejected or properly escaped (by doubling it up) this won't work however.
You might try to pass this VALUE_VAR
1'' or ''''=''
If it's used as-is and executed as Dynamic SQL it should result in
SELECT * FROM tab WHERE fieldname = '1' or ''=''
here is a simple example,
hope it might help
declare #a varchar(max)
set #a=' ''1'' or 1=1 '
declare #b varchar(max)
set #b=('select * from [TABLE_NAME_VAR] where [FIELD_NAME_VAR]='+#a)
exec(#b)
If your API allows column name instead of constant,
select * from [TABLE_NAME_VAR] where [FIELD_NAME_VAR] = [FIELD_NAME_VAR] ;

Matching sub string in a column

First I apologize for the poor formatting here.
Second I should say up front that changing the table schema is not an option.
So I have a table defined as follows:
Pin varchar
OfferCode varchar
Pin will contain data such as:
abc,
abc123
OfferCode will contain data such as:
123
123~124~125
I need a query to check for a count of a Pin/OfferCode combination and when I say OfferCode, I mean an individual item delimited by the tilde.
For example if there is one row that looks like abc, 123 and another that looks like abc,123~124, and I search for a count of Pin=abc,OfferCode=123 I wand to get a count = 2.
Obviously I can do a similar query to this:
SELECT count(1) from MyTable (nolock) where OfferCode like '%' + #OfferCode + '%' and Pin = #Pin
using like here is very expensive and I'm hoping there may be a more efficient way.
I'm also looking into using a split string solution. I have a Table-valued function SplitString(string,delim) that will return table OutParam, but I'm not quite sure how to apply this to a table column vs a string. Would this even be worth wile pursuing? It seems like it would be much more expensive, but I'm unable to get a working solution to compare to the like solution.
Your like/% solution is open to a bug if you had offer codes other than 3 digits (if there was offer code 123 and 1234, searching for like '%123%' would return both, which is wrong). You can use your string function this way:
SELECT Pin, count(1)
FROM MyTable (nolock)
CROSS APPLY SplitString(OfferCode,'~') OutParam
WHERE OutParam.Value = #OfferCode and Pin = #Pin
GROUP BY Pin
If you have a relatively small table you can probably get away with this. If you are working with a large number of rows or encountering performance problems, it would be more effective to normalize it as RedFilter suggested.
using like here is very expensive and I'm hoping there may be a more efficient way
The efficient way is to normalize the schema and put each OfferCode in its own row.
Then your query is more like (although you may need to use an intersection table depending on your schema):
select count(*)
from MyTable
where OfferCode = #OfferCode
and Pin = #Pin
Here is one way to use like for this problem, which is standard for getting exact matches when searching delimited strings while avoiding the '%123%' matches '123' and '1234' problem:
-- Create some test data
declare #table table (
Pin varchar(10) not null
, OfferCode varchar(100) not null
)
insert into #table select 'abc', '123'
insert into #table select 'abc', '123~124'
-- Mock some proc params
declare #Pin varchar(10) = 'abc'
declare #OfferCode varchar(10) = '123'
-- Run the actual query
select count(*) as Matches
from #table
where Pin = #Pin
-- Append delimiters to find exact matches
and '~' + OfferCode + '~' like '%~' + #OfferCode + '~%'
As you can see, we're adding the delimiters to the searched string, and also the search string in order to find matches, thus avoiding the bugs mentioned by other answers.
I highly doubt that a string splitting function will yield better performance over like, but it may be worth a test or two using some of the more recently suggested methods. If you still have unacceptable performance, you have a few options:
Updated:
Try an index on OfferCode (or on a computed persisted column of '~' + OfferCode + '~'). Contrary to the myth that SQL Server won't use an index with like and wildcards, this might actually help.
Check out full text search.
Create a normalized version of this table using a string splitter. Use this table to run your counts. Update this table according to some schedule or event (trigger, etc.).
If you have some standard search terms, pre-calculate the counts for these and store them on some regular basis.
Actually, the LIKE condition is going to have much less cost than doing any sort of string manipulation and comparison.
http://www.simple-talk.com/sql/performance/the-seven-sins-against-tsql-performance/

Sql Server Contains

I need to match on a partial string but can't turn full-text indexing on so can't use contains. I've looked at Levenstein's function for determining the distance between two strings but I'm not looking for fuzzy matching but that every character in the column exists in the string.
I.e. If the string being passed is something like AB_SYS_20120430.TXT I want to match on any columns containing AB_SYS. The like predicate isn't getting me there. I really need the equivalent of the .NET contains feature but as mentioned turning on full text indexing isn't an option to be turned on. Thought I would see if there were any other possible work arounds.
Are you looking for the LIKE function?
http://www.w3schools.com/sql/sql_like.asp
... WHERE MyColumn LIKE '%AB_SYS%'
That may not be optimal, but it seems like it answers your question... If you can search from only the left or right side that could further optimize.
That is functionally similar to String.Contains
http://msdn.microsoft.com/en-us/library/dy85x1sa.aspx
EDIT: How will you parse the input text into the "relevant" substring?
EDIT: To search the same LIKE condition but reverse, from your partial column to the complete literal, simply append the wildcard characters:
... WHERE 'AB_SYS_20120430.TXT' LIKE '%' + MyColumn + '%'
EDIT: You have suggested that you can't get it to work. If you add the schema do your question then I can help you further but consider this:
You have a table called MyTable
In that table there is a column called MyColumn
Some rows in that table have the data 'AB_SYS' in MyColumn
Given the parameter 'AB_SYS_20120430.TXT' you want to return all matching rows
CREATE PROCEDURE MyTestProcedure
#pFullNameString nvarchar(4000) = '' -- parameter passed in, like AB_SYS_20120430.TXT
AS
BEGIN
SELECT
*
FROM
MyTable
WHERE
#pFullNameString LIKE '%' + MyTable.[MyColumn] + '%'
END
GO
You could use CHARINDEX
WHERE CHARINDEX(StringToCheckFor, StringToCheckIn) > 0