I have a table Com_Main which contains column CompanyName nvarchar(250). It has average length of 19, max length = 250.
To improve performance I want to add a computed column left20_CompanyName which holds the first 20 characters of CompanyName:
alter table Com_main
add left20_CompanyName as LEFT(CompanyName, 20) PERSISTED
Then I create Index on this column:
create index ix_com_main_left20CompanyName
on Com_main (LEFT20_CompanyName)
So when I use
select CompanyName from Com_Main
where LEFT20_CompanyName LIKE '122%'
it uses this nonclustered index, but when the query is like:
select CompanyName from Com_Main
where CompanyName LIKE '122%'
It uses full table scan, and don't use this index.
So the question:
Is it possible to make SQL Server use this index on computable column in last query?
No. MySQL supports partial indexing of varchar columns but MS SQL Server does not.
You might be able to speed up table scans through partitioning but I don't know how smart SQL Server is in this regard.
I don't think the SQL query engine would realize that the LEFT20_CompanyName column maps over so neatly to the CompanyName column - since a computed column could use virtually any formula, there's no way for it to know that the index on that other column is actually useful in this case.
Why not just create the index on the CompanyName column? So what if a few values in that field are longer than average? If you create it directly on the column and avoid the computed column altogether, I think it will use the index in both cases.
Maybe I'm missing something, but I'm not sure what you're trying to gain by doing the computed column on only the first 20 characters.
Related
I have table A(id,name,code)
I have sql statement:
Select * from A where upper(code || name) like upper('%<search text>%');
How to create an index so that the following statement has an index?
Question for two option: table partitioned, and table not partitioned
Thanks & BR
Do you have a performance issue or is this just a hypothetical question?
An index is unlikely to help with this example: a full table scan will probably be the quickest solution. Why? Your table has 3 columns. The best index would be one that avoided looking in the table at all e.g.
create index ai on a (code, name, id);
But that needs to contain all the same data as the table plus a ROWID for each table row - so it is going to be bigger than the table and take longer to scan. You could try putting the columns in the index with the least selective first and using compression:
create index ai on a (code, name, id) compress;
Now the index may be smaller than the table - it depends on how selective the code and name columns are. If it is small enough, the optimizer might decide to use it instead of the table. It still contains all the IDs and ROWIDs so the reduction in size probably won't be dramatic. In the test case I set up the compressed index is about half the size of the table, yet Explain Plan shows the query has a higher cost if I use a hint to force it to use the index - maybe due to overheads of compression, I don't know.
You could look into Oracle Text and the CONTAINS expression - but then you would be writing a different query, not using LIKE.
I have a large table of 7 column in Oracle 11G. Total size of the table is more than 3GB and total row in this table is 1876823. Query we are using
select doc_mstr_id from index_mstr where page_con1 like('%sachin%') it is taking almost a minute. please help me to optimize the query as well as proper indexing for this table. Please let me know if partitioned is required for this table.
Below are the column description
INDEX_MSTR_ID NUMBER
DOC_MSTR_ID NUMBER
PAGE_NO NUMBER
PAGE_PART NUMBER
PAGE_CON1 VARCHAR2(4000)
FILE_MODIFIED_DATE DATE
CREATED_DATE DATE
This query is always going to result in a full table scan. Your only filter cannot use a B-TREE index, due to the leading wildcard:
where page_con1 like('%sachin%')
If you want to do lots of queries of this nature you need to build a Text index on that column. From its datatype page_con1 appears to hold text fragments rather than full documents so you should use a CTXCAT index. This type of index has the advantage of being transactional, rather than requiring background maintenance. Find out more.
Your query would then look like this:
select doc_mstr_id from index_mstr
WHERE CATSEARCH(page_con1, 'sachin') > 0;
I have a massive table in which I can't do any more partitioning or sub-partitioning, nor am allowed to do any alter. I want to query its records by batches, and thought a good way would be using the last two digits from the account numbers (wouldn't have any other field splitting records as evenly).
I guess I'd need to at least index that somehow (remember I can't alter table to add a virtual column either).
Is there any kind of index to be used in such situation?
I am using Oracle 11gR2
You can use function based index:
create index two_digits_idx on table_name (substr(account_number, -2));
This index will work only in queries like that:
select ...
from table_name t ...
where substr(account_number, -2) = '25' -- or any other two digits
For using index, you need to use in a query the same expression like in an index.
I have a table that I expect to get 7 million records a month on a pretty wide table. A small portion of these records are expected to be flagged as "problem" records.
What is the best way to implement the table to locate these records in an efficient way?
I'm new to Oracle, but is a materialized view an valid option? Are there such things in Oracle such as indexed views or is this potentially really the same thing?
Most of the reporting is by month, so partitioning by month seems like an option, but a "problem" record may be lingering for several months theorectically. Otherwise, the reporting shuold be mostly for the current month. Would you expect that querying across all month partitions to locate any problem record would cause significant performance issues compared to usinga single table?
Your general thoughts of where to start would be appreciated. I realize I need to read up and I'll do that but I wanted to get the community thought first to make sure I read the right stuff.
One more thought: The primary key is a GUID varchar2(36). In order of magnitude, how much of a performance hit would you expect this to be relative to using a NUMBER data type PK? This worries me but it is out of my control.
It depends what you mean by "flagged", but it sounds to me like you would benefit from a simple index, function based index, or an indexed virtual column.
In all cases you should be careful to ensure that all the index columns are NULL for rows that do not need to be flagged. This way your index will contain only the rows that are flagged (Oracle does not - by default - index rows in B-Tree indexes where all index column values are NULL).
Your primary key being a VARCHAR2 GUID should make no difference, at least with regards to the specific flagging of rows in this question, indexes will point to rows via Oracle internal ROWIDs.
Indexes support partitioning, so if your data is already partitioned, your index could be set to match.
Simple column index method
If you can dictate how the flagging works, or the column already exists, then I would simply add an index to it like so:
CREATE INDEX my_table_problems_idx ON my_table (problem_flag)
/
Function-based index method
If the data model is fixed / there is no flag column, then you can create a function-based index assuming that you have all the information you need in the target table. For example:
CREATE INDEX my_table_problems_fnidx ON my_table (
CASE
WHEN amount > 100 THEN 'Y'
ELSE NULL
END
)
/
Now if you use the same logic in your SELECT statement, you should find that it uses the index to efficiently match rows.
SELECT *
FROM my_table
WHERE CASE
WHEN amount > 100 THEN 'Y'
ELSE NULL
END IS NOT NULL
/
This is a bit clunky though, and it requires you to use the same logic in queries as the index definition. Not great. You could use a view to mask this, but you're still duplicating logic in at least two places.
Indexed virtual column
In my opinion, this is the best way to do it if you are computing the value dynamically (available from 11g onwards):
ALTER TABLE my_table
ADD virtual_problem_flag VARCHAR2(1) AS (
CASE
WHEN amount > 100 THEN 'Y'
ELSE NULL
END
)
/
CREATE INDEX my_table_problems_idx ON my_table (virtual_problem_flag)
/
Now you can just query the virtual column as if it were a real column, i.e.
SELECT *
FROM my_table
WHERE virtual_problem_flag = 'Y'
/
This will use the index and puts the function-based logic into a single place.
Create a new table with just the pks of the problem rows.
I'm using a function-based index with a user-defined function for the first time, and have stumbled across a performance problem when the index can't be used.
Internally, a function-based index seems to generate a hidden table column (of type varchar2(4000), since my function returns a varchar2), and indexes that. That works fine when the index is used, but sometimes we have to do a full table scan using the function as a filter, and in that case I see a performance degradation by a factor of 6. Seems in that case, Oracle does not use the hidden column, but recomputes the function for each row, make the query CPU-bound instead of IO-bound.
Is there a way to make Oracle use that hidden column also for filtering? I wonder if I'm missing some rewrite options or something along those lines.
If not, I'll have to define the column myself and using a trigger to keep it up to date. I'd prefer using the function-based index for transparency and easier maintanance.
Which version of Oracle are you using? If it's 11g you should try using a virtual column. This is a column whose value is derived from an expression or a literal. They're defined as part of the table, so they have a visibility in a table DESC (unlike a function-based index). We can build indexes on virtual columns. And they are maintained automatically, without the need for a trigger.
So you can add a virtual column to your table using the same expression as your function based index. Perhaps like this:
create table t23
(id number
, col_a varchar2(10)
, vcol_a as (upper(substr(col_a, 1, 1)))
)
/
Note that we cannot insert or update a virtual column. So you need to specify the projection of the insert statement:
insert into t23 (id, col_a) values (1, 'this is a test');
Then you can build a regular index on the virtual column:
create index t23_vc_i on t23(vcol_a)
/
Don't forget to drop your function based index!
There is no general way a function-based index can be used in a table scan.
The assumption I made in my question, namely "Internally, a function-based index seems to generate a hidden table column ...", is simply wrong: the results of the function are not stored in a table column, but only in the index.
So, unless there is a way to access the index when performing the scan (the only way I can think of is if it's a combined index starting with the key column(s)), the precomputed function result can't be used.
The 11g "virtual column" feature does not help either, as the column is not stored in the table, but computed on-the-fly, similar to using the function in a view.
In summary: if you can't rule out table scans, and your function call is expensive (slow), use a real column in combination with a "before insert or update" trigger. A function-based index won't do.
(Note: Added this answer because I did not want to let this question stand as unanswered. The credit for the answer belongs to thilo, who pointed out that the column is never materialized).