How to set Sqlite3 to be case insensitive when string comparing? - sqlite

I want to select records from sqlite3 database by string matching. But if I use '=' in the where clause, I found that sqlite3 is case sensitive. Can anyone tell me how to use string comparing case-insensitive?

You can use COLLATE NOCASE in your SELECT query:
SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE
Additionaly, in SQLite, you can indicate that a column should be case insensitive when you create the table by specifying collate nocase in the column definition (the other options are binary (the default) and rtrim; see here). You can specify collate nocase when you create an index as well. For example:
create table Test
(
Text_Value text collate nocase
);
insert into Test values ('A');
insert into Test values ('b');
insert into Test values ('C');
create index Test_Text_Value_Index
on Test (Text_Value collate nocase);
Expressions involving Test.Text_Value should now be case insensitive. For example:
sqlite> select Text_Value from Test where Text_Value = 'B';
Text_Value
----------------
b
sqlite> select Text_Value from Test order by Text_Value;
Text_Value
----------------
A
b
C
sqlite> select Text_Value from Test order by Text_Value desc;
Text_Value
----------------
C
b
A
The optimiser can also potentially make use of the index for case-insensitive searching and matching on the column. You can check this using the explain SQL command, e.g.:
sqlite> explain select Text_Value from Test where Text_Value = 'b';
addr opcode p1 p2 p3
---------------- -------------- ---------- ---------- ---------------------------------
0 Goto 0 16
1 Integer 0 0
2 OpenRead 1 3 keyinfo(1,NOCASE)
3 SetNumColumns 1 2
4 String8 0 0 b
5 IsNull -1 14
6 MakeRecord 1 0 a
7 MemStore 0 0
8 MoveGe 1 14
9 MemLoad 0 0
10 IdxGE 1 14 +
11 Column 1 0
12 Callback 1 0
13 Next 1 9
14 Close 1 0
15 Halt 0 0
16 Transaction 0 0
17 VerifyCookie 0 4
18 Goto 0 1
19 Noop 0 0

SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE

You can do it like this:
SELECT * FROM ... WHERE name LIKE 'someone'
(It's not the solution, but in some cases is very convenient)
"The LIKE operator does a pattern
matching comparison. The operand to
the right contains the pattern, the
left hand operand contains the string
to match against the pattern. A
percent symbol ("%") in the pattern
matches any sequence of zero or more
characters in the string. An
underscore ("_") in the pattern
matches any single character in the
string. Any other character matches
itself or its lower/upper case
equivalent (i.e. case-insensitive
matching). (A bug: SQLite only
understands upper/lower case for ASCII
characters. The LIKE operator is case
sensitive for unicode characters that
are beyond the ASCII range. For
example, the expression 'a' LIKE 'A'
is TRUE but 'æ' LIKE 'Æ' is FALSE.)."

This is not specific to sqlite but you can just do
SELECT * FROM ... WHERE UPPER(name) = UPPER('someone')

Another option is to create your own custom collation. You can then set that collation on the column or add it to your select clauses. It will be used for ordering and comparisons.
This can be used to make 'VOILA' LIKE 'voilà'.
http://www.sqlite.org/capi3ref.html#sqlite3_create_collation
The collating function must return an integer that is negative, zero, or positive if the first string is less than, equal to, or greater than the second, respectively.

Another option that may or may not make sense in your case, is to actually have a separate column with pre-lowerscored values of your existing column. This can be populated using the SQLite function LOWER(), and you can then perform matching on this column instead.
Obviously, it adds redundancy and a potential for inconsistency, but if your data is static it might be a suitable option.

Its working for me Perfectly.
SELECT NAME FROM TABLE_NAME WHERE NAME = 'test Name' COLLATE NOCASE

If the column is of type char then you need to append the value you are querying with spaces, please refer to this question here . This in addition to using COLLATE NOCASE or one of the other solutions (upper(), etc).

use like this
"select * from $pwsXDataHistory where type = '$type' COLLATE NOCASE and $t_uStatus != '$DELETE' order by $t_name COLLATE NOCASE asc ");

Simply, you can use COLLATE NOCASE in your SELECT query:
SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE

you can use the like query for comparing the respective string with table vales.
select column name from table_name where column name like 'respective comparing value';

Related

Teradata SQL - Convert values separated by ; in a single column to multiple rows

I have a single column with comma separated values like below.
sel job_dependency from test_table;
job_dependency
1;2;3;4;5;6
I need to convert it into below format in Teradata SQL where each number is a row.
job_dependency
1
2
3
4
5
6
Any help would be really helpful.
There's a table function for this task:
WITH cte AS
(
SELECT
1 AS inKey -- might be a column, either INT or VarChar up to 60 bytes
-- Other data types should be CASTed to VarChar (and back in the Select)
,job_dependency2 AS inString
FROM test_table
)
SELECT *
FROM TABLE
( StrTok_Split_To_Table(cte.inKey, cte.inString, ';')
RETURNS (outKey INTEGER, -- data type must match input column
tokenNum INTEGER,
token VARCHAR(20))
) AS dt

