MySQL MyISAM indexing a text column - sql

I have a slow performing query on my table. It has a where clause such as:
where supplier= 'Microsoft'
The column type is text. In phpmyadmin I looked to see if I could add an index to the table but the option is disabled. Does this mean that you can not index a text column? Does this mean that every update query like this is performing a full table scan?
Would then the best thing to do is separate the column into it's own table and place an ID in the current table then place an index on that? Would this potentially speed up the query?

You need to add a prefix length to the index. Have a look at Column Indexes docs
The following creates an index on the first 50 bytes of supplier field:
mysql> create index supplier_ix on t(supplier(50));
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
But maybe you should rethink the datatype of supplier? Judging from the name, it doesn't sound like a typical text field...

You should do a check on
select max(length(supplier)) from the_table;
If the length is less than 255, you can (and you should) convert it to varchar(255) and built an index on it
Choosing a right data type is more important.
If the length is long, built an index on limited length will help.

Did I understand you right? It's a TEXT column? As in the type that corresponds to BLOB? Might I advise considering a VARCHAR for this column?

Related

use string value like numeric value in Oracle

I have a condition in my oracle query:
AND a.ACCARDACNT > '0880080200000006' and a.ACCARDACNT < '0880080200001000'
type of ACCARDACNT column in table is varchar and indexed but in that condition I want to use it as number. When I execute this query, the execution plan shows that CBO can use index and scan the table by index.
is it true?
I want to use and compare them as number and also an indexed be used. Is there any solution?
If it is guaranteed that all ACCARDACNT are numbers, then just use
and to_number(a.accardacnt) > 880080200000006 and a.accardacnt < 880080200001000;
This makes sure that the numbers are no compared as strings where '2' > '10', because looking at the first characters '2' is greater than '1'.
(In case of decimal numbers, make sure that the the decimal separator stored in the strings matches the current session settings.)
If you want to provide an index for this, use this function index:
create index idx_accardacnt on mytable( to_number(accardacnt) );
or a composite index containing to_number(accardacnt). As the execution plan for the strings query showed an index to be used, the same should be true for the numeric comparision and function index. (Remember a DBMS is free to use the provided indexes or not. We are simply offering them, but the DBMS knows best whether it makes sense to use them in a query or not.)
Think you cannot use numeric comparison and index together.
the execution plan shows that CBO can use index
There is a chance that Full Index Scan is used here, so it just a table scan but with less columns.
Possible approach is to convert numbers to fixed length strings with leading zeros and then use ones in comparison. In this case the index will be used.
Your current query should be able to use an index. But the problem is that you are comaparing text but expecting it to sort numerically. It may not, in general, because text sorts lexicographically in SQL (i.e. in dictionary order). So, to get the correct sorting behavior you will have to cast ACCARDACNT to a number:
AND CAST(LTRIM(a.ACCARDACNT, '0') AS FLOAT) BETWEEN 880080200000007 AND 880080200000999
Another option still would be a computed column:
alter table mytable add accardacnt_num as (to_number(accardacnt));
and provide one or more indexes containing this column:
create index idx_accardacnt_num on mytable(accardacnt_num);
Then existing code continues working, but new queries can benefit from the numeric column:
and a.accardacnt_num > 880080200000006 and a.accardacnt_num < 880080200001000;
I think the following logic does what you might really want:
a.ACCARDACNT > '0880080200000006' and
a.ACCARDACNT < '0880080200001000' and
length(ACCARDACNT) = 16
In addition, this can use an index on the column, if an appropriate one is available.
This would not be correct if you wanted the 15-character account number '880080200000060' to match your criteria. My guess is that you do not want this.
You have to create a function index on column : accardacnt and
create index idx_fn_accardant on table_name( to_number(accardacnt) );
convert it into number in the where clause of the query query :
where to_number(ACCARDACNT) > 0880080200000006 and to_number(ACCARDACNT) < 0880080200001000

Index on VARCHAR column

I have a table of 32,589 rows, and one of the columns is called 'Location' and is a Varchar(40) column type. The column holds a location, which is actually a suburb, all uppercase text.
A function that uses this table does a:
IF EXISTS(SELECT * FROM MyTable WHERE Location = 'A Suburb')
...
Would it be beneficial to add an index to this column, for efficiency? This is more a read-only table, so not much edits or inserts except for maintanance.
Without an index SQL Server will have to perform a table scan to find the first instance of the location you're looking for. You might get lucky and have the value be in one of the first few rows, but it could be at row 32,000, which would be a waste of time. Adding an index only takes a few second and you'll probably see a big performance gain.
I concur with #Brian Shamblen answer.
Also, try using TOP 1 in the inner select
IF EXISTS(SELECT TOP 1 * FROM MyTable WHERE Location = 'A Suburb')
You don't have to select all the records matching your criteria for EXISTS, one is enough.
An opportunistic approach to performance tuning is usually a bad idea.
To answer the specific question - if your function is using location in a where clause, and the table has more than a few hundred rows, and the values in the location column are not all identical, creating an index will speed up your function.
Whether you notice any difference is hard to say - there may be much bigger performance problems lurking in the database, and you might be fixing the wrong problem.

How to create an index for a string column in sql?

