efficient searching in database - oracle

I have a database containing user details. I don't have separate columns for 'firstname' and 'lastname'. I have used only a single column: 'name'. My query is like this:
ps_details=con.prepareStatement("select * from student_details where name=?");
ps_details.setString(1,name);
The problem is:
In db the name is stored in UPPERCASE. So when the user types his/her name in lowercase or mixed-case, the search is unsuccessful. What is the efficient solution for this problem?
Further, what if 'name' are stored in db in lower-case, mixed-case as well as uppercase, i.e., if the case is not known before hand. I also want to use like clause
I am using Oracle 11g Express Editon.

If you really intend to do an exact match (i.e. the user needs to enter their entire name in order to do the search rather than just entering a portion of their name), you can wrap both sides of the expression in either the UPPER or the LOWER function.
SELECT *
FROM student_details
WHERE upper(name) = upper(?)
If you do that, however, Oracle won't be able to use an index on name so it will have to scan the entire table. That's not particularly efficient. You can address that issue by creating a function based index on upper(name)
CREATE INDEX idx_upper_name
ON student_details( upper(name) );
In reality, though, I suspect that you don't want to do a wild-card based match. You almost certainly want to allow the user to enter some part of the name and to return the results that contain that string. So if I were a student, you probably want to allow the user to search for either 'Justin' or 'Cave' or 'Justin Cave' or 'Just' and have all those searches return my row (along with whatever other rows match, of course). If you want to do that, the simplest approach would be to use the LIKE function along with the % wildcard
SELECT *
FROM student_details
WHERE upper(name) like '%' || upper(?) || '%'
will return all the rows where the input string is found anywhere in the text you're searching. But that realistically takes us back to nearly square one with respect to performance-- having the leading wildcard is going to make it very difficult for Oracle to benefit from using the index we defined on UPPER(name). This is why most people would store first and last name separately. That allows them to do something like
SELECT *
FROM student_details
WHERE upper(first_name) like upper(?) || '%'
OR upper(last_name) like upper(?) || '%'
This allows them to return my row whether the user searches for 'Justin' or 'Cave' or 'Just' and regardless of the casing of the data in the database or the data that is entered in the search. And it is able to use appropriate function-based indexes on the data.

UPPER - the upper function converts all letters in the specified string to uppercase. Use it on both the variable (user input) and table column values, convert them both to the same case for comparison to handle both problems. (You could also use lower - the idea is to convert both to the same case for comparison). *EDITED FOR LIKE with only the postceding wildcard *
select * from student_details where UPPER(name) like UPPER(?) || '%'
This you have to play with, but I think you can also do the following with the wildcard.
ps_details=con.prepareStatement("select * from student_details where UPPER(name) like UPPER(?)");
ps_details.setString(1,name%);

Try using UPPER() on your input.

Related

conditional join (Oracle)

