Checking if string has letter e (case insensitive) sql query using one where condition - sql

I am practising on a website, where they ask me to show a list of players with an 'e' or 'E' in their name using only one WHERE condition.
My query shows the same output as what is expected, yet the website is saying 'I did something that is not right or result is incomplete'.
Does my WHERE statement count as one condition? Is there a way to be case insensitive only after the LIKE?
SELECT DISTINCT s.spelersnr, voorletters || ' ' || naam AS spelersnaam
FROM spelers s INNER JOIN wedstrijden w
ON s.spelersnr = w.spelersnr
WHERE LOWER(naam) LIKE '%e%'
ORDER BY s.spelersnr ASC

Assuming your database supports a LOWER function, then your current WHERE clause would already seem to be one condition:
WHERE LOWER(naam) LIKE '%e%'
In Postgres, you could also write this using SIMILAR TO:
WHERE naam SIMILAR TO '%(e|E)%'
Or, you could use a POSIX regular expression:
WHERE naam ~* '.*e.*'

as you mentioned it is postgres db, using similar to or like will help. a detailed explanation is in below link
https://dba.stackexchange.com/questions/10694/pattern-matching-with-like-similar-to-or-regular-expressions-in-postgresql

this will work:
SELECT DISTINCT s.spelersnr, voorletters || ' ' || naam AS spelersnaam
FROM spelers s INNER JOIN wedstrijden w
ON s.spelersnr = w.spelersnr
WHERE REGEXP_LIKE (naam, '(.)*e(.)*', 'i')
ORDER BY s.spelersnr ASC

The simplest way is to use ILIKE:
WHERE naam ILIKE '%e%'
This is built-in Postgres operator that does case-insensitive matching. This is explained in the documentation.

Related

Subquery not excluding items from main query

I have the following SQL query to exclude employees from 'Post Closing' dept. But I am still getting people in Post-Closing Dept. The not exists doesn't seem to be working.
select concat(ltrim(rtrim(FirstName)), ' ',ltrim(rtrim(LastName))) Employee
from employeelist t1
where Department_Desc like '%Closing' or Department_Desc like '%Funding'
and (Position like 'A1%' or position like 'A2%')
and not exists(select concat(ltrim(rtrim(FirstName)), ' ',ltrim(rtrim(LastName))) Employee
from employeelist t2
where t1.Employee_Code = t2.Employee_Code
and t2.Department_Desc in ('Post Closing' ))
Any help in resolving this issue will be appreciated.
Because of operator precedence you get all rows where
department_desc LIKE '%Closing'
or
department_desc LIKE '%Funding'
AND ... -- the rest of the WHERE clause
And as 'Post Closing' is like '%Closing' the records with 'Post Closing' are in the result.
You seem to want
WHERE (department_desc LIKE '%Closing'
OR department_desc LIKE '%Funding')
AND ...
i.e. let the OR in the expression on the department descriptions take precedence over the following AND.
Your issue is the parentheses. I would recommend that you write the query as:
where (Department_Desc like '%Closing' or
Department_Desc like '%Funding'
) and
Position like 'A[12]%' and
not exists (select 1
from employeelist t2
where t1.Employee_Code = t2.Employee_Code and
t2.Department_Desc in ('Post Closing' )
)
In addition to fixing the parentheses, there are two changes:
This uses SQL Servers support for (limited) character classes in LIKE patterns to simplify the comparison for Position.
The select for exists is simplified to "1". Exists only checks if a row is returned; the value doesn't matter.
Please try with escape character.
and t2.Department_Desc LIKE ('%Post_Closing%' ))

SQL special group by on list of strings ending with *