sqlite advanced case sensitive query

i search for a special kind of query in SQLite
to sort a notes table.
The result from the query should be like this:
id oid
1 1
2 1,1
5 1,1,a
6 1,1,a,1
3 1,1,A
4 1,1,A,1
But with the folling code I receive this:
CREATE TABLE note (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created DATETIME DEFAULT CURRENT_TIMESTAMP,
oid VARCHAR unique,
tit VARCHAR,
dsc VARCHAR
);
select id, oid from note
order by oid collate NOCASE
Result:
id oid
1 1
2 1,1
5 1,1,a
3 1,1,A
6 1,1,a,1
4 1,1,A,1
Any suggestions?
Thanks
--jonah
The following transforms the sort keys so that the normal case sensitive ordering yields the requested result:
If there was a togglecase() function, the function would uppercase lowercase and lowercase uppercase (for example Hello => hELLO), one could ORDER BY togglecase(oid) and the result would be in the order requested.
You could define such a function and expose it to SQLite as a UDF. It could also be possible to write this function using builtin SQLite functions but I don't know them well enough to give an answer using them. The following is an example of such a function in Python:
def togglecase(s):
def toggle(l):
if l.isupper():
return l.lower()
if l.islower():
return l.upper()
return l
return ''.join(toggle(l)
for l in s)
Note that for proper Unicode support it needs to iterate over graphemes. Not over code points.
See that this does what I described it to do:
>>> togglecase("1,1,A")
'1,1,a'
>>> togglecase("1,1,a")
'1,1,A'
It is possible to test if this sorts correctly in Python:
>>> sorted(["1", "1,1", "1,1,a", "1,1,a,1", "1,1,A", "1,1,A,1"])
['1', '1,1', '1,1,A', '1,1,A,1', '1,1,a', '1,1,a,1']
See how the uppercase follows the lowercase:
>> sorted(["1", "1,1", "1,1,a", "1,1,a,1", "1,1,A", "1,1,A,1"], key=togglecase)
['1', '1,1', '1,1,a', '1,1,a,1', '1,1,A', '1,1,A,1']
Now if you use it in SQLite like:
SELECT id, oid
FROM note
ORDER BY togglecase(oid)
This should result in:
1 "1"
2 "1,1"
3 "1,1,a"
4 "1,1,a,1"
5 "1,1,A"
6 "1,1,A,1"
The code is untested except for the togglecase function.
You are getting that result because the sorting is pecified to be NOCASE. That means that "a" and "A" are equals. So, first the rows with "a/A" and nothing after, and then rows with "a/A" and data after.
If you make the query CASE SENSITIVE, you will get a different result. BUT "A" comes befores "a" in case sensitive sort:
SELECT id, oid
FROM note
ORDER by oid
Results:
1 "1"
2 "1,1"
5 "1,1,A"
6 "1,1,A,1"
3 "1,1,a"
4 "1,1,a,1"

Oracle PL SQL - Sort column value beginning with '_" then followed by alphabets

Using Oracle SQL, I want to sort the below data with values beginning with "_" followed by alphabet (without using COLLATE)
AE
BASMI1_02
CBPBC_01
_TYPERR
AE1_01
AE1_03
AEPS
AEYN
ASAS1TABLE
ASAS1_01
CBPBC_01B
CM
as
_TYPERR
AE
AE1_01
AE1_03
AEPS
AEYN
ASAS1_01
ASAS1TABLE
BASMI1_02
CBPBC_01
CBPBC_01B
CM
Try this:
select *
from table
order by case
when col like '\_%' escape '\'
then 0
else 1
end,
col;
It defines a custom sort order using case Statement
If you want the underscore values to be sorted as well then do a union separating the two, simply put:
SELECT * FROM table
WHERE col LIKE '\_%'
ORDER BY col
UNION
SELECT * FROM table
WHERE col NOT LIKE '\_%'
ORDER BY col;

REGEXP_SUBSTR to return first and last segment

