SQLite - Why wasn't this thrown as a syntax error? - sql

I just spent the last hour trying to figure out why my SQLite database wasnt updating.
The query being done was
UPDATE Table SET col1 = val AND col2 = val2 AND col3 = val3 WHERE col1 = val4
Obviously this is the wrong syntax for an update query, however no exceptions were thrown, no errors shown and when run directly using SQLiteBrowser it even said that the query executed correctly and had updated a row (yet no update was done)
Can anyone explain why this query wasn't rejected with an exception or syntax error?
Update:
So after looking at things further, I can see that an update was indeed performed, however instead of setting col1 to the new value, it was instead set to 0 which seems odd.

The AND operator in most languages, including SQLite, can be used in expressions (for boolean or bitwise operations, etc.), and the = operators is testing for equality, so you're just setting col1 equal to the value of the expression val AND col2 = val2 AND col3 = val3.
For example, if all the columns and values are 1 in your expression, you're setting col1 to:
1 AND 1 = 1 AND 1 = 1
...which is a valid expression that evaluates to 1.
SQLite will report as many rows as matched by the WHERE clause to have been affected by the update, even if the new value in col1 was the same as the old value, i.e. even if the data isn't actually changing. It still found rows to update and put a new value in there, so it reports having successfully performed an update.
We'd need to know the types/values of the columns to work out exactly what's going on, but I don't see a syntax error there, though it's clear you didn't mean to do what the expression (validly!) says!

Related

Oracle ERROR-01722 not showing up consistently

