Spring JPA #Query where clause with 'like' and 'or' - spring

I have the following query:
#Query("select c from Category c where ( (lower(c.name) like '%' || lower(:searchText) || '%') or (lower(c.description) like '%' || lower(:searchText)) || '%')")
My product is designed to run in multiple platform, I am getting an error on postgreSQL which is:
PSQLException: ERROR: argument of OR must be type boolean, not type
text.
Which is undestandable since the like clause return strings. But I wasn't able to perform the search in one query request. So the question is how can I perform a search where the where conditions refer to 2 differnt columns and use the 'like' operator.

The parentheses you have are not correct the following should work:
#Query("select c from Category c " +
"where (lower(c.name) like ('%' || lower(:searchText) || '%')) " +
" or (lower(c.description) like ('%' || lower(:searchText) || '%'))")

Related

How to use REGEXP_SUBSTR to filter?

I have a select query which is working in postgres , but not in Oracle
The Select Query Uses regexp_split_to_array , which is not suppourted in Oracle
The regexp_split_to_array used here is to filter non working days
select
*
from
department
where
dept_status = 'A'
AND NOT (
#{DAY} = any ( regexp_split_to_array(lower(non_working_days), ',')))
AND dept_loc = 'US'
http://sqlfiddle.com/#!4/fbac4/4
Oracle does not have a function regexp_split_to_array . Instead you can use LIKE (which is much faster than regular expressions) to check for a sub-string match (including the surrounding delimiters so you match a complete term):
SELECT *
FROM department
WHERE dept_status = 'A'
AND ',' || lower(non_working_days) || ',' NOT LIKE '%,' || :DAY || ',%'
AND dept_loc = 'US'
Note: You should pass in variables using bind variables such as :day or ? (for an anonymous bind variable) rather than relying on a pre-processor to replace strings as it will help to prevent SQL injection attacks.
fiddle

In a CASE statement, can you store WHEN subquery result for use in the THEN output?

I have a report outputting the results of a query which was designed to provide links to a webpage:
SELECT
a,
b,
c,
'string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%URL_LINK~' || ipp_code || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY
) AS URL
FROM men_ipp
This works well, but I was asked to amend it so that if the records needed to generate the URL were missing (ie. sso_code can't be retrieved), it outputs a warning message instead of the subquery output.
Since there's always going to be a string of a set length (6 characters in this example), my solution was to create a CASE statement which is evaluating the length of the subquery output, and if the answer is greater than 6 characters it returns subquery result itself, otherwise it returns a warning message to the user. This looks like:
SELECT
a,
b,
c,
CASE
WHEN
LENGTH('string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || (ipp_code) || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY)
) > 6
THEN
('string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || (ipp_code) || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY)
ELSE 'warning message'
END AS URL
FROM men_ipp
The statment works fine, however the processing time is nearly doubled because it's having to process the subquery twice. I want to know if there's any way to store the result of the subquery in the WHEN, so it doesn't need to be run a second time in the THEN? eg. as a temporary variable or similar?
I've tried to declare a variable like this:
DECLARE URLLINK NVARCHAR(124);
SET URLLINK = 'string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%URL_LINK~' || ipp_code || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY
)
However this causes the query to error saying the it Encountered the symbol "https://evision.dev.uwl.tribalsits.com/urd/sits.urd/run/siw_file_load.sso?" when expecting one of the following: := . ( # % ; not null range default character
You can use NULLIF to make the result null if it is "string" (i.e., you appended nothing to it from your subquery). Then use NVL to convert to the warning message. Something like this:
SELECT
a,
b,
c,
nvl(nullif(
'string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || (ipp_code) || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY),'string'),'warning message')
FROM men_ipp
Use a CTE.
with temp as
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || (ipp_code) || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY
)
select a, b, c,
case when sso_code is null then 'warning message'
else 'string' || sso_code
end as url
from men_ipp full outer join temp on 1 = 1;
Use a sub-query:
SELECT a,
b,
c,
CASE
WHEN LENGTH(sso_code) > 6
THEN sso_code
ELSE 'warning message'
END AS URL
FROM (
SELECT a,
b,
c,
'string' ||
( SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || ipp_code || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY ) AS sso_code
FROM men_ipp
)

Search Query for Oracle SQL APEX

I am trying to create an advance search query using oracle SQL in Oracle APEX class report.
Is there a way to insert the query line into the query when there are values in the parameter?
For example, I have a query like so:
select person_id, fullname from person where first_name like '%:P11_FNAME%' AND last_name like '%:P11_LNAME%'
is there a way to add the first_name like '%:P11_FNAME%' into the query when there are actual values being passed in?
Bind variable syntax will not be recognised by the SQL engine if it is embedded within a string literal like '%:P11_FNAME%'.
You need to use string concatenation to do what you wanted:
select person_id, fullname from person
where first_name like '%' || :P11_FNAME || '%'
AND last_name like '%' || :P11_LNAME || '%';
If the user leaves search criteria blank, this will match all rows except those that have a NULL for the name. To make a blank search criterion match all rows, you need to add extra predicates, e.g.:
select person_id, fullname from person
where (first_name like '%' || :P11_FNAME || '%' or :P11_FNAME is null)
AND (last_name like '%' || :P11_LNAME || '%' or :P11_LNAME is null);

how to get the '' for each string in a listagg?

I have the following query :
SELECT
ix.dt AS DT,
ix.UDBENCH_UDIDX AS UDFO,
' .' || REPLACE(REPLACE( ix.UDBENCH_UDIDX,' ',''),'IS','') AS PF_TICKER,
i.szbez AS PORTFOLIO_NAME,
ix.rm_generic_inst_type_l1,
ix.rm_generic_inst_type_l2,
ix.scd_sec_type,
m.ud_isin AS SECURITY_ID,
'%' AS POS_TYPE,
ix.sec_weight AS QUANTITY,
ix.sec_ccy,
ix.sec_price AS MKT_PRICE,
'' AS COST_PX,
'' AS POSITION_VALUE_AC,
'' AS POSITION_VALUE_FC,
m.ud_sedol AS UD_SEDOL,
m.ud_bbgid AS UD_ID_BB_UNIQUE,
m.ud_cusip AS UD_CUSIP,
m.ud_bbgid AS UD_BBGID,
m.inst_name AS INST_NAME,
ix.idas AS IDAS,
m.ud_scd_securityid AS UD_SCD_SECURITYID
FROM XXXX ix
INNER JOIN XXXXR i ON (i.udidx = ix.UDBENCH_UDIDX),
XXXXX m
WHERE ix.dt >= to_date(sdt_start,'DD.MM.YYYY')
AND ix.dt <= to_date(sdt_end,'DD.MM.YYYY')
AND ix.UDBENCH_UDIDX IN (select listagg( udfo,',') within group(ORDER BY udfo)
from XXXXX where pf_ticker is null )
AND i.szbez LIKE '%DFLT%'
AND ix.idas = m.idas;
I would like the part :
AND ix.UDBENCH_UDIDX IN (select listagg( udfo,',') within group(ORDER
BY udfo)
from XXXXX where pf_ticker is null )
Equivalent to : ix.UDBENCH_UDIDX IN ('blal','bll',blc') but it shows ix.UDBENCH_UDIDX IN (blal,bll,blc) and the result of my query is an empty table, do you know how to set listagg to have this result ( 'blal','bll',blc' instead of blal,bll,blc)?
Thanks
The IN operator doesn't work like that. You'd be comparing the UDBENCH_UDIDX values with a single string containing all udfo values, not all of the individual values of that column.
You can just use a subquery without the listagg():
AND ix.UDBENCH_UDIDX IN (select udfo from XXXXX where pf_ticker is null)
Or you can join to that table instead of using a subquery at all; something like:
FROM XXXX ix
INNER JOIN XXXXR i ON (i.udidx = ix.UDBENCH_UDIDX)
INNER JOIN XXXXX m ON (m.udfo = ix.UDBENCH_UDIDX)
WHERE ix.dt >= to_date(sdt_start,'DD.MM.YYYY')
AND ix.dt <= to_date(sdt_end,'DD.MM.YYYY')
AND i.szbez LIKE '%DFLT%'
AND ix.idas = m.idas
AND m.pf_ticker is null;
... assuming the old-style join to XXXXX m is supposed to be getting the data related to the subquery you're doing - it's hard to tell with obfuscated names. (It's not a good idea to mix old and new style joins anyway; or to use old-style joins at all). It's possible you might want that to be an outer join, or the driving table, or something else - again can't infer that from the information provided.
If you already had a set of string literals to look for then you would do something like:
IN ('val1', 'val2', 'val3')
but you don't have string literals, you have string values from a table, which are not the same. You don't need to, and shouldn't, enclose those column values in single quotes within the query. The single quotes denote a literal value which is to be treated as a string; the values in the column are already strings.
You can actually do what you asked:
select '''' || listagg(udfo, ''',''') within group (order by udfo) || '''' from ...
which would give you a comma-separated list of quoted values from your table (or an empty string, which is the same as null, if there are no matching rows. If you were generating a statement to run later then that might make some sense, but that isn't the case here.

How to return all rows if IN clause has no value?

Following is sample query.
CREATE PROCEDURE GetModel
(
#brandids varchar(100), -- brandid="1,2,3"
#bodystyleid varchar(100) -- bodystyleid="1,2,3"
)
AS
select * from model
where brandid in (#brandids) -- use a UDF to return table for comma delimited string
and bodystyleid in (#bodystyleid)
My requirement is that if #brandids or #bodystyleid is blank, query should return all rows for that condition.
Please guide me how to do this? Also suggest how to write this query to optimize performance.
You'll need dynamic SQL or a split function for this anyway, since IN ('1,2,3') is not the same as IN (1,2,3).
Split function:
CREATE FUNCTION dbo.SplitInts
(
#List VARCHAR(MAX),
#Delimiter CHAR(1)
)
RETURNS TABLE
AS
RETURN ( SELECT Item = CONVERT(INT, Item) FROM (
SELECT Item = x.i.value('(./text())[1]', 'int') FROM (
SELECT [XML] = CONVERT(XML, '<i>' + REPLACE(#List, #Delimiter, '</i><i>')
+ '</i>').query('.') ) AS a CROSS APPLY [XML].nodes('i') AS x(i)) AS y
WHERE Item IS NOT NULL
);
Code becomes something like:
SELECT m.col1, m.col2 FROM dbo.model AS m
LEFT OUTER JOIN dbo.SplitInts(NULLIF(#brandids, ''), ',') AS br
ON m.brandid = COALESCE(br.Item, m.brandid)
LEFT OUTER JOIN dbo.SplitInts(NULLIF(#bodystyleid, ''), ',') AS bs
ON m.bodystyleid = COALESCE(bs.Item, m.bodystyleid)
WHERE (NULLIF(#brandids, '') IS NULL OR br.Item IS NOT NULL)
AND (NULLIF(#bodystyleid, '') IS NULL OR bs.Item IS NOT NULL);
(Note that I added a lot of NULLIF handling here... if these parameters don't have a value, you should be passing NULL, not "blank".)
Dynamic SQL, which will have much less chance of leading to bad plans due to parameter sniffing, would be:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'SELECT columns FROM dbo.model
WHERE 1 = 1 '
+ COALESCE(' AND brandid IN (' + #brandids + ')', '')
+ COALESCE(' AND bodystyleid IN (' + #bodystyleid + ')', '');
EXEC sp_executesql #sql;
Of course as #JamieCee points out, dynamic SQL could be vulnerable to injection, as you'll discover if you search for dynamic SQL anywhere. So if you don't trust your input, you'll want to guard against potential injection attacks. Just like you would if you were assembling ad hoc SQL inside your application code.
When you move to SQL Server 2008 or better, you should look at table-valued parameters (example here).
if(#brandids = '' or #brandids is null)
Begin
Set #brandids = 'brandid'
End
if(#bodystyleid = '' or #bodystyleid is null)
Begin
Set #bodystyleid = 'bodystyleid'
End
Exec('select * from model where brandid in (' + #brandids + ')
and bodystyleid in (' + #bodystyleid + ')')

Resources