How to get the exact value from sql like query - mysql

I have a Mysql database that contains some category ids on it which stores comma-separated values on a table.
sql table view
By using select * from style where categories like '%8,%'; it returns all the values end with 8. For example, if the table rows have two values like 8 and 148 it returns both rows. But I want to get only the rows that contain 8. How to do it

Storing multiple values in a single column is a denormalised design that will almost always cause you problems. However you need to add commas to both sides and compare:
select *
from Style
where concat(',',Categories,',') like '%,8,%';

Like everyone else: normalize your data. But if you can't mySQL supports find_in_set() for set datatypes which this appears to be.
DEMO dbfiddle.uk
DOC LINK: Find_in_set()
DOC LINK: SET data type
SQL
With CTE as (SELECT 'T-Shrits' as baseCategory, '8,21,75,87,148' categories UNION ALL
SELECT 'T-Shrits' as baseCategory, '8,21,75,87,148' categories UNION ALL
SELECT 'T-Shrits - Long Sleeve' as baseCategory, '8,21,75,87,148,92' categories UNION ALL
SELECT 'T-Shrits' as baseCategory, '21,75,87,100,148' categories)
SELECT * FROM CTE where find_in_set(8,categories) >0
OR we can use a boolean evaluation and eliminate the > 0
SELECT * FROM CTE where find_in_set(8,categories)
Giving us:
+------------------------+-------------------+
| baseCategory | categories |
+------------------------+-------------------+
| T-Shrits | 8,21,75,87,148 |
| T-Shrits | 8,21,75,87,148 |
| T-Shrits - Long Sleeve | 8,21,75,87,148,92 |
+------------------------+-------------------+
Notes
Find_in_set() returns the Returns a value in the range of 1 to N in the pseudo array of the value being searched. We need to ensure the result is greater than 0 (or treat it as a Boolean) in order for the searched value to "exist" within a record column.
The engine didn't return my 4th union value in CTE because it doesn't have an "alone" 8 value
If we searched for just 100 it would return that last record.
This function comes at a cost of performance on large datasets; which if data was normalized and indexed, you wouldn't have.
So why does this exit? For small enumerated lists or properties. It's still not ideal but if you have just a few using it "can" make sense. but in a very limited use case and often is missused.
This design violates 3rd normal form. Which is why most RDBMS designs cringe when it's brought up as it's not scalable.
as to why people are up in arms about multi value columns: Read this or This

You can also use rlike and in fact it is much better than like as it has much more options.
* = repetition of what is in front of it zero or more times
. = Equivalent to any character including none
^ = Anchor start (Forces that begins with ...)
$ = final anchor (forces it to end with ....)
[ ] = [ RST ] Contain an R or S or T but only one
[^] = DENY IT
And many more options
select * from style where concat(',',categories,',') rlike '*,8,*';

Related

Emulated SQL Queries

Let's say I have an arbitrary MySQL query called query1 that runs on a table table1 and generates a subset of the rows of that table. From a functional perspective, this query is just yielding a boolean for each row, where if the boolean is true, the row is returned in the results, and if it is false, it is not (or vice-versa, w.l.o.g.).
Let's also say that I'm taking a JSON blob, parsing it into a row, and adding the row to table1 every minute. I want to test and see if query1 returns true or false on the row immediately after I add it.
Is there a way to perform that test without actually adding the row somewhere in the database and then seeing if the row is returned by the query? My intuition is that it should be possible to 'emulate' the query on the new data without actually adding it anywhere, but I'm not familiar with how (if it's even possible in the first place).
You could use a derived table. E.g.:
select max(id) from (select 1 id, 'test' name from dual) as t;
In the above example 1 and 'test' represent the row you would need to emulate being present in the table and select max(id) from t represent query1.
You can use SQL to evaluate expressions without touching any table.
Example:
mysql> set #age = 29;
mysql> set #state = 'CA';
mysql> select #age > 30 and #state = 'CA';
+-----------------------------+
| #age > 30 and #state = 'CA' |
+-----------------------------+
| 0 |
+-----------------------------+
This example returns 0 (false) because the #age variable is not greater than 30.
You are certainly free to use values you extract from your JSON data (or any other source) as terms in an SQL expression like this.
It isn't required to use user variables as I showed in my example above. You can just use literal values too. I just showed using variables because it might be more clear to test your query conditions if you use variable names that match your column names.

How to combine CONCAT() and WHERE in MariaDB?

I like to use the result from another query to address the column name. For this I like to use CONCAT(). But somehow it don't work; when I run this line I get 0 rows back:
SELECT * FROM cover WHERE CONCAT('c','9') = 1;
When I don't make use of CONCAT() it work perfectly:
SELECT * FROM cover WHERE c9 = 1;
And also CONCAT() seems to work. With this I get a result:
SELECT CONCAT('c','9');
I tried all solution from this question:
MySQL select with CONCAT condition
like this one, but i always got 0rows back:
SELECT * FROM (
SELECT id, CONCAT('c', '9') as target
FROM cover) base
WHERE target = "1"
My MySQL Version is; 10.1.16-MariaDB
It is bad schema design to splay an array across a bunch of columns. And, as you are discovering, it is hard to use the columns. Build another table for the c values.
Or...
With lots of 0/1 "columns", consider SET or BIGINT UNSIGNED; either will hold up to 64 boolean flags in a tiny fraction of the space. And, with different code, BLOB could be used.
To extract bit 22 from a BIGINT, ((col >> 22) & 1) will give you 0 or 1.
Consider using a case when, since the number of options is known beforehand (you can only access columns that exist):
SELECT id
FROM cover
WHERE case ?
when 1 then c1
when 2 then c2
when 9 then c9
end = 1
... where the question mark would be the provided value, like 9 in your example.

