Oracle Text multi column index based query does not return any rows - oracle

I have a MAH_KERESES_MV table with 3 columns OBJEKTUM_NEV, KERESES_SZOVEG_1, KERESES_SZOVEG_2. I create the following multi column Oracle Text index:
exec ctx_ddl.create_preference( 'MAH_SEARCH', 'MULTI_COLUMN_DATASTORE');
exec ctx_ddl.set_attribute('MAH_SEARCH', 'COLUMNS', 'OBJEKTUM_NEV, KERESES_SZOVEG_1, KERESES_SZOVEG_2');
create index MAX_KERES_CTX on MAH_KERESES_MV(OBJEKTUM_NEV)
indextype is ctxsys.context
parameters ('DATASTORE MAH_SEARCH');
But the query does not return any rows, although if I formulate the query with the like operator, then I get the results as expected:
SELECT id, OBJEKTUM_NEV
FROM MAH_KERESES_MV
WHERE CONTAINS(OBJEKTUM_NEV, 'C')>0;
Can some body please help? TIA,
Tamas

Just in case any body might be interested later on, the solution was that the above CONTAINS clause filters for the C character as a stand alone entity (i.e. word). The correct where clause would have been:
WHERE CONTAINS(OBJEKTUM_NEV, 'C%')>0;

Related

Oracle query with "in clause" - how to speed up using an index?

I got a oracle query that uses an in-clause with eight given values, like:
select * from mytable a
where a.wf_type in ('value1', 'value2', 'value3', 'value4', 'value5', 'value6', 'value7', 'value8');
The table is not really big (about 3 million rows) and the query does a full table scan.
Therefore I added an index for the wf_type attribute.
But the index is not used by the query with the in-clause. If I change the query to one specific value like
select * from mytable a where a.wf_type = 'value1';
the index is used and the query runs fast.
How do I fasten the query with the in-clause? Is it possible by using an index or are there other ways?

Oracle no index for function calls in sql query

I have a table with name t(abc varchar2(50),xyz varchar2(50), ..etc) and index enabled on column abc. Oracle uses the index for userfunction(a) which takes long time. This is a dynamic query formed can have another conditions that must use index on abc so I don't want to use no_index hint.
select *from t
where
userfunction(a) = 0
and exists (select 1 from tab where t.abc='' ...etc)
and ..etc
I tried to re-write the query with nested query by moving the function to nested query, but oracle is re-writing and still executing userfunction(a) at the first and the query is taking long time.
select *from (
select *from t
where
and exists (select 1 from tab where t.abc='' ...etc)
..etc
)
userfunction(a) = 0
Also tried using WITH clause but no luck.
Any idea of oracle not to use index for user function calls or certain condition in where clause?
Your query is not logically consistent: you reference table t and tab, but only desribe t. Assuming you meant t when you wrote tab, I can't see why you need the exists clause with a sub-query. EXISTS will just return TRUE when the expression in the subselect evaluates to TRUE; so why not just use that expression in the main query on t?

Oracle fuzzy text search with wildcards

