Database is MySQL 5.6
CREATE TABLE set_t
(
set_r SET ('a', 'b', 'c')
);
INSERT INTO set_t (set_r) VALUES ('a,b,c'), ('a,b');
In my case i know only 'a' and 'b'.
For example, need to select row where set_r is "a,b,c".
Value 'c' is unknown, cant use it in query.
Values order is unknown also. They are may be set_r SET ('c', 'b', 'a') or else.
How to select rows, which contains unknown values?
The easy way to do it for your sample data is with NOT IN:
SELECT *
FROM set_t
WHERE set_r NOT IN ('a', 'b', 'a,b')
But this does not scale well if you have larger set and you want to include more members than a and b in the list of known members.
Every member of a set is stored as a numeric value.
In your set the numeric values are:
SET Member Decimal Value Binary Value
'a' 1 001
'b' 2 010
'c' 4 100
So, a value like 'a,b' is stored as 3 (Binary 011)
and a value like 'a,b,c' is stored as 7 (Binary 111)
If you know the numeric values of the known members, which you can get by the order they are defined in the set, then you can use bit operations to get what you want:
SELECT *
FROM set_t
WHERE set_r | 3 <> 3 -- 3 is the numeric value of 'a,b'
or:
SELECT *
FROM set_t
WHERE set_r | 0b11 <> 0b11 -- 0b11 is the numeric binary value of 'a,b'
See the demo.
You can use find_in_set() to check for individual values. See SET documentation here
select * from set_t where find_in_set('a', set_r) and find_in_set('b', set_r)
This gives you
set_r
(a,b,c)
(a,b)
See this db<>fiddle
If you don't want the second row, you can add and not like 'a,b'.
The most efficient method is to use the binary comparisons. This assumes that you know the position of 'a' and 'b' in the set:
select *
from set_t
where (set_r | b'11') <> b'11';
You can also do this using string operations. This is a bit of a pain, because you need to handle the comma delimiter:
select *
from set_t
where replace(replace(replace(concat(',', set_r, ','), ',a,', ','), ',b,', ','), ',', '') <> ''
The logic is:
Put delimiters at the beginning and end of set_r.
Remove delimited elements, replacing them with the delimiter.
Check if the result only has delimiters.
This version works regardless of the size of the set and the position of the elements you want to compare against.
Here is a db<>fiddle.
Related
Use-Case: I want to select all entries from table t123, where field 'text' of the table is NOT a subset string of 'text_target'.
For example:
text_target = 'abf'
t123 has entries like,
name text
1) record1 abc
2) record2 abd
3) record3 af
result should be records 1 and 2, since their 'text' field is not subset of text_target value 'abf'.
EDIT: corrected the question
use like
select * from t123 where text like'ab%'
I think you'll need to explode the string:
where ( (test like '%a%') +
(test like '%b%') +
(test like '%f%')
) <> length(test)
It is not obvious that even regular expressions can help.
However, looking at strings as sets with one element per letter sounds like a data modeling problem. You may want to ask another question describing your data and suggestions on how you might design your database.
Here is a rextester (thanks to Tim).
Not elegant, but I suppose:
select *
from t123
where not ( substring( text from 1 for 1 ) in ( 'a', 'b', 'f', ' ' )
and substring( text from 2 for 1 ) in ( 'a', 'b', 'f', ' ' )
and substring( text from 3 for 1 ) in ( 'a', 'b', 'f', ' ' ) )
could return those rows that contain other letters.
Use regular expression. For instance, if we suppose that you want to retrieve records NOT containing characters from "text_target" ("abf") :
SELECT * FROM t123 WHERE NOT text RLIKE "a|b|f"
I have a database that contains a column "Code" where the records have the following format "xx-xxx" and "xx-xx", for the later format i want to add a zero after the "-" to make it "xx-0xx", is there anyway to count the characters after a certain pattern in Mysql
Hmmm. If those are your only two possibilities, you can use case:
select (case when length(code) = 5
then replace(code, '-', '-0')
else code
end) as new_code
If you want to be more general, deconstruct the string and build it back again:
select concat_ws('-', substring_index(code, '-', 1),
lpad(substring_index(code, '-', -1), 3, '0')
)
Yes, you can use the CHAR_LENGTH(str) like this:
SELECT code,CHAR_LENGTH(SUBSTR(code,3))
from table
I would like to ask how to split string from column (all rows in table) by " " and insert result separated by , into another column in same table?
Many thanks for any advice.
Table struct example:
------------------------------------------
| Original string | Spliced string |
------------------------------------------
| Some string 001 | Some,String,001 |
------------------------------------------
If I needed to "split" a string on a delimiter, I'd likely make use of the nifty SUBSTRING_INDEX function. But there are a few quirks to be aware of.
The approach I would take would certainly be to write a SELECT statement first. That would include the expression(s) in the SELECT list that return the "separated" values that I wanted to assign to another column. I'd get those expressions tested using a SELECT statement, before I wrote an UPDATE statement.
SELECT t.id
, t.column_i_want_to_split
, expr1
FROM mytable t
ORDER BY t.id
To test specific cases, I'd make use of an inline view
SELECT t.id
, t.note
, t.val
, expr1
FROM ( SELECT 1 AS id, 'empty string test' AS note, '' AS val
UNION ALL SELECT 2, 'null', NULL
UNION ALL SELECT 3, 'one space', ' '
UNION ALL SELECT 4, 'four spaces', ' '
UNION ALL SELECT 5, 'test5', ' abc def '
UNION ALL SELECT 6, 'test6', 'g hi kl m'
) t
ORDER BY t.id
Once I had the expression(s) returning the values I want to assign to another column, I'd convert the SELECT into an UPDATE statement. To process all rows, omit the WHERE clause.
UPDATE mytable t
SET t.another_column = expr1
Without a more definitive specification, or at least some concrete examples of what you are attempting to achieve, we're just guessing. Given only a general description of the problem, all we can offer is some general advice.
In MySQL, why does the following query return '----', '0', '000', 'AK3462', 'AL11111', 'C131521', 'TEST', etc.?
select varCharColumn from myTable where varCharColumn in (-1, '');
I get none of these results when I do:
select varCharColumn from myTable where varCharColumn in (-1);
select varCharColumn from myTable where varCharColumn in ('');
Note: I'm using MySQL version 5.0.45-log (show variables like "%version%";)
Note 2: I tried this on a number column as well, but I do not get unexpected results there.
Your expression is:
where varCharColumn in (-1, '')
The list has to have consistent types. The first element says "this is a list of integers", so the second value is converted to an integer. And '' becomes 0.
In fact, any alphanumeric string that starts with a non-digit is also converted to 0 for an integer comparison. So, you have this situation
'A' in (0) --> TRUE
'B' in (0) --> TRUE
'A' in ('B') --> FALSE
You can readily test this with:
select 'A' in (0) union all
select 'B' in (0) union all
select 'A' in ('B');
You can see it in action with a column:
select val in (0), val in ('0'), val in (0, '')
from (select 'A' as val) t
This returns true, false, true. However, note that val in (-1, 'B') returns FALSE in this case. MySQL is treating the empty string differently from a real string, perhaps inconsistently with the documentation.
That this is true with columns is exhibited by:
select val in (0)
from (select 'A' as val) t;
Who said logic can't be fun?
To fix this, make the list all be of consistent types, probably by putting single quotes around the numbers.
As documented under Comparison Functions and Operators:
You should never mix quoted and unquoted values in an IN list because the comparison rules for quoted values (such as strings) and unquoted values (such as numbers) differ. Mixing types may therefore lead to inconsistent results. For example, do not write an IN expression like this:
SELECT val1 FROM tbl1 WHERE val1 IN (1,2,'a');
Instead, write it like this:
SELECT val1 FROM tbl1 WHERE val1 IN ('1','2','a');
I'd like to select rows from the database where the last character in the mov_id column equals to 1 and 2.
How would the query look like?
SELECT * FROM `myTable` WHERE `mov_id` LIKE '%1' OR `mov_id` LIKE '%2'
the % character is a wildcard which matches anything (like * in many other places)
If mov_id is a numeric value (TINYINT, INT, etc...) then you should use a numeric operator. For instance, use the modulo operator to keep the last digit
SELECT * FROM mytable
WHERE (mov_id MOD 10) IN (1, 2)
If mov_id is a string, you can use LIKE or SUBSTRING(). SUBSTRING() will be slightly faster.
SELECT * FROM mytable
WHERE SUBSTRING(mov_id, -1) IN ('1', '2')
If your table is big or that query is frequently run, you should definitely consider adding a column to your table, in which you would store mov_id's last digit/character, and index that column.
Try this way too:
SELECT field1
FROM table
WHERE RIGHT(field1, 1) = 'x'
it displays the fields that has last a value of x.
SELECT *
FROM Table
WHERE RIGHT(Column_name, 1) IN ('x')
if you want to match two character just replace 1 by 2.
In general:
RIGHT(COLUMN_NAME, NO_OF_CHARACTER_YOU WANT_TO_MATCH_FROM_LAST)
And if you want to match the starting char just use LEFT instead of RIGHT
You could also do something like:
select
your, fields, go, here
from table
where
substring(mov_id, (char_length(move_id) - 1)) = x
SELECT * FROM table WHERE mov_id REGEXP '1$' OR mov_id REGEXP '2$'