MySQL - matching and non-matching results in Where IN query - mysql

I need to find matching and non matching keywords from the database against user entered keywords.
Below is the database schema where we store all the known keywords
Table keywords
keyword varchar
Finding matching keywords in simple. We use following query to find matching keywords
select keyword from keywords where keyword in ('abc', 'pqr', 'xyz')
Where 'abc', 'pqr', 'xyz' are keywords supplied by the user.
However, I also need to find the keywords which are not present in the database. Definitely NOT IN does not work as it will return all the tags from the database rather than the one which was not matched. For example, if 'abc' and 'pqr' is present in the database and 'xyz' is not, I am expecting output as following
keyword present
abc 1
pqr 1
xyz 0
Please help me.

You can try below - using cte
with cte1 as
(
select 'abc' as key
union
select 'pqr' union select 'xyz'
)
select keyword,case when key is null then 0 else 1 end as is_present
from keywords left join cte1 on keyword=key

Select the three keywords and use a subquery to look them up in the keywords table:
select
keyword,
keyword in (select keyword from keywords) as present
from
(
select 'abc' as keyword
union all
select 'pqr' as keyword
union all
select 'xyz' as keyword
) user_keywords
order by keyword;

Related

how to query by checking if specific fields start with a value from a given array?

(MySQL)
I have a query to check if 'phone_number' or 'fax_number' startsWith a value from a given array,
lets say const possibleValues = [123,432,645,234]
currently my query runs with the 'or' condition, to check if -
'phone_number' or 'fax_number' that starts with 123
or
'phone_number' or 'fax_number' that starts with 432
or
'phone_number' or 'fax_number' that starts with 645
or
'phone_number' or 'fax_number' that starts with 234
it runs extremely slow on a big database, and I wish to make it faster,
is there a way to make it run faster?
I'm kinda new to sql queries,
any help would be highly appreciated!
You can try something like:
SELECT * FROM table_1
WHERE CONCAT(',', `phone_number`, ',') REGEXP ',(123|432|645|234),'
OR CONCAT(',', `fax_number`, ',') REGEXP ',(123|432|645|234),';
Demo
Try creating an in-line table and join with it.
WITH telnostart(telnostart) AS (
SELECT '123'
UNION ALL SELECT '432'
UNION ALL SELECT '645'
UNION ALL SELECT '234'
)
SELECT
*
FROM your_search_table
JOIN telnostart ON (
LEFT(tel_number,3) = telnostart
OR LEFT(fax_number,3) = telnostart
you can use a case statement to add a flag column
select *
,case when left(phone_number,3) in (123,432,645,234) or left(fax_number,3) in (123,432,645,234) then 1 else 0 end as contact_check_flag
from table_name
As per your requirement, you can filter it or use it elsewhere.
SELECT * FROM table_1
WHERE `phone_number` REGEXP '^(123|432|645|234)'
OR `fax_number` REGEXP '^(123|432|645|234)';
But it won't be fast. (And no regular INDEX will help.)
If there phone numbers are spelled out like in the US: "123-456-7890", then you could use a FULLTEXT(phone_number, fax_number) index and
SELECT * FROM table_1
WHERE MATCH(phone_number, fax_number)
AGAINST('123 432 645 234');
This is likely to be much faster, but not as "general".

MySQL find rows that do not contain keywords from a lookup table

I can reject rows from a query where an item is not in a lookup table if there is an exact match:
select * from mytable where (item not in (select keyword from lookup_table))
But now I need to reject rows if the item contains any of the keywords in the lookup table
Pseudo code:
select *
from mytable
as long as any part of the item field does not contain any keyword from the lookup table
keywords in lookup table: 'something', 'test', 'something else'
item: 'this is a test' would be rejected since it contains 'test'
item: 'this is something else' would be rejected since it contains 'something'
(it would also have been rejected since it contains 'something else')
item: 'this is OK' would be included in the select since it contains none of the keywords
Thank you!
I would use not exists:
select t.*
from mytable t
where not exists (
select 1
from lookup_table l
where t.item like concat ('%', l.keyword, '%')
)

Joining two strings using rlike

I am joining the tables Table_A and Table_B on columns Col_A and Col_B. Below are some test sample values.
Table_A, Col_A
USA1FullCover
USAMainland
USA2Islands
Table_B, Col_B
USA
USA1
USA2
When joining, I need to match the value 'USA' followed by a number exactly. For instance, the join result should look like this.
Col_A Col_B
USA1FullCover USA1
USAMainland USA
USA2Island USA2
I'm trying to achieve this in MySQL. I tried the rlike function. But the issue is that with rlike, I am not able to completely match them.
select
case when 'USA1FullCover' rlike 'USA' then 1 else 0 end;
#matches, but shouldn't
select
case when 'USA1FullCover' rlike 'USA1' then 1 else 0 end;
#matches, which is what I need/expect
select
case when 'USA1FullCover' rlike 'USA2' then 1 else 0 end;
#doesn't match, which is what I need/expect
My question is how can I fix the rlike so that the first case doesn't happen i.e. it doesn't match when there is no digit on the RHS? Or is it possible using regex? Taking a substring of the LHS doesn't help since we cannot really define the length beforehand.
Use
col_A regexp '^USA[0-9]{1}.+$'
to match USA followed by exactly one digit followed by any other characters.
To join only when such pattern exists
select a.*,b.*
from tableA a
join tableB b on
case when a.col_A regexp '^USA[0-9]{1}.+$' then substring(a.col_A,1,4) else '' end
= b.col_B

MySQL query to categorised address based on values using regular Expression

I have a table which has one column of addresses. I have some 10 - 11 places name.
When i query that table using 'Select * ...', i want to create a new column which matches the values with address fields and store that values into new column of exist else 'Not Found'.
The table has address column as below. I want to extract areas from it such as BTM Layot, Wilson Garden
When i do the select query, the output should be that address field and one more field which will give me the abstract location area from address field. And if any value does not matches the address field then it shoud display as 'Area Nt Specified'
Consider a cross join query (query with no joins but a list of tables in FROM clause) between the larger table of addresses (t1) and smaller table of your 10-11 places (t2) holding BTM Layot, Wilson Garden... values. This will be scalable instead of manually entering/editing places in an IN clause.
Then use a LIKE expression in a WHERE clause to match the places which are a part of the larger address string. However, to return all original address values with matched places use the LEFT JOIN...NOT NULL query with cross join as derived table (sub).
SELECT `maintable`.`address`, IFNULL(sub.`place`, 'Area Nt Specified') As matchplaces
FROM `maintable`
LEFT JOIN
(SELECT t1.ID, t1.address, t2.place
FROM `maintable` As t1,
(SELECT `place` FROM `placestable`) As t2
WHERE t1.address LIKE Concat('%',t2.place,'%')) As sub
ON `maintable`.ID = sub.ID
WHERE `maintable`.ID IS NOT NULL;
If really need to use regular expression, replace the LIKE expression in derived table with below:
WHERE t1.address regexp t2.place
If you have a list of know places, then you can do:
select (case when address regexp '(, BTM Layout|, Bapuji Nagar|, Adugodi)$'
then substring_index(address, ', ', -1)
else 'Not Found'
end)
You can expand the regular expression to include as many places as you like.
Or alternatively, you don't really need a regular expression:
select (case when substring_index(address, ', ', -1) in ('BTM Layout', 'Bapuji Nagar', 'Adugodi', . . .)
then substring_index(address, ', ', -1)
else 'Not Found'
end)

Select query with IN clause: filling the blanks

I have the following problem with a MySQL query in C#:
Given a list of strings, I want to query the database for any rows that match said strings. The strings are unique in that each string matches no more than one row. Today, my query looks something like this:
SELECT Id FROM SomeTable
WHERE SomeColumn IN("foo", "bar", "baz")
Now, ideally I would like to be able to map the result from the query directly to the list of strings I supplied in the IN clause:
String Returned ID
------------------------------------------
foo 123
bar NULL <-- Missing row filled with NULL
baz 42
This works fine as long as all strings I pass to the query match a row. When one is missing, however, I would like to fill in the blank with a NULL as in the example above.
Is there any way to accomplish this?
Edit: I should probably have pointed out that the solution must scale to a lot of strings. The way I do it right now is that I pass 100 at a time through the IN clause.
You could do this:
SELECT
helper.SomeColumn,
SomeTable.Id
FROM
(
SELECT 'foo' AS SomeColumn
UNION SELECT 'bar'
UNION SELECT 'baz'
) AS helper
LEFT JOIN SomeTable ON SomeTable.SomeColumn = helper.SomeColumn
Of course you can create the helper table (as a temp table) beforehand instead of inline.
Anyway, maybe it is smarter and more efficient to just do the query you have (WHERE SomeColumn IN (...)) and simply figure out the missing rows in your application. You will loop over them anyway, so you will notice.
What you could do is SELECT the set of strings as a result set and then LEFT JOIN on SomeTable.SomeColumn.
Try this:
SELECT Id
FROM (
SELECT "foo" SomeColumn
UNION ALL
SELECT "bar" AS SomeColumn
UNION ALL
SELECT "baz" AS SomeColumn
) b
LEFT JOIN
SomeTable a
ON a.SomeColumn = b.SomeColumn