I've got a SAP Oracle database full with customer data.
In our custom CRM it is quite common to search the for customers using wildcards. In addtion to the SAP standard search, we would like to do some fuzzy text searching for names which are similar to the entered name.
Currently we're using the UTL_MATCH.EDIT_DISTANCE function to search for similar names. The only disadvantage is that it is not possible to use some wildcard patterns.
Is there any possiblity to use wildcards in combination with the UTL_MATCH.EDIT_DISTANCE function or are there different(or even better) approaches to do that?
Let's say, there are the following names in the database:
PATRICK NOR
ORVILLE ALEX
OWEN TRISTAN
OKEN TRIST
The query could look like OKEN*IST* and both OWEN TRISTAN and OKEN TRISTAN should be returned. OKEN would be a 100% match and OWEN less.
My current test-query looks like:
SELECT gp.partner, gp.bu_sort1, UTL_MATCH.edit_distance(gp.bu_sort1, ?) as edit_distance,
FROM but000 gp
WHERE UTL_MATCH.edit_distance(gp.bu_sort1, ?) < 4
This query works fine except if wildcards * are used within the search string (which is quite common).
Beware of the implications of your approach in terms of performances. Even if it "functionally" worked, with UTL_MATCH you can only filter the results obtained by an internal table scan.
What you likely need is an index on such data.
Head to Oracle Text, the text indexing capabilities of Oracle. Bear in mind that they require some effort to be put at work.
You might juggle with the fuzzy operator, but handle with care. Most oracle text features are language dependent (they take into account the English dictionary, German, etc..).
For instance
-- create and populate the table
create table xxx_names (name varchar2(100));
insert into xxx_names(name) values('PATRICK NOR');
insert into xxx_names(name) values('ORVILLE ALEX');
insert into xxx_names(name) values('OWEN TRISTAN');
insert into xxx_names(name) values('OKEN TRIST');
insert into xxx_names(name) values('OKENOR SAD');
insert into xxx_names(name) values('OKENEAR TRUST');
--create the domain index
create index xxx_names_ctx on xxx_names(name) indextype is ctxsys.context;
This query would return results that you'd probably like (input is the string "TRST")
select
SCORE(1), name
from
xxx_names n
where
CONTAINS(n.name, 'definescore(fuzzy(TRST, 1, 6, weight),relevance)', 1) > 0
;
SCORE(1) NAME
---------- --------------------
1 OWEN TRISTAN
22 OKEN TRIST
But with the input string "IST" it would likely return nothing (in my case this is what it does).
Also note that in general, inputs of less than 3 characters are considered non-matching by default.
You'll possibly get a more "predictable" outcome if you take off the "fuzzy" requirement and stick to finding rows that just "contains" the exact sequence you passed in.
In this case try using a ctxcat index, which, by the way supports some wildcards (warning: supports multi columns, but a column cannot exceed 30 chars in size!)
-- create and populate the table
--max length is 30 chars, otherwise the catsearch index can't be created
create table xxx_names (name varchar2(30));
insert into xxx_names(name) values('PATRICK NOR');
insert into xxx_names(name) values('ORVILLE ALEX');
insert into xxx_names(name) values('OWEN TRISTAN');
insert into xxx_names(name) values('OKEN TRIST');
insert into xxx_names(name) values('OKENOR SAD');
insert into xxx_names(name) values('OKENEAR TRUST');
begin
ctx_ddl.create_index_set('xxx_names_set');
ctx_ddl.add_index('xxx_names_set', 'name');
end;
/
drop index xxx_names_cat;
CREATE INDEX xxx_names_cat ON xxx_names(name) INDEXTYPE IS CTXSYS.CTXCAT
PARAMETERS ('index set xxx_names_set');
The latter, with this query would work nicely (input is "*TRIST*")
select
UTL_MATCH.edit_distance(name, 'TRIST') dist,
name
from
xxx_names
where
catsearch(name, '*TRIST*', 'order by name desc') > 0
;
DIST NAME
---------- --------------------
7 OWEN TRISTAN
5 OKEN TRIST
But with the input "*O*TRIST*" wouldn't return anything (for some reasons).
Bottom line: text indexes are probably the only way to go (for performance) but you have to fiddle quite a bit to understand all the intricacies.
References:
fuzzy search: Oracle Text CONTAINS Query Operators
catsearch : Oracle Text SQL Statements and Operators
Assuming "wildcard" means an asterisk, you want a name that matches all specified letters to rank highest, with more specified letters matching better than less, otherwise rank by edit distance similarity.
using the placeholder ? for your search term, try this:
select *
from mytable
order by case
when name like '%' || replace(?, '*', '%') || '%' then 0 - length(replace(?, '*', ''))
else 100 - UTL_MATCH.edit_distance_similarity(?, name) end
fetch first 10 rows
FYI all "like" matches have a negative number for their ordering with magnitude the number of letters specified. All like misses have a non-negative ordering number with magnitude of the percentage difference. In all cases, a lower number is a better match.

birt sql dataset with parameters sql error

