I have a text column and the data in the text columns are as below:
Rob goes to school,get punished
Rob goes to school
Rob does not goes to school,get punished
When trying to write a query using case statement like
CASE
WHEN (PATINDEX('%Rob goes to school%',value) > 0) OR
(PATINDEX('%Rob is ill%',value) > 0 ) AND
(PATINDEX(%get punished%',value) > 0) THEN
'DONE'
It should select only the 1st statement but instead it is picking both the 1st and 2nd statement with 'DONE'. Any suggestion how to do a pattern match in this case?
I am using SQL Sever 2005/2008
Operator precedence and not enough parenthesis probably
You have x OR y AND z which is actually x OR (y AND z). Do you want want (x OR y) AND z?
The 2nd statement give true OR (false AND false) which gives true
You want (true OR false) AND false to give false
So the SQL should be
CASE WHEN
(
PATINDEX('%Rob goes to school%', value) > 0
OR
PATINDEX('%Rob is ill%', value) > 0
)
AND
(PATINDEX(%get punished%', value) > 0) THEN 'DONE'
...
PATINDEX does not treat your strings as delimited lists (comma-separated values) -- it searches for a match against the entire string.
Rob goes to school,get punished
Rob goes to school
PATINDEX('%Rob goes to school%',value) > 0 evaluates to true for both of them because the wildcard % matches any string of 0 or more characters. Your second and third patterns never get evaluated.
If you want to test which pattern is returning true, try this:
CASE
WHEN (PATINDEX('%Rob goes to school%',value) > 0) THEN 'Pattern 1'
WHEN (PATINDEX('%Rob is ill%',value) > 0 ) THEN 'Pattern 2'
WHEN (PATINDEX('%get punished%',value) > 0) THEN 'Pattern 3'
ELSE 'No Match Found' END
If you want a pattern to match the first value, but not the second, then look for (PATINDEX('%Rob goes to school,%',value) > 0) with the comma instead.
Otherwise -- if you're wanting to treat the strings like comma-separated values, PATINDEX is not your best tool for that. Other options might include converting your strings to tables via table-value function, or what have you.
Related
I have certain values that are needed for validation in Forms, either they look like Value X >= 0 but it could also be X <= 0, it depends on what operator should be used. How can I store such a value?
(I use MS SQL Server + Access as Frontend)
I basicly wanna store the Value and if it needs to be bigger than or smaller than.
Store the value, as usual, in a field, and the operator in another field as Short Text.
The you can use Eval:
Result = Eval("" & [ValueField] & [OperatorField] & "0")
You can store your value as it is, and to check if this value is positive or not you have two ways
First one
Create a computed column to check the Value column as
CREATE TABLE YourTable(
YourValue INT,
IsPositive AS CASE WHEN YourValue < 0 THEN 0 ELSE 1 END
);
INSERT INTO YourTable (YourValue) VALUES
(1), (-1);
SELECT *
FROM YourTable;
Second one
Use a CASE expression (or even you can create a view) as
SELECT CASE WHEN YourValue < 0 THEN 0 ELSE 1 END IsPositive,
--...
FROM YourTable;
I need to evaluate a field with a CASE statement. The field name is Commodity and is a varchar(255). The values differ and I need to extract a specific portion from it after a specific character. The character is a '>'. I was able to come up with the value I want returned after the > by using the following code:
SUBSTRING(Commodity, CHARINDEX('>', Commodity) + 2, LEN(Commodity))
I am however unsure of how to work this into my CASE statement. I need to test for Is Null and then just assign it a value of 'No Commodity'. Then I need to test for the presence of a > and then implement the code above to return the value. Then I need to test for when there is no > but it is not null and just return the value of the Commodity field.
You just need to have these three conditions in when clauses. You can use charindex to make sure the > character exists in the string:
CASE
WHEN commodity IS NULL THEN 'No Comodity'
WHEN CHARINDEX('>', Commodity) > 0 THEN
SUBSTRING(commodity, CHARINDEX('>', commodity) + 2, LEN(commodity))
ELSE comodity
END
I am attempting to search a column that contains alphanumeric ids in it but want to write a query that returns records with letters and numbers but not one or the other.
i.e Acceptable: jjk44kndkfndFF
i.e Not acceptable: 223232323232 or aajnfdskDFdd
So far I have:
where PATINDEX('%[^a-zA-Z0-9 ]%',columnInQuestion)
This returns all alphanumeric records. Any direction appreciated
I think you need three predicates in the WHERE clause:
WHERE (columnInQuestion NOT LIKE '%[^a-zA-Z0-9]%') AND
(PATINDEX('%[a-zA-Z]%', columnInQuestion) <> 0) AND
(PATINDEX('%[0-9]%', columnInQuestion) <> 0)
First predicate (columnInQuestion NOT LIKE '%[^a-zA-Z0-9]%') is true if columnInQuestion contains only alphanumeric characters
Second predicate (PATINDEX('%[a-zA-Z]%', columnInQuestion) <> 0) is true if there is at least one alphabetic character in columnInQuestion
Third predicate (PATINDEX('%[0-9]%', columnInQuestion) <> 0) is true if there is at least one numeric character in columnInQuestion
It can be done with just one regexp:
^[a-zA-Z0-9]*([a-zA-Z][0-9]|[0-9][a-zA-Z])[a-zA-Z0-9]*$
It starts and ends with 0-x legal chars.
And somewhere there is a switch from a letter to a digit or from a digit to a letter.
I have a general question for when you are using a CASE statement in SQL (Server 2008), and more than one of your WHEN conditions are true but the resulting flag is to be different.
This is hypothetical example but may be transferable when applying checks across multiple columns to classify data in rows. The output of the code below is dependant on how the cases are ordered, as both are true.
DECLARE #TESTSTRING varchar(5)
SET #TESTSTRING = 'hello'
SELECT CASE
WHEN #TESTSTRING = 'hello' THEN '0'
WHEN #TESTSTRING <> 'hi' THEN '1'
ELSE 'N/A'
END AS [Output]
In general, would it be considered bad practice to create flags in this way? Would a WHERE, OR statement be better?
Case statements are guaranteed to be evaluated in the order they are written. The first matching value is used. So, for your example, the value 0 would be returned.
This is clearly described in the documentation:
Searched CASE expression:
Evaluates, in the order specified, Boolean_expression for each WHEN clause.
Returns result_expression of the first Boolean_expression that evaluates to TRUE.
If no Boolean_expression evaluates to TRUE, the Database Engine returns the else_result_expression if an ELSE clause is specified, or
a NULL value if no ELSE clause is specified.
As for whether this is good or bad practice, I would lean on the side of neutrality. This is ANSI behavior so you can depend on it, and in some cases it is quite useful:
select (case when val < 10 then 'Less than 10'
when val < 100 then 'Between 10 and 100'
when val < 1000 then 'Between 100 and 1000'
else 'More than 1000' -- or NULL
end) as MyGroup
To conclude further - SQL will stop reading the rest of the of the case/when statement when one of the WHEN clauses is TRUE. Example:
SELECT
CASE
WHEN 3 = 3 THEN 3
WHEN 4 = 4 THEN 4
ELSE NULL
END AS test
This statement returns 3 since this is the first WHEN clause to return a TRUE, even though the following statement is also a TRUE.
IF #insertedValue IS NOT NULL AND #insertedValue > 0
This logic is in a trigger.
The value comes from a deleted or inserted row (doesn't matter).
2 questions :
Do I need to check both conditions? (I want all value > 0, value in db can be nullable)
Does SQL Server check the expression in the order I wrote it ?
1) Actually, no, since if the #insertedValue is NULL, the expression #insertedValue > 0 will evaulate to false. (Actually, as Martin Smith points out in his comment, it will evaluate to a special value "unknown", which when forced to a Boolean result on its own collapses to false - examples: unknown AND true = unknown which is forced to false, unknown OR true = true.) But you're relying on comparison behaviour with NULL values. A single step equivalent method, BTW, would be:
IF ISNULL(#insertedValue, 0) > 0
IMHO, you're better sticking with the explicit NULL check for clarity if nothing else.
2) Since the query will be optimised before execution, there is absolutely no guarantee of order of execution or short circuiting of the AND operator.
Combining the two - if the double check is truly unnecessary, then it will probably be optimised out before execution anyway, but your SQL code will be more maintainable in my view if you make this explicit.
You can use COALESCE => Returns the first nonnull expression among its arguments.
Now you can make the query more flexible, by increasing the column limits and again you need to check the Greater Then Zero condition. Important point to note down here is you have the option to check values in multiple columns.
declare #val int
set #val = COALESCE( null, 1, 10 )
if(#val>0)
select 'fine'
else
select 'not fine'