I would like to perform a "special group by" on strings with SQL language, some ending with "*". I use postgresql.
I can not clearly formulate this problem, even if I have partially solved it, with select, union and nested queries which are not elegant.
For exemple :
1) INPUT : I have a list of strings :
thestrings
varchar(9)
--------------
1000
1000-0001
1000-0002
2000*
2000-0001
2000-0002
3000*
3000-00*
3000-0001
3000-0002
2) OUTPUT : That I would like my "special group by" return :
1000
1000-0001
1000-0002
2000*
3000*
Because 2000-0001 and 2000-0002 are include in 2000*,
and because 3000-00*, 3000-0001 and 3000-0002 are includes in 3000*
3) SQL query I do :
SELECT every strings ending with *
UNION
SELECT every string where the begining NOT IN (SELECT every string ending with *) <-- with multiple inelegant left functions and NOT IN subqueries
4) That what I'm doing return :
1000
1000-0001
1000-0002
2000*
3000*
3000-00* <-- the problem
The problem is : 3000-00* staying in my result.
So my question is :
How can I generalize my problem? to remove all string who have a same begining string in the list (ending with *) ?
I think of regular expressions, but how to pass a list from a select in a regex ?
Thanks for help.
Select only strings for which no master string exists in the table:
select str
from mytable
where not exists
(
select *
from mytable master
where master.str like '%*'
and master.str <> mytable.str
and rtrim(mytable.str, '*') like rtrim(master.str, '*') || '%'
);
Assuming that only one general pattern can match any given string, the following should do what you want:
select coalesce(tpat.thestring, t.thestring) as thestring
from t left join
t tpat
on t.thestring like replace(tpat.thestring, '*', '%') and
t.thestring <> tpat.thestring
group by coalesce(tpat.thestring, t.thestring);
However, that is not your case. However, you can adjust this with distinct on:
select distinct on (t.thestring) coalesce(tpat.thestring, t.thestring)
from t left join
t tpat
on t.thestring like replace(tpat.thestring, '*', '%') and
t.thestring <> tpat.thestring
order by t.thestring, length(tpat.thestring)

SQL Query inside a function

I am using PostgreSQL with PostGis. I am executing a query like this:
select st_geomfromtext('point(22 232)',432)
It works fine. But now I want to take a value through a query. for example:
select st_geomfromtext('point((select x from data_name where id=1) 232)' , 432)
Here data_name is some table I am using and x stores some values. Now query inside is treated as a string and no value is returned.
Please help.
ERROR: syntax error at or near "select"
Try this:
select st_geomfromtext('point(' || x || ' 232)', 432) from data_name where id=1
Postgis has a function ST_MakePoint that is faster than ST_GeomFromText.
select ST_SetSRID(ST_MakePoint(x),432) from data_name where id=1;
While #muratgu answer is generally the way to go, one minor note:
A subquery gets you a different result when no row is found for id = 1. Then you get nothing back (no row), instead of:
select st_geomfromtext(NULL, 432)
If you need a drop-in replacement:
select st_geomfromtext('point('
|| (select x from data_name where id=1)
|| ' 232)' , 432)

How to join tables on regex