I have a birt dataset for a db2 query. My query works fine without parameters with the following query...
with params as (SELECT '2014-02-16' enddate,'1' locationid FROM sysibm.sysdummy1)
select
t.registerid
from (
select
...
FROM params, mytable sos
WHERE sos.locationid=params.locationid
AND sos.repositorytype ='xxx'
AND sos.repositoryaccountability='xxx'
AND sos.terminalid='xxx'
AND DATE(sos.balanceDate) between date(params.enddate)-6 DAY and date(params.enddate)
GROUP BY sos.terminalid,sos.balancedate,params.enddate) t
GROUP BY
t.registerid
WITH UR
But when I change the top line to ...
with params as (SELECT ? enddate,? locationid FROM sysibm.sysdummy1)
And make the two input paramters of string datatype I get db2 errors sqlcode -418. But i know that it is not my querty because my query works.
What is the right way for me to set up the parameters so there is no error?
thanks
I'm not familiar with DB2 programming, but on Oracle the ? works anywhere in the query.
Have you looked at http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=%2Fcom.ibm.db2z9.doc.codes%2Fsrc%2Ftpc%2Fn418.htm?
Seems that on DB2 it's a bit more complicated and you should use "typed parameter markers".
The doc says:
Typed parameter marker
A parameter marker that is specified with its target data type. A typed parameter marker has the general form:
CAST(? AS data-type)
This invocation of a CAST specification is a "promise" that the data type of the parameter at run time will be of the data type that is specified or some data type that is assignable to the specified data type.
Apart from that, always assure that your date strings are in the format that the DB expects, and use explicit format masks in the date function, like this:
with params as (
SELECT cast (? as varchar(10)) enddate,
cast (? as varchar2(80)) locationid
FROM sysibm.sysdummy1
)
select
...
from params, ...
where ...
AND DATE(sos.balanceDate) between date(XXX(params.enddate))-6 DAY and date(XXX(params.enddate))
...
Unfortunately I cannot tell you how the XXX function should look on DB2.
On Oracle, an example would be
to_date('2014-02-18', 'YYYY-MM-DD')
On DB2, see Converting a string to a date in DB2
In addition to hvb answer, i see two options:
Option 1 you could use a DB2 stored procedure instead of a plain SQL query. Thus there won't be these limitations you face to, due to JDBC query parameters.
Option 2, we should be able to remove the first line of the query "with params as" and replace it with question marks within the query:
select
t.registerid
from (
select
sos.terminalid,sos.balancedate,max(sos.balanceDate) as maxdate
FROM params, mytable sos
WHERE sos.locationid=?
AND sos.repositorytype ='xxx'
AND sos.repositoryaccountability='xxx'
AND sos.terminalid='xxx'
AND DATE(sos.balanceDate) between date(?)-6 DAY and date(?)
GROUP BY sos.terminalid,sos.balancedate) t
GROUP BY
t.registerid
A minor drawback is, this time we need to declare 3 dataset parameters in BIRT instead of 2. More nasty, i removed params.endDate from "group by" and replaced it with "max(sos.balanceDate)" in select clause. This is very near but not strictly equivalent. If this is not acceptable in your context, a stored procedure might be the best option.

SSRS - T-SQL - Concatenate multiple rows

I have T-SQL query that joins multiple tables. I am using that in SSRS as Dataset query. I am only selecting two columns, ID and Names. I have three records with same "ID" values but three different "Names" values. In SSRS, I am getting the first "Names" value and I need to concatonate all three values with same ID and have it in one cell on a table.
How would I go about doing that?
I am using lookup to combine cube + sql
Pulling ID straight from a table but using Case statement for Names to define alias.
You can accomplish this in TSQL either using PIVOT to get them as separate columns which you can then combine in the report cell, or you can use one of these concatenation methods to get all the names in one column.
For example, you can do this:
SELECT SomeTableA.Id,
STUFF(
(SELECT ',' + SomeTableB.Names AS [text()]
FROM SomeTable SomeTableB
WHERE SomeTableB.Id = SomeTableA.Id
FOR XML PATH('')), 1, 1, '' )
AS ConcatenatedNames
FROM SomeTable SomeTableA
INNER JOIN AnotherTable
ON SomeTableA.Id = AnotherTable.SomeId
...

Resources