find number of occurence in mysql

I have a string for example 'p2p3p4p9c5c6c7' I want to make a select-statement in mysql that returns how much of those strings ('p6','p7','p8' or 'p9') are containing in the initial string.
The result of my example should be 1, because only 'p9' is containing in my string.
I don't find a good way to do that. Can someone help?
another example
'k2p4p6p8p9c8' the result should be here 3
You would seem to have a poor data format. If you want to store lists of things, use a junction table.
However, the best answer that I can think of is a set of conditions that are added together:
select ((str like '%p6%') +
(str like '%p7%') +
(str like '%p8%') +
(str like '%p9%')
) as NumInString
MySQL treats booleans as integers in a numeric context, with "1" for true and "0" for false.
I should repeat that if the substrings are really codes of some type, then these should be stored in a separate junction table, with one row per code and original row.
SELECT count(*) FROM
(SELECT 'p2p3p4p9c5c6c7' AS a) AS string_table
INNER JOIN
(SELECT 'p6' AS b UNION ALL
SELECT 'p7' UNION ALL
SELECT 'p8' UNION ALL
SELECT 'p9') AS list_table
ON INSTR(string_table.a,list_table.b) > 0;

MySQL returns all rows when field=0 from SECOND Select query

This case is similar to: S.O Question; mySQL returns all rows when field=0, and the Accepted answer was a very simple trick, to souround the ZERO with single quotes
FROM:
SELECT * FROM table WHERE email=0
TO:
SELECT * FROM table WHERE email='0'
However, my case is slightly different in that my Query is something like:
SELECT * FROM table WHERE email=(
SELECT my_column_value FROM myTable WHERE my_column_value=0 AND user_id =15 LIMIT 1 )
Which in a sense, becomes like simply saying: SELECT * FROM table WHERE email=0, but now with a Second Query.
PLEASE NOTE: It is a MUST that I use the SECOND QUERY.
When I tried: SELECT * FROM table WHERE email='( SELECT my_column_value FROM myTable WHERE my_column_value=0 LIMIT 1 )' (Notice the Single Quotes on the second query)
MySql SCREAMED Errors near '(.
How can this be achieved
Any Suggestion is highly honored
EDIT1: For a visual perspective of the Query
See the STEN_TB here: http://snag.gy/Rq8dq.jpg
Now, the main aim is to get the sten_h where rawscore_h = 0;
The CURRENT QUERY as a whole.
SELECT sten_h
FROM sten_tb
WHERE rawscore_h = (
SELECT `for_print_stens_rowscore`
FROM `for_print_stens_tb`
WHERE `for_print_stens_student_id` =3
AND `for_print_stens_factor_name` = 'Factor H' )
The result of the Second Query can be any number including ZERO.
Any number from >=1 Works and returns a single corresponding value from sten_h. Only =0 does not Work, it returns all rows
That's the issue.
CORRECT ANSWER OR SOLUTION FOR THIS
Just in case someone ends up in this paradox, the Accepted answer has it all.
SEE STEN_TB: http://snag.gy/Rq8dq.jpg
SEE The desired Query result here: http://snag.gy/wa4yA.jpg
I believe your issue is with implicit datatype conversions. You can make those datatype conversions explicit, to gain control.
(The "trick" with wrapping a literal 0 in single quotes, that makes the literal a string literal, rather than a numeric.)
In the more general case, you can use a CAST or CONVERT function to explicitly specify a datatype conversion. You can use an expression in place of a column name, wherever you need to...
For example, to get the value returned by my_column_value to match the datatype of the email column, assuming email is character type, something like:
... email = (SELECT CONVERT(my_column_value,CHAR(255)) FROM myTable WHERE ...
or, to get the a literal integer value to be a string value:
... FROM myTable WHERE my_column_value = CONVERT(0,CHAR(30)) ...
If email and my_column_value are just indicating true or false then they should almost certainly be both BIT NOT NULL or other two-value type that your schema uses for booleans. (Your ORM may use a particular one.) Casting is frequently a hack made necessary by a poor design.
If it should be a particular user then you shouldn't use LIMIT because tables are unordered and that doesn't return a particular user. Explain in your question what your query is supposed to return including exactly what you mean by "15th".
(Having all those similar columns is bad design: rawscore_a, sten_a, rawscore_b, sten_b,... . Use a table with two columns: rawscore, sten.)

How to effectively search through MySQL DB with multiple searcheable columns?

I have a table with 60 boolean (TINYINT(1)) searcheable columns. User has possibility of using any subset of the given columns as search condition. Based on that I cannot create a good index for my needs. I was wondering if I can create another column (concat_col) of type BIT(60) that would be concatenation of the searchable columns, i.e.
Table_A:
id |col1|col2|...|col60|concat_col
9999 | 1 | 0 |...| 1 |10...1
I could then create a good index for it (on concat_col) but there is one problem - how do I create a query for it?
Please see this example written in pseudo code:
Standard version (This would obviously work fine):
SQL = SELECT * FROM Table_A WHERE col1=1 AND col60=1
My version ('*' is wildcard because it is not '1' neither '0'):
SQL = SELECT * FROM Table_a WHERE concat_col = '1*...1'
Is there any possibility of solving this problem effectively? Thank you very much for your help!
try with:
SQL = SELECT * FROM Table_a WHERE concat_col REGEXP '^1[0-9a-zA-Z]{58}1$'