I have a table with 3 columns: a list id, name and numeric value.
The goal is to use the table to retrieve and update the numeric value for a name in various lists.
The problem is that sql refuses to create an index with the name column because it's a string column with variable length.
Without an index selecting with a name will be inefficient, and the option of using a static length text column will be wasting a lot of storage space as names can be fairly long.
What is the best way to build this table and it's indexes?
(running sql server 2008)
If your string is longer than 900 bytes, then it can't be an index key, regardless of whether it is variable or fixed length.
One idea would be to at least make seeks more selective by adding a computed column. e.g.
CREATE TABLE dbo.Strings
(
-- other columns,
WholeString VARCHAR(4000),
Substring AS (CONVERT(VARCHAR(10), WholeString) PERSISTED
);
CREATE INDEX ss ON dbo.Strings(Substring);
Now when searching for a row to update, you can say:
WHERE s.Substring = LEFT(#string, 10)
AND s.WholeString = #string;
This will at least help the optimizer narrow its search down to the index pages where the exact match is most likely to live. You may want to experiment with that length as it depends on how many similar strings you have and what will best help the optimizer weed out a single page. You may also want to experiment with including some or all of the other columns in the ss index, with or without using the INCLUDE clause (whether this is useful will vary greatly on various factors such as what else your update query does, read/write ratio, etc).
A regular index can't be created on ntext or text columns (i guess your name column is of that type, or (n)varchar longer than 900 bytes). You can create full-text index on that column type.

Expain the result of "explain" query in mysql

I am using indexing for mysql tables.
My query was like this
EXPLAIN SELECT * FROM `logs` WHERE userId =288 AND dateTime BETWEEN '2010-08-01' AND '2010-08-27'
I have indexing on field userId for this table logs,
and the result of explain query is like below.
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE logs ref userId userId 4 const 49560 Using where
The question is "My indexing is really useful or not?"...
Thanks in advance
#fastmultiplication
I thought that indexing on both this field might increase load on mysql as there will be lot of entries with unique (userId and dateTime).
I have tried adding indexing on both userId_dateTime and the result is
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE logs ref userId_dateTime userId_dateTime 4 const 63455 Using where
Your query is using indexes, and yes, they are useful. You might find the following doc pages useful:
EXPLAIN Output Format
How MySQL Uses Indexes
Multiple-Column Indexes
Also:
Multiple column index vs multiple indexes
MySQL will usually use the index that returns the smallest number of rows. In your first example, MySQL is using the userId index to narrow down the number of rows to 49560. That means that userId does not contain unique values (if it did, you wouldn't need the date range condition). As there is no index on the dateTime column, it then has to scan each row to find the ones that meet your date range criteria.
In your second example, you appear to have created a compound (multiple-column) index on userId and dateTime. In this case, it appears as though MySQL is not able to use the latter half of the index for the BETWEEN clause—I'm not sure why. It may be worth trying it with two separate indexes, rather than a multiple-column index. You may also want to try replacing BETWEEN with:
'2010-08-01' >= AND <= '2010-08-27'
This should be identical, but see the following bug report, which may affect your version of MySQL:
Optimizer does not use index for BETWEEN in a JOIN condition
From the 'rows' field it looks like MySQL still estimates it will have to look at a lot of rows.
You should try adding an index to the dateTime field, too.
And for this particular query, maybe an index on both of the fields.
alter table logs add index user_datetime (userId,dateTime);
How many rows should the query return? And how fast is the query running?
It looks to me like a pretty simple query, which is using the correct index, so if it is slow for some reason, it is probably because it has to actually return a lot of data. If you are not actually interested in all the rows, you can use LIMIT to get less.

Adding fields to optimize MySQL queries

I have a MySQL table with 3 fields:
Location
Variable
Value
I frequently use the following query:
SELECT *
FROM Table
WHERE Location = '$Location'
AND Variable = '$Variable'
ORDER BY Location, Variable
I have over a million rows in my table and queries are somewhat slow. Would it increase query speed if I added a field VariableLocation, which is the Variable and the Location combined? I would be able to change the query to:
SELECT *
FROM Table
WHERE VariableLocation = '$Location$Variable'
ORDER BY VariableLocation
I would add a covering index, for columns location and variable:
ALTER TABLE
ADD INDEX (variable, location);
...though if the variable & location pairs are unique, they should be the primary key.
Combining the columns will likely cause more grief than it's worth. For example, if you need to pull out records by location or variable only, you'd have to substring the values in a subquery.
Try adding an index which covers the two fields you should then still get a performance boost but also keep your data understandable because it wouldn't seem like the two columns should be combine but you are just doing it to get performance.
I would advise against combining the fields. Instead, create an index that covers both fields in the same order as your ORDER BY clause:
ALTER TABLE tablename ADD INDEX (location, variable);
Combined indices and keys are only used in queries that involve all fields of the index or a subset of these fields read from left to right. Or in other words: If you use location in a WHERE condition, this index would be used, but ordering by variable would not use the index.
When trying to optimize queries, the EXPLAIN command is quite helpful: EXPLAIN in mysql docs
Correction Update:
Courtesy: #paxdiablo:
A column in the table will make no difference. All you need is an index over both columns and the MySQL engine will use that. Adding a column in the table is actually worse than that since it breaks 3NF and wastes space. See http://dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html which states: SELECT * FROM tbl_name WHERE col1=val1 AND col2=val2; If a multiple-column index exists on col1 and col2, the appropriate rows can be fetched directly.