I have a dataset which may store an account number in several different variations. It may contain hyphens or spaces as segment separators, or it may be fully concatenated. My desired output is the first three and last 5 alphanumeric characters. I'm having problems with joining the two segments "FIRST_THREE_AND_LAST_FIVE:
with testdata as (select '1-23-456-78-90-ABCDE' txt from dual union all
select '1 23 456 78 90 ABCDE' txt from dual union all
select '1234567890ABCDE' txt from dual union all
select '123ABCDE' txt from dual union all
select '12DE' txt from dual)
select TXT
,regexp_replace(txt, '[^[[:alnum:]]]*',null) NO_HYPHENS_OR_SPACES
,regexp_substr(regexp_replace(txt, '[^[[:alnum:]]]*',null), '([[:alnum:]]){3}',1,1) FIRST_THREE
,regexp_substr(txt, '([[:alnum:]]){5}$',1,1) LAST_FIVE
,regexp_substr(regexp_replace(txt, '[^[[:alnum:]]]*',null), '([[:alnum:]]){3}',1,1) FIRST_THREE_AND_LAST_FIVE
from testdata;
My desired output would be:
FIRST_THREE_AND_LAST_FIVE
-------------------------
123ABCDE
123ABCDE
123ABCDE
123ABCDE
(null)
Here's my try. Note that when regexp_replace() does not find a match, the original string is returned, that's why you can't get a null directly. My thought was to see if the result string matched the original string but of course that would not work for line 4 where the result is correct and happens to match the original string. Others have mentioned methods for counting length, etc with a CASE but I would get more strict and check for the first 3 being numeric and the last 5 being alpha as well since just checking for 8 characters being returned doesn't guarantee they are the right 8 characters! I'll leave that up to the reader.
Anyway this looks for a digit followed by an optional dash or space (per the specs) and remembers the digit (3 times) then also remembers the last 5 alpha characters. It then returns the remembered groups in that order.
I highly recommend you make this a function where you pass your string in and get a cleaned string in return as it will be much easier to maintain, encapsulate this code for re-usability and allow for better error checking using PL/SQL code.
SQL> with testdata(txt) as (
2 select '1-23-456-78-90-ABCDE' from dual
3 union
4 select '1 23 456 78 90 ABCDE' from dual
5 union
6 select '1234567890ABCDE' from dual
7 union
8 select '123ABCDE' from dual
9 union
10 select '12DE' from dual
11 )
12 select
13 case when length(regexp_replace(upper(txt), '^(\d)[- ]?(\d)[- ]?(\d)[- ]?.*([A-Z]{5})$', '\1\2\3\4')) < 8
14 -- Needs more robust error checking here
15 THEN 'NULL' -- for readability
16 else regexp_replace(upper(txt), '^(\d)[- ]?(\d)[- ]?(\d)[- ]?.*([A-Z]{5})$', '\1\2\3\4')
17 end result
18 from testdata;
RESULT
--------------------------------------------------------------------------------
123ABCDE
123ABCDE
123ABCDE
123ABCDE
NULL
SQL>
You can use the fact that the position parameter of REGEXP_REPLACE() can take back-references to get a lot closer. Wrapped in a CASE statement you get what you're after:
select case when length(regexp_replace(txt, '[^[:alnum:]]')) >= 8 then
regexp_replace( regexp_replace(txt, '[^[:alnum:]]')
, '^([[:alnum:]]{3}).*([[:alnum:]]{5})$'
, '\1\2')
end
from test_data
This is, where the length of the string with all non-alpha-numeric characters replaced is greater or equal to 8 return the 1st and 2nd groups, which are respectively the first 3 and last 8 alpha-numeric characters.
This feels... overly complex. Once you've replaced all non-alpha-numeric characters you can just use an ordinary SUBSTR():
with test_data as (
select '1-23-456-78-90-ABCDE' txt from dual union all
select '1 23 456 78 90 ABCDE' txt from dual union all
select '1234567890ABCDE' txt from dual union all
select '123ABCDE' txt from dual union all
select '12DE' txt from dual
)
, standardised as (
select regexp_replace(txt, '[^[:alnum:]]') as txt
from test_data
)
select case when length(txt) >= 8 then substr(txt, 1, 3) || substr(txt, -5) end
from standardised
I feel like I'm missing something, but can't you just concatenate your two working columns? I.e., since you have successful regex for first 3 and last 5, just replace FIRST_THREE_AND_LAST_FIVE with:
regexp_substr(regexp_substr(regexp_replace(txt, '[^[[:alnum:]]]*',null), '([[:alnum:]]){3}',1,1)||regexp_substr(txt, '([[:alnum:]]){5}$',1,1),'([[:alnum:]]){5}',1,1)
EDIT: Added regexp_substr wrapper to return null when required

SQLite does not order by two columns

Structure is:
CREATE TABLE stopsbuses (_id INTEGER PRIMARY KEY, stop_id NUMERIC, bus_id NUMERIC, drive TEXT, day TEXT);
CREATE TABLE stopsbusestimes (_id INTEGER PRIMARY KEY, sb_id NUMERIC, timeh NUMERIC, timem NUMERIC);
When I execute query like this:
SELECT timeh, timem FROM stopsbuses
INNER JOIN stopsbusestimes ON stopsbuses._id = stopsbusestimes.sb_id
WHERE stopsbuses.stop_id = 2 ORDER BY timeh asc, timem asc
Output for example is:
[..]
7 1
7 31
7 34
7 54
7 57
7 22
[..]
Same output is with simple:
SELECT timeh, timem FROM stopsbusestimes ORDER BY timeh, timem
Same output using php + pdo, SQLite Database Browser and in Android aplication.
Am i missing something in ordering by two columns?
In SQLite, all strings are sorted behind all numbers, so it's likely that 22 is not a number.
To see the types, try typeof or quote:
SELECT quote(timeh), quote(timem) FROM stopsbuses ...

Resources