There seems to be inconsistencies with how ERROR-01722 error worked, for those who don't know the issue is due to an invalid number and to fix it you'll need to wrap the number to char.
But when filtering VARCHAR2 it is stated that Oracle will convert the data of the column being filtered based on the value given to it. (see: https://stackoverflow.com/a/10422418/5337433)
Now that this is explained for some reason, the error is inconsistent. As an example I have this query:
In this example filter1 is varchar2
select *
from table
where filter1 = 12345
and filter2 = ''
and filter3 = '';
When this statement run there were no issues, but when you run it like this:
select *
from table
where filter1 = 12345
and filter2 = '';
it errors out to ERROR-01722, im not sure why it is acting this way, and how to fix it.
When you compare a varchar column to a number, Oracle will try to convert the column's content to a number, not the other way round (because 123 could be stored as '0123' or '00123')
In general you should always use constant values that match the data type of the column you compare them with. So it should be:
where filter1 = '12345'
However if you are storing numbers in that column, you should not define it as varchar - it should be converted to a proper number column.
The reason the error doesn't show up "consistently" is that you seem to have some values that can be converted to a number and some can't. It depends on other conditions in the query if the those values are included or not.
Additionally: empty strings are converted to NULL in Oracle. So the condition filter2 = '' will never be true. You will have to use filter2 is null if you want to check for an "empty" column.

Oracle CASE missing right parenthesis for a "in" limit

I have a QRY im developing in Oracle for spotfire. In the where statement, I have a decision case statement and if its True, im trying to pass a list of items to match a column, below is what I have, but its throwing a missing right parenthesis error and I cannot determine why. In short, when a variable is determined True (in this case 9>8 for the example, I need it to result those items, else, result the entire column with no limits.
Note: This works fine when its only 1 item being passed, i.e. 'BOB' but as soon as its multiple, this error occurs.
and Column1 = (CASE When 9>8 Then ('BOB','TOM') Else Column1 END)
Case expressions are best avoided in the where clause. Instead, write the logic with AND and OR:
And (
(9>8 AND Column1 IN ('BOB','TOM'))
OR 9<=8 -- You say you check a variable here, don't forget to check for NULL
)
Oracle does not have a boolean type for use in SQL queries.
Instead, just use basic logic:
and ( (9 > 8 and Column1 in ('BOB','TOM')) or
9 <= 8
)

Comparing two empty Strings in Oracle SQL

Hi today I have met with weird situation. I had a where clause where was condition which returns String and I wanted to check if it's empty or not. And when it returns empty string Oracle still treat it like a different Strings. So I went further and prepared simple queries:
select 1 from dual where 1 = 1;
returns: 1
select 1 from dual where 'A' = 'A';
returns: 1
And now what I cannot understand:
select 1 from dual where '' = '';
No result.
Even if I check if they are different there is still no result.
select 1 from dual where '' != '';
No result.
Can someone explain it for me ?
Oracle treats empty strings as NULL. It's a gotcha. Make a note of it and hope it never bites you in the butt in production.
The reason is as #Captain Kenpachi explained. If want to compare two strings (or other types that are the same) and want to be tolerant of NULLs (or empty string in Oracle as it treats it as the same) then you need to involve an IS test.
You could try the common cheat of using a rogue value that will never be used but Murphy's Law dictates that one day someone will. This technique also has the drawback that the rogue value should match the type of the thing you are comparing i.e. comparing strings you need a rogue string while comparing dates you need a rouge date. This also means you can't cut-and-paste it liberally without applying a little thought. Example:
WHERE NVL(col1,'MyRougeValue')=NVL(col2,'MyRougeValue')
The standard version is to explicitly test for NULLs
WHERE (col1=col2 OR (col1 IS NULL AND col2 IS NULL))
The opposite becomes WHERE NOT(col1=col2 OR (col1 IS NULL AND col2 IS NULL))
I have seen the a long winded opposite version (as seen in Toad's data compare tool)
WHERE (col1<>col2 OR (col1 IS NULL AND col2 IS NOT NULL) OR (col1 IS NOT NULL AND col2 IS NULL))
Oracle does have a handy DECODE function that is basically is IF a IS b THEN c ELSE d so equality is WHERE DECODE(col1,col2,1,0)=1 and the opposite is WHERE DECODE(col1,col2,1,0)=0. You may find this a little slower than the explicit IS test. It is proprietary to Oracle but helps make up for the empty string problem.

How to use conditional different columns in WHERE clause?

I want to use completely different conditions in a WHERE-clause in an SQL Server 2014 query, not just change the parameter.
I have the following clause
WHERE (dbo.SecurityCheckUserInADGroups.CWID = #cwid2) AND (dbo.SecurityCheckUserInDatabaseRoles.Server = #server)
If the variable #cwid2 is NULL or empty I'd like to replace the whole first part of the AND-Statement with
dbo.SecurityCheckLDAPGroupName.DatabaseUserName = #role2
So here not only the parameter but also the column changes.
I tried If, Case and Iif with something like this, but it isn't accepted:
(IIF (LEN(#cwid1) > '0, 'dbo.SecurityCheckUserInADGroups.CWID = #cwid2','dbo.SecurityCheckLDAPGroupName.DatabaseUserName = #role2'))
It keeps telling me "An expression of non-boolean type specified in a context where a condition is expected"
Any ideas how I can solve this?
Just use basic logic:
WHERE ((#cwid <> '' AND dbo.SecurityCheckUserInADGroups.CWID = #cwid2) OR
(COALESCE(#cwid, '') = '' AND dbo.SecurityCheckLDAPGroupName.DatabaseUserName = #role2)
) AND
(dbo.SecurityCheckUserInDatabaseRoles.Server = #server)
The most general solution would be
Where (#var is null and col1=val1)
Or (#var is not null and col2=val2)
Because query optimizers can sometimes choke on OR, I tend to look for other solutions when performance could be an issue (large data sets, etc.) but the above might be worth trying first.
If you do need something more specific, the trick to iif logic is that it returns a value to compare, not a predicate to evaluate. You could do something like
Where col1 = iif(#var is null, val1, col1)
And col2 = iif(var is null, col2, val2)
The idea here is that your "don't care" cases - like the value of col2 when var is null - resolve as "always true". But there are cases (like when col1 or col2 could be NULL) where this specific solution Will fail.
Which is why the first approach is the most general
WHERE CASE WHEN ISNULL(#cwid2,'') <> '' THEN
dbo.SecurityCheckUserInADGroups.CWID = #cwid2 ELSE
dbo.SecurityCheckLDAPGroupName.DatabaseUserName = #role2
END
AND dbo.SecurityCheckUserInDatabaseRoles.Server = #server

SQL Select to keep out fields that are NULL

I am trying to connect a Filemaker DB to Firebird SQL DB in both ways import to FM and export back to Firebird DB.
So far it works using the MBS Plug-in but FM 13 Pro canot handle NULL.
That means that for example Timestamp fields that are empty (NULL) produce a "0" value.
Thats means in Time something like 01.01.1889 00:00:00.
So my idea was to simply ignore fields containing NULL.
But here my poor knowlege stops.
First I thought I can do this with WHERE, but this is ignoring whole records sets:
SELECT * FROM TABLE WHERE FIELD IS NOT NULL
Also I tried to filter it later on like this:
If (IsEmpty (MBS("SQL.GetFieldAsDateTime"; $command; "FIELD") ) = 0 ; MBS("SQL.GetFieldAsDateTime"; $command; "FIELD"))
With no result either.
This is a direct answer to halfbit's suggestion, which is correct but not for this SQL dialect. In a query to provide a replacement value when a field is NULL you need to use COALESCE(x,y). Where if X is null, Y will be used, and if Y is null then the field is NULL. Thats why it is common for me to use it like COALESCE(table.field,'') such that a constant is always outputted if table.field happens to be NULL.
select COALESCE(null,'Hello') as stackoverflow from rdb$database
You can use COALESCE() for more than two arguments, I just used two for conciseness.
I dont know the special SQL dialect, but
SELECT field1, field2, value(field, 0), ...FROM TABLE
should help you:
value gives the first argument, ie, your field if it is NOT NULL or the second argument if it is.