Say I have two tables msg for messages and mnc for mobile network codes.
They share no relations. But I want to join them
SELECT msg.message,
msg.src_addr,
msg.dst_addr,
mnc.name,
FROM "msg"
JOIN "mnc"
ON array_to_string(regexp_matches(msg.src_addr || '+' || msg.dst_addr, '38(...)'), '') = mnc.code
But query fails with error:
psql:marketing.sql:28: ERROR: argument of JOIN/ON must not return a set
LINE 12: ON array_to_string(regexp_matches(msg.src_addr || '+' || msg...
Is there a way to do such join? Or am I moving wrong way?
A very odd way to join. Every match on one side is combined with every row from the other table ...
regexp_matches() is probably the wrong function for your purpose. You want a simple regular expression match (~). Actually, the LIKE operator will be faster:
Presumably fastest with LIKE
SELECT msg.message
, msg.src_addr
, msg.dst_addr
, mnc.name
FROM mnc
JOIN msg ON msg.src_addr LIKE ('%38' || mnc.code || '%')
OR msg.dst_addr LIKE ('%38' || mnc.code || '%')
WHERE length(mnc.code) = 3;
In addition, you only want mnc.code of exactly 3 characters.
With regexp match
You could write the same with regular expressions but it will most definitely be slower. Here is a working example close to your original:
SELECT msg.message
, msg.src_addr
, msg.dst_addr
, mnc.name
FROM mnc
JOIN msg ON (msg.src_addr || '+' || msg.dst_addr) ~ (38 || mnc.code)
AND length(mnc.code) = 3;
This also requires msg.src_addr and msg.dst_addr to be NOT NULL.
The second query demonstrates how the additional check length(mnc.code) = 3 can go into the JOIN condition or a WHERE clause. Same effect here.
With regexp_matches()
You could make this work with regexp_matches():
SELECT msg.message
, msg.src_addr
, msg.dst_addr
, mnc.name
FROM mnc
JOIN msg ON EXISTS (
SELECT *
FROM regexp_matches(msg.src_addr ||'+'|| msg.dst_addr, '38(...)', 'g') x(y)
WHERE y[1] = mnc.code
);
But it will be slow in comparison.
Explanation:
Your regexp_matches() expression just returns an array of all captured substrings of the first match. As you only capture one substring (one pair of brackets in your pattern), you will exclusively get arrays with one element.
You get all matches with the additional "globally" switch 'g' - but in multiple rows. So you need a sub-select to test them all (or aggregate). Put that in an EXISTS - semi-join and you arrive at what you wanted.
Maybe you can report back with a performance test of all three?
Use EXPLAIN ANALYZE for that.
Your immediate problem is that regexp_matches could return one or more rows.
Try using "substring" instead, which extracts a substring given a regex pattern.
SELECT msg.message,
msg.src_addr,
msg.dst_addr,
mnc.name
FROM "msg"
JOIN "mnc"
ON substring(msg.src_addr || '+' || msg.dst_addr from '38(...)') = mnc.code

MySQL LIKE IN()?

My current query looks like this:
SELECT * FROM fiberbox f WHERE f.fiberBox LIKE '%1740 %' OR f.fiberBox LIKE '%1938 %' OR f.fiberBox LIKE '%1940 %'
I did some looking around and can't find anything similar to a LIKE IN() - I envision it working like this:
SELECT * FROM fiberbox f WHERE f.fiberbox LIKE IN('%140 %', '%1938 %', '%1940 %')
Any ideas? Am I just thinking of the problem the wrong way - some obscure command I've never seen.
MySQL 5.0.77-community-log
A REGEXP might be more efficient, but you'd have to benchmark it to be sure, e.g.
SELECT * from fiberbox where field REGEXP '1740|1938|1940';
Paul Dixon's answer worked brilliantly for me. To add to this, here are some things I observed for those interested in using REGEXP:
To Accomplish multiple LIKE filters with Wildcards:
SELECT * FROM fiberbox WHERE field LIKE '%1740 %'
OR field LIKE '%1938 %'
OR field LIKE '%1940 %';
Use REGEXP Alternative:
SELECT * FROM fiberbox WHERE field REGEXP '1740 |1938 |1940 ';
Values within REGEXP quotes and between the | (OR) operator are treated as wildcards. Typically, REGEXP will require wildcard expressions such as (.*)1740 (.*) to work as %1740 %.
If you need more control over placement of the wildcard, use some of these variants:
To Accomplish LIKE with Controlled Wildcard Placement:
SELECT * FROM fiberbox WHERE field LIKE '1740 %'
OR field LIKE '%1938 '
OR field LIKE '%1940 % test';
Use:
SELECT * FROM fiberbox WHERE field REGEXP '^1740 |1938 $|1940 (.*) test';
Placing ^ in front of the value indicates start of the line.
Placing $ after the value indicates end of line.
Placing (.*) behaves much like the % wildcard.
The . indicates any single character, except line breaks. Placing .
inside () with * (.*) adds a repeating pattern indicating any number
of characters till end of line.
There are more efficient ways to narrow down specific matches, but that requires more review of Regular Expressions. NOTE: Not all regex patterns appear to work in MySQL statements. You'll need to test your patterns and see what works.
Finally, To Accomplish Multiple LIKE and NOT LIKE filters:
SELECT * FROM fiberbox WHERE field LIKE '%1740 %'
OR field LIKE '%1938 %'
OR field NOT LIKE '%1940 %'
OR field NOT LIKE 'test %'
OR field = '9999';
Use REGEXP Alternative:
SELECT * FROM fiberbox WHERE field REGEXP '1740 |1938 |^9999$'
OR field NOT REGEXP '1940 |^test ';
OR Mixed Alternative:
SELECT * FROM fiberbox WHERE field REGEXP '1740 |1938 '
OR field NOT REGEXP '1940 |^test '
OR field NOT LIKE 'test %'
OR field = '9999';
Notice I separated the NOT set in a separate WHERE filter. I experimented with using negating patterns, forward looking patterns, and so on. However, these expressions did not appear to yield the desired results. In the first example above, I use ^9999$ to indicate exact match. This allows you to add specific matches with wildcard matches in the same expression. However, you can also mix these types of statements as you can see in the second example listed.
Regarding performance, I ran some minor tests against an existing table and found no differences between my variations. However, I imagine performance could be an issue with bigger databases, larger fields, greater record counts, and more complex filters.
As always, use logic above as it makes sense.
If you want to learn more about regular expressions, I recommend www.regular-expressions.info as a good reference site.
Regexp way with list of values
SELECT * FROM table WHERE field regexp concat_ws("|",
"111",
"222",
"333");
You can create an inline view or a temporary table, fill it with you values and issue this:
SELECT *
FROM fiberbox f
JOIN (
SELECT '%1740%' AS cond
UNION ALL
SELECT '%1938%' AS cond
UNION ALL
SELECT '%1940%' AS cond
) с
ON f.fiberBox LIKE cond
This, however, can return you multiple rows for a fiberbox that is something like '1740, 1938', so this query can fit you better:
SELECT *
FROM fiberbox f
WHERE EXISTS
(
SELECT 1
FROM (
SELECT '%1740%' AS cond
UNION ALL
SELECT '%1938%' AS cond
UNION ALL
SELECT '%1940%' AS cond
) с
WHERE f.fiberbox LIKE cond
)
Sorry, there is no operation similar to LIKE IN in mysql.
If you want to use the LIKE operator without a join, you'll have to do it this way:
(field LIKE value OR field LIKE value OR field LIKE value)
You know, MySQL will not optimize that query, FYI.
Just note to anyone trying the REGEXP to use "LIKE IN" functionality.
IN allows you to do:
field IN (
'val1',
'val2',
'val3'
)
In REGEXP this won't work
REGEXP '
val1$|
val2$|
val3$
'
It has to be in one line like this:
REGEXP 'val1$|val2$|val3$'
This would be correct:
SELECT * FROM table WHERE field regexp concat_ws("|",(
"111",
"222",
"333"
));
Flip operands
'a,b,c' like '%'||field||'%'
Just a little tip:
I prefer to use the variant RLIKE (exactly the same command as REGEXP) as it sounds more like natural language, and is shorter; well, just 1 char.
The "R" prefix is for Reg. Exp., of course.
You can get desired result with help of Regular Expressions.
SELECT fiberbox from fiberbox where fiberbox REGEXP '[1740|1938|1940]';
We can test the above query please click SQL fiddle
SELECT fiberbox from fiberbox where fiberbox REGEXP '[174019381940]';
We can test the above query please click SQL fiddle
You can use like this too:
SELECT
*
FROM
fiberbox f
JOIN (
SELECT
substring_index( substring_index( '1740,1938,1940', ',', help_topic_id + 1 ), ',',- 1 ) AS sub_
FROM
mysql.help_topic
WHERE
help_topic_id <(
length( '1740,1938,1940' )- length(
REPLACE ( '1740,1938,1940', ',', '' ))+ 1
) AS b
) ON f.fiberBox LIKE concat('%',
b.sub_,
'%')
You can use like this too:
SELECT * FROM fiberbox WHERE fiber IN('140 ', '1938 ', '1940 ')