We have front-end app where user enters customer and/or PO to retrieve data.
For instance, if user want to retrieve all POs for a customer, he will enter '%' in PO field.
If user wants to retrieve all data, he will enter '%' in every field.
I am trying this, but it does not work
SELECT *
FROM PO_INFO
WHERE customer_id = case when '%' then customer_id else 'Macys' end
AND purchase_order = case when '%' then purchase_order else '79124' end
What am I missing?
You should not (some would say must not) just plug the user-entered search values into your SQL query as literals. That is,
AND purchase_order = case when '%' then purchase_order else '79124' end
... is not going to perform or scale well because every single search looks to Oracle like a brand new SQL query that has to get parsed and optimized (i.e., "hard parsed). This is an expensive process that also requires a lot of latches, meaning multiple users trying to run searches at the same time will have to wait for each other.
Instead, you should construct your SQL using bind variables. So,
AND purchase_order = :1 -- or something. We'll discuss the exact logic later...
That :1 is a bind variable, a placeholder for a value your code will supply when it submits the query. Now, everyone doing a search is using the same query (just each supplying different values for the bind variables). No more excessive hard parsing and performance disaster averted.
But, here is the next problem. One query for all means it only gets hard parse one time (good) but it also means everyone runs using the same execution plan. But in a search like this, one size does not fit all. Suppose the execution plan Oracle comes up with uses an index on column 'ABC'. If a user does not supply a bind variable value for column 'ABC', that execution plan will still be followed, but with terrible results.
So, what we want really is one SQL for each set of bind variables that have values or don't, but not one SQL for each distinct set of literal search values.
Build your SQL in code by starting with this string:
SELECT * FROM PO_INFO WHERE 1=1
Then, for each search condition add this (if the value is %)
AND (:1 IS NULL) -- and pass `NULL`, not "%" as the value for :1
(Aside: the reason for this condition, which is essentially NULL IS NULL is to make to so the number and order of the bind variables that have to be passed in is always the same, regardless of what the end user does or does not give you a value for. This makes it much easier to submit the SQL in some languages, like PL/SQL).
If the search condition is not %, add this:
AND (customer_id /* or whatever column */ = :1) -- and pass the user-entered value
So, for example, if the user specified values for customer_id and po_date but not purchase_order, your final SQL might look like:
SELECT *
FROM PO_INFO
WHERE 1=1
AND customer_id = :1
AND po_date := 2
AND :3 IS NULL -- this is purchase order and :3 you will pass as null
If you do all this, you'll get the least amount of hard-parsing and the best execution plan for each search.

Why does "UPDATE Users SET Password=? WHERE Username=?" give a syntax error? [duplicate]

One of my columns is called from. I can't change the name because I didn't make it.
Am I allowed to do something like SELECT from FROM TableName or is there a special syntax to avoid the SQL Server being confused?
Wrap the column name in brackets like so, from becomes [from].
select [from] from table;
It is also possible to use the following (useful when querying multiple tables):
select table.[from] from table;
If it had been in PostgreSQL, use double quotes around the name, like:
select "from" from "table";
Note: Internally PostgreSQL automatically converts all unquoted commands and parameters to lower case. That have the effect that commands and identifiers aren't case sensitive. sEleCt * from tAblE; is interpreted as select * from table;. However, parameters inside double quotes are used as is, and therefore ARE case sensitive: select * from "table"; and select * from "Table"; gets the result from two different tables.
These are the two ways to do it:
Use back quote as here:
SELECT `from` FROM TableName
You can mention with table name as:
SELECT TableName.from FROM TableName
While you are doing it - alias it as something else (or better yet, use a view or an SP and deprecate the old direct access method).
SELECT [from] AS TransferFrom -- Or something else more suitable
FROM TableName
Your question seems to be well answered here, but I just want to add one more comment to this subject.
Those designing the database should be well aware of the reserved keywords and avoid using them. If you discover someone using it, inform them about it (in a polite way). The keyword here is reserved word.
More information:
"Reserved keywords should not be used
as object names. Databases upgraded
from earlier versions of SQL Server
may contain identifiers that include
words not reserved in the earlier
version, but that are reserved words
for the current version of SQL Server.
You can refer to the object by using
delimited identifiers until the name
can be changed."
http://msdn.microsoft.com/en-us/library/ms176027.aspx
and
"If your database does contain names
that match reserved keywords, you must
use delimited identifiers when you
refer to those objects. For more
information, see Identifiers (DMX)."
http://msdn.microsoft.com/en-us/library/ms132178.aspx
In Apache Drill, use backquotes:
select `from` from table;
If you ARE using SQL Server, you can just simply wrap the square brackets around the column or table name.
select [select]
from [table]
I have also faced this issue.
And the solution for this is to put [Column_Name] like this in the query.
string query= "Select [Name],[Email] from Person";
So it will work perfectly well.
Hi I work on Teradata systems that is completely ANSI compliant. Use double quotes " " to name such columns.
E.g. type is a SQL reserved keyword, and when used within quotes, type is treated as a user specified name.
See below code example:
CREATE TABLE alpha1
AS
(
SEL
product1
type_of_product AS "type"
FROM beta1
) WITH DATA
PRIMARY INDEX (product1)
--type is a SQL reserved keyword
TYPE
--see? now to retrieve the column you would use:
SEL "type" FROM alpha1
I ran in the same issue when trying to update a column which name was a keyword. The solution above didn't help me. I solved it out by simply specifying the name of the table like this:
UPDATE `survey`
SET survey.values='yes,no'
WHERE (question='Did you agree?')
The following will work perfectly:
SELECT DISTINCT table.from AS a FROM table
Some solid answers—but the most-upvoted one is parochial, only dealing with SQL Server. In summary:
If you have source control, the best solution is to stick to the rules, and avoid using reserved words. This list has been around for ages, and covers most of the peculiarities. One tip is that reserved words are rarely plural—so you're usually safe using plural names. Exceptions are DIAGNOSTICS, SCHEMAS, OCTETS, OFFSETS, OPTIONS, VALUES, PARAMETERS, PRIVILEGES and also verb-like words that also appear plural: OVERLAPS, READS, RETURNS, TRANSFORMS.
Many of us don't have the luxury of changing the field names. There, you'll need to know the details of the RDBM you're accessing:
For SQL Server use [square_braces] around the name. This works in an ODBC connection too.
For MySQL use `back_ticks`.
Postgres, Oracle and several other RDBMs will apparently allow "double_quotes" to be used.
Dotting the offending word onto the table name may also work.
You can put your column name in bracket like:
Select [from] from < ur_tablename>
Or
Put in a temprary table then use as you like.
Example:
Declare #temp_table table(temp_from varchar(max))
Insert into #temp_table
Select * from your_tablename
Here I just assume that your_tablename contains only one column (i.e. from).
In MySQL, alternatively to using back quotes (`), you can use the UI to alter column names. Right click the table > Alter table > Edit the column name that contains sql keyword > Commit.
select [from] from <table>
As a note, the above does not work in MySQL
Judging from the answers here and my own experience. The only acceptable answer, if you're planning on being portable is don't use SQL keywords for table, column, or other names.
All these answers work in the various databases but apparently a lot don't support the ANSI solution.
Simple solution
Lets say the column name is from ; So the column name in query can be referred by table alias
Select * from user u where u.from="US"
In Oracle SQL Developer, pl/sql you can do this with double quotes but if you use double quotes you must type the column names in upper case. For example, SELECT "FROM" FROM MY_TABLE

SELECT Roughly equals

I am transfering from access to oracle and couldn't find a roughly equals function in oracle. Does such a thing exist?
I am referring to something like:
SELECT * FROM Table WHERE name ='*nswer is thi*';
Use LIKE:
SELECT * FROM Table WHERE name LIKE '%nswer is thi%';
Explanation:
This query will select records which contains "nswer is thi" anywhere in the field name.
For example:
name
--------------------
answer is this
blahnswer is thiblah
The LIKE conditions specify a test involving pattern matching. Whereas the equality operator (=) exactly matches one character value to another
Read more here.

Select distinct results, when using with() operator in Yii

How can I select only distinct records, from relational table, when using with() operator in Yii?
I'm getting my models (records) like that:
$probe = Probes::model()->with(array
(
'user',
'results',
'results.answer',
'survey',
'survey.questions',
'survey.questions.question',
'survey.questions.question.answers',
'manager'
))->findByPk($id);
I want to make sure, that survey.questions relation returns only distinct records. But it seems, that I don't see any way to achieve this (or I'm blind / not educated enough).
When giving relational table name / alias as array:
'results.question'=>array('alias'=>'results_question'),
the distinct key is not among those, that can be used in such array (as modifier).
I tried very ugly, bumpy way of changing select from default * to DISTINCT *:
'survey.questions'=>array('select'=>'distinct'),
But this has (of course?) failed:
Active record "SurveysQuestions" is trying to select an invalid column "distinct". Note, the column must exist in the table or be an expression with alias.
How can I achieve this (seemed so obvious and easy), if it is possible at all this way (using with())? If not, then -- please, advice how to get distinct records in relational table any way (other than manually filtering results using foreach, what I'm doing right now, and what is ugly).
You could set CDbCriteria::distinct to true:
'survey.questions'=>array('distinct'=>true),

oracle database contains with "in%"

While working with one of the oracle query, when i am firing a select command with where condition something like:
select * from table_name where contains(address,'"IN%" AND "BÖDEN%"') >0
No results are shown as part of this query though results are there in database.
Again if i introduce one space between "IN" and "%" i am getting results in proper:
select * from table_name where contains(address,'"IN %" AND "BÖDEN%"') >0
There is one more scenario :
Same type of query but if "IN" is replaced with "IM" the corresponding results are displayed.
select * from table_name where contains(address,'"IM%" AND "BÖDEN%"') >0
or
select * from table_name where contains(address,'"IM %" AND "BÖDEN%"') >0
Both above queries are giving same results. which is quite obvious too.
Hence i suspect there is something speacial with "IN" in oracle.
Can anybody suggest me proper reason for this problem and any resolution for the problem ?
The IN keyword is a stop word in the default Stoplist which is used to specify the words which are not be indexed in Oracle Text.
You need to remove it from the stop list in order to search such stop words.
Check the link for Stoplist
Also please find the other default Stop words for English in oracle for your reference Default Stop words

Resources