Query runs faster without an index. Why? - mysql

I have two tables. One of those tables has this schema:
CREATE TABLE `object_master_70974_` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_object` int(10) unsigned NOT NULL DEFAULT '0',
`id_master` int(10) unsigned NOT NULL DEFAULT '0',
`id_slave` int(10) unsigned NOT NULL DEFAULT '0',
`id_field` bigint(20) unsigned NOT NULL DEFAULT '0',
`id_slave_field` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `id_object` (`id_object`,`id_master`,`id_slave`,`id_field`,`id_slave_field`),
KEY `id_object_2` (`id_object`,`id_master`,`id_field`,`id_slave_field`),
KEY `id_object_3` (`id_object`,`id_slave`,`id_field`),
KEY `id_object_4` (`id_object`,`id_slave_field`),
KEY `id_object_5` (`id_object`,`id_master`,`id_slave`,`id_field`),
KEY `id_object_6` (`id_object`,`id_master`,`id_slave`,`id_slave_field`),
KEY `id_master` (`id_master`,`id_slave_field`),
KEY `id_object_7` (`id_object`,`id_field`)
) ENGINE=InnoDB AUTO_INCREMENT=17827 DEFAULT CHARSET=utf8;
As you can see, there is an overlapping index KEY id_object_5 (id_object,id_master,id_slave,id_field) and there is no index that would cover these three fields: id_object, id_master, id_field. However, when I run these two queries:
SELECT f1.id
FROM object_70974_ f1
LEFT JOIN object_master_70974_ mss0 ON mss0.id_object IN (70974,71759)
AND mss0.id_master = 71100 AND mss0.id_slave = 70912 AND mss0.id_field = f1.id
and
SELECT f1.id
FROM object_70974_ f1
LEFT JOIN object_master_70974_ mss0 ON mss0.id_object IN (70974,71759)
AND mss0.id_master = 71100 AND mss0.id_field = f1.id
they both return the same number of rows (since in fact id_slave field does not really matter) - 3530, however, the first query is slower than the second query by one second - 8 and 7 seconds respectively. So, I guess I have to ask two questions - 1) why does the second query run faster, even though it does not use index and 2) why does the first query run so slowly and why does not it use an index (obviously). In short, what the heck is going on?
EDIT
This is the result of EXPLAIN command (identical for both queries):
"id" "select_type" "table" "type" "possible_keys" "key" "key_len" "ref" "rows" "Extra"
"1" "SIMPLE" "f1" "index" \N "attr_80420_" "5" \N "3340" "Using index"
"1" "SIMPLE" "mss0" "ref" "id_object,id_object_2,id_object_3,id_object_4,id_object_5,id_object_6,id_master,id_object_7" "id_master" "4" "const" "3529" "Using where"
EDIT
It's extremely interesting, because if I DROP id_master index (which for some strange reason is used by both queries), then it starts to use id_object_5 index.
EDIT
And, yes, with id_master index being dropped, both queries start to run super-fast. So, I guess there is some trouble with optimizer.
EDIT
I even have a guess what trouble faces the optimizer - it may be incorrectly treats id_slave_field field name in the key, as if it were two fields instead - id_slave and id_field. In this case it becomes reasonable, why it firstly used this key in both queries.
EDIT
Schema of object_70974_
CREATE TABLE `object_70974_` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`id_inherit` BIGINT(20) NOT NULL DEFAULT '0',
`id_obj` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`if_control` TINYINT(1) NOT NULL DEFAULT '0',
`id_order` BIGINT(20) NOT NULL DEFAULT '0',
`if_archive` TINYINT(1) NOT NULL DEFAULT '0',
`id_group` BIGINT(20) NOT NULL DEFAULT '0',
`if_hist` SMALLINT(6) NOT NULL DEFAULT '0',
`if_garbage` TINYINT(1) NOT NULL DEFAULT '0',
`id_color` CHAR(6) DEFAULT NULL,
`id_text` TINYINT(4) NOT NULL DEFAULT '0',
`if_default` TINYINT(1) NOT NULL DEFAULT '0',
`id_parent` BIGINT(20) NOT NULL DEFAULT '0',
.... a long list of other fields
PRIMARY KEY (`id`),
KEY `id_order` (`id_order`)
) ENGINE=INNODB AUTO_INCREMENT=3636 DEFAULT CHARSET=utf8;

Why does the SELECT mention f1 at all? It is essentially useless. This would give the same answer, possibly except for some end case:
SELECT mss0.id_field
FROM object_master_70974_ mss0
WHERE mss0.id_object IN (70974, 71759)
AND mss0.id_master = 71100
AND mss0.id_slave = 70912
The optimal index for that is
INDEX(id_master, id_slave, id_object)
where master and slave can be in either order, but id_object is last. Build the 'best' index by starting with any WHERE clause that have = (constant).
Don't use LEFT unless you are want to see NULLs for the 'right' table when there is no match. I think this is part of the problem -- the optimizer was forced to start with f1 when it would be a lot better to start with the other table.
8 vs 7 seconds could be caching.
Note in the EXPLAIN that it needs to hit 3K rows in each table.

Related

MySQL query with multiple joins taking too long to execute

I have 3 tables. The first one is called map_life, the second one is called scripts and the third one is called npc_data.
I'm running the following query to get all the properties from map_life while also getting the script column from scripts and the storage_cost column from npc_data if the ids match.
SELECT life.*
, script.script
, npc.storage_cost
FROM map_life life
LEFT
JOIN scripts script
ON script.objectid = life.lifeid
AND script.script_type = 'npc'
LEFT
JOIN npc_data npc
ON npc.npcid = life.lifeid
As you can see, map_life id is lifeid, while scripts id is objectid and npc_data id is npcid.
This query is taking about 5 seconds to execute, and I have no idea why. Here's the CREATE statements for all those 3 tables, maybe I'm missing something?
CREATE TABLE `mcdb83`.`map_life` (
`id` bigint(21) unsigned NOT NULL AUTO_INCREMENT,
`mapid` int(11) NOT NULL,
`life_type` enum('npc','mob','reactor') NOT NULL,
`lifeid` int(11) NOT NULL,
`life_name` varchar(50) DEFAULT NULL COMMENT 'For reactors, specifies a handle so scripts may interact with them; for NPC/mob, this field is useless',
`x_pos` smallint(6) NOT NULL DEFAULT '0',
`y_pos` smallint(6) NOT NULL DEFAULT '0',
`foothold` smallint(6) NOT NULL DEFAULT '0',
`min_click_pos` smallint(6) NOT NULL DEFAULT '0',
`max_click_pos` smallint(6) NOT NULL DEFAULT '0',
`respawn_time` int(11) NOT NULL DEFAULT '0',
`flags` set('faces_left') NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `lifetype` (`mapid`,`life_type`)
) ENGINE=InnoDB AUTO_INCREMENT=32122 DEFAULT CHARSET=latin1;
CREATE TABLE `mcdb83`.`scripts` (
`script_type` enum('npc','reactor','quest','item','map_enter','map_first_enter') NOT NULL,
`helper` tinyint(3) NOT NULL DEFAULT '-1' COMMENT 'Represents the quest state for quests, and the index of the script for NPCs (NPCs may have multiple scripts).',
`objectid` int(11) NOT NULL DEFAULT '0',
`script` varchar(30) NOT NULL DEFAULT '',
PRIMARY KEY (`script_type`,`helper`,`objectid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Lists all the scripts that belong to NPCs/reactors/etc. ';
CREATE TABLE `mcdb83`.`npc_data` (
`npcid` int(11) NOT NULL,
`storage_cost` int(11) NOT NULL DEFAULT '0',
`flags` set('maple_tv','is_guild_rank') NOT NULL DEFAULT '',
PRIMARY KEY (`npcid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
For this query:
SELECT l.*, s.script, npc.storage_cost
FROM map_life l LEFT JOIN
scripts s
ON s.objectid = l.lifeid AND
s.script_type = 'npc' LEFT JOIN
npc_data npc
ON npc.npcid = l.lifeid;
You want indexes on: scripts(object_id, script_type, script) and npc_data(npcid, storage_cost). The order of the columns in these indexes is important.
map_life.lifeid does not have any indexes defined, therefore the joins will result in full table scans. Define an index on map_life.lifeid field.
In scripts table the primary key is defined on the following fields in that order: script_type, helper, objectid. The join is done on objectid and there is a constant filter criterion on script_type. Because the order of the fields in the index is wrong, MySQL cannot use the primary key for this query. For this query the order of the fields in the index should b: objectid, script_type, helper.
The above will significantly speed up the joins. You may further increase the speed of the query if your indexes actually cover all fields that are in the query because in this case MySQL does not even have to touch the tables.
Consider adding an index with the following fields and order to the scripts table: object_id, script_type, script and npcid, storage_cost index to npc_data table. However, these indexes may slow down insert / update / delete statements, so do some performance testing before adding these indexes to production environment.

MARIADB: Index not used for a select with join on a range

I have a first table containing my ips stored as integer (500k rows), and a second one containing ranges of black listed ips and the reason of black listing (10M rows)
here is the table structure :
CREATE TABLE `black_lists` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`ip_start` INT(11) UNSIGNED NOT NULL,
`ip_end` INT(11) UNSIGNED NULL DEFAULT NULL,
`reason` VARCHAR(3) NOT NULL,
`excluded` TINYINT(1) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `ip_range` (`ip_end`, `ip_start`),
INDEX `ip_start` ( `ip_start`),
INDEX `ip_end` (`ip_end`),
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=10747741
;
CREATE TABLE `ips` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'Id ips',
`idhost` INT(11) NOT NULL COMMENT 'Id Host',
`ip` VARCHAR(45) NULL DEFAULT NULL COMMENT 'Ip',
`ipint` INT(11) UNSIGNED NULL DEFAULT NULL COMMENT 'Int ip',
`type` VARCHAR(45) NULL DEFAULT NULL COMMENT 'Type',
PRIMARY KEY (`id`),
INDEX `host` (`idhost`),
INDEX `index3` (`ip`),
INDEX `index4` (`idhost`, `ip`),
INDEX `ipsin` (`ipint`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=675651;
my problem is when I try to run this query no index is used and it takes an eternity to finish :
select i.ip,s1.reason
from ips i
left join black_lists s1 on i.ipint BETWEEN s1.ip_start and s1.ip_end;
I'm using MariaDB 10.0.16
True.
The optimizer has no knowledge that start..end values are non overlapping, nor anything else obvious about them. So, the best it can do is decide between
s1.ip_start <= i.ipint -- and use INDEX(ip_start), or
s1.ip_end >= i.ipint -- and use INDEX(ip_end)
Either of those could result in upwards of half the table being scanned.
In 2 steps you could achieve the desired goal for one ip; let's say #ip:
SELECT ip_start, reason
FROM black_lists
WHERE ip_start <= #ip
ORDER BY ip_start DESC
LIMIT 1
But after that, you need to see if the ip_end corresponding to that ip_start is <= #ip before deciding whether you have a black-listed item.
SELECT reason
FROM ( ... ) a -- fill in the above query
JOIN black_lists b USING(ip_start)
WHERE b.ip_end <= #ip
That will either return the reason or no rows.
In spite of the complexity, it will be very fast. But, you seem to have a set of IPs to check. That makes it more complex.
For black_lists, there seems to be no need for id. Suggest you replace the 4 indexes with only 2:
PRIMARY KEY(ip_start, ip_end),
INDEX(ip_end)
In ips, isn't ip unique? If so, get rid if id and change 5 indexes to 3:
PRIMARY KEY(idint),
INDEX(host, ip),
INDEX(ip)
You have allowed more than enough in the VARCHAR for IPv6, but not in INT UNSIGNED.
More discussion.

Very Slow simple MySql query with index

i have this table :
CREATE TABLE `messenger_contacts` (
`number` varchar(15) NOT NULL,
`has_telegram` tinyint(1) NOT NULL DEFAULT '0',
`geo_state` int(11) NOT NULL DEFAULT '0',
`geo_city` int(11) NOT NULL DEFAULT '0',
`geo_postal` int(11) NOT NULL DEFAULT '0',
`operator` tinyint(1) NOT NULL DEFAULT '0',
`type` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `messenger_contacts`
ADD PRIMARY KEY (`number`),
ADD KEY `geo_city` (`geo_city`),
ADD KEY `geo_postal` (`geo_postal`),
ADD KEY `type` (`type`),
ADD KEY `type1` (`operator`),
ADD KEY `has_telegram` (`has_telegram`),
ADD KEY `geo_state` (`geo_state`);
with about 11 million records.
A simple count select on this table takes about 30 to 60 seconds to complete witch seems very high.
select count(number) from messenger_contacts where geo_state=1
I am not a Database pro so beside setting indexes i don't know what else i can do to make the query faster?
UPDATE:
OK , i made some changes to column type and size:
CREATE TABLE IF NOT EXISTS `messenger_contacts` (
`number` bigint(13) unsigned NOT NULL,
`has_telegram` tinyint(1) NOT NULL DEFAULT '0' ,
`geo_state` int(2) NOT NULL DEFAULT '0',
`geo_city` int(4) NOT NULL DEFAULT '0',
`geo_postal` int(10) NOT NULL DEFAULT '0',
`operator` tinyint(1) NOT NULL DEFAULT '0' ,
`type` tinyint(1) NOT NULL DEFAULT '0' ,
PRIMARY KEY (`number`),
KEY `has_telegram` (`has_telegram`,`geo_state`),
KEY `geo_city` (`geo_city`),
KEY `geo_postal` (`geo_postal`),
KEY `type` (`type`),
KEY `type1` (`operator`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Now the query only takes 4 to 5 seconds with * and number
Tanks every one for your help, even the guy that gave me -1. this would be good enough for now considering that my server is a low end hardware and i will be caching the select count results.
Maybe
select count(geo_state) from messenger_contacts where geo_state=1
as it will give the same result but will not use number column from the clustered index?
If this does not help, I would try to change number column into INT type, which should reduce the index size, or try to increase amount of memory MySQL could use for caching indexes.
You did not change the datatypes. INT(11) == INT(2) == INT(100) -- each is a 4-byte signed integer. You probably want 1-byte unsigned TINYINT UNSIGNED or 2-byte SMALLINT UNSIGNED.
It is a waste to index "flags", which I assume type and has_telegram are. The optimizer will never use them because it will less efficient than simply doing a table scan.
The standard coding pattern is:
select count(*)
from messenger_contacts
where geo_state=1
unless you need to not count NULLs, which is what COUNT(geo_state) implies.
Once you have the index on geo_state (or an index starting with geo_state), the query will scan the index (which is a separate BTree structure) starting with the first occurrence of geo_state=1 until the last, counting as it goes. That is, it will touch 1.1 millions index entries. So, a few seconds is to be expected. Counting a 'rare' geo_state will run much faster.
The reason for 30-60 seconds versus 4-5 seconds is very likely to be caching. The former had to read stuff from disk; the latter did not. Run the query twice.
Using the geo_state index will be faster for that query than using the PRIMARY KEY unless there are caching differences.
INDEX(number,geo_state) is virtually useless for any of the SELECTs mentioned -- geo_state should be first. This is an example of a "covering" index for the select count(number)... case.
More on building indexes.

Optimizing MySQL indexes and query execution time

A question about query performance in MySQL. I have a table (the largest I've ever dealt with) of 2.3 million records (and growing). The table is part of a database keeping track of users logging in and scoring points in kind of seperate, quiz-like, sessions. For the query at hand I need the 'highscore table' of all the sessions.
So, the points scored in a session are stored per question in order to analyse the progress of the user better. A session combines the total of a user's points, and a session is connected to a user.
At first the query executiontime ran towards 12 seconds (unacceptable) with the table and query data as follows under 'Original set'. Under 'Improved scores table' there is the altered situation with some optimization in the indexes. This results in a query execution time of about 2 seconds.
My Question is: Is there an additional way to optimize? Like I said, 2.3 million (and counting) is the largest table I've ever seen, so I'm not that experienced at this and optimization sooner results in seconds than tenths of a second improvement.
Original set
CREATE TABLE `players` (
`id_players` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_organisations` int(10) unsigned NOT NULL,
`player_name` varchar(45) NOT NULL,
`player_comments` text NOT NULL,
PRIMARY KEY (`id_players`),
KEY `FK_players_organisation` (`id_organisations`),
CONSTRAINT `FK_players_organisation` FOREIGN KEY (`id_organisations`) REFERENCES `organisations` (`id_organisations`)
) ENGINE=InnoDB AUTO_INCREMENT=9139 DEFAULT CHARSET=latin1
SELECT COUNT(*) FROM players => 9126
CREATE TABLE `scores` (
`id_scores` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_sessions` int(10) unsigned NOT NULL,
`id_levels` int(10) unsigned NOT NULL,
`id_categories` int(10) unsigned NOT NULL,
`score_points` int(10) unsigned NOT NULL,
`score_correct` tinyint(4) NOT NULL,
`score_submitted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id_scores`),
KEY `FK_scores_sessions` (`id_sessions`),
KEY `FK_scores_levels` (`id_levels`),
KEY `FK_scores_categories` (`id_categories`),
KEY `Index_3_points` (`score_points`),
KEY `Index_4_submitted` (`score_submitted`)
) ENGINE=InnoDB AUTO_INCREMENT=2328510 DEFAULT CHARSET=latin1
SELECT COUNT(*) FROM scores => 2328469
CREATE TABLE `sessions` (
`id_sessions` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_players` int(10) unsigned NOT NULL,
`id_classes` int(11) DEFAULT NULL,
`session_start` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`session_grade` decimal(4,1) NOT NULL,
`session_ip` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id_sessions`),
KEY `FK_sessions_players` (`id_players`),
KEY `FK_sessions_classes` (`id_classes`)
) ENGINE=InnoDB AUTO_INCREMENT=40800 DEFAULT CHARSET=latin1
SELECT COUNT(*) FROM sessions => 40788
The 'offending' query:
SELECT sum( s.score_points ) AS score_points, p.player_name
FROM scores s
INNER JOIN sessions se ON s.id_sessions = se.id_sessions
INNER JOIN players p ON se.id_players = p.id_players
GROUP BY se.id_sessions
ORDER BY score_points DESC
LIMIT 50;
Above query took about 12 seconds with said scores table. (below the EXPLAIN ouput)
id select_type table type possible_keys key key_len ref rows Extra
'1' 'SIMPLE' 'p' 'ALL' 'PRIMARY' NULL NULL NULL '9326' 'Using temporary; Using filesort'
'1' 'SIMPLE' 'se' 'ref' 'PRIMARY,FK_sessions_players' 'FK_sessions_players' '4' 'earzsql.p.id_players' '2' 'Using index'
'1' 'SIMPLE' 's' 'ref' 'FK_scores_sessions' 'FK_scores_sessions' '4' 'earzsql.se.id_sessions' '72' ''
(the apparently infamous Using temporary and Using filesort)
After some 'research' I've changed indexes (Index_3_points) in scores table resulting in a table like:
Improved scores table
CREATE TABLE `scores` (
`id_scores` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_sessions` int(10) unsigned NOT NULL,
`id_levels` int(10) unsigned NOT NULL,
`id_categories` int(10) unsigned NOT NULL,
`score_points` int(10) unsigned NOT NULL,
`score_correct` tinyint(4) NOT NULL,
`score_submitted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id_scores`),
KEY `FK_scores_sessions` (`id_sessions`),
KEY `FK_scores_levels` (`id_levels`),
KEY `FK_scores_categories` (`id_categories`),
KEY `Index_4_submitted` (`score_submitted`),
KEY `Index_3_points` (`id_sessions`,`score_points`)
) ENGINE=InnoDB AUTO_INCREMENT=2328510 DEFAULT CHARSET=latin1
With above scores table the query execution time drops to about 2 seconds. Explain (below) has not really changed a lot though (at least, the infamous temporary and filesorts are still used)
id select_type table type possible_keys key key_len ref rows Extra
'1' 'SIMPLE' 'p' 'ALL' 'PRIMARY' NULL NULL NULL '9326' 'Using temporary; Using filesort'
'1' 'SIMPLE' 'se' 'ref' 'PRIMARY,FK_sessions_players' 'FK_sessions_players' '4' 'earzsql.p.id_players' '2' 'Using index'
'1' 'SIMPLE' 's' 'ref' 'FK_scores_sessions,Index_3_points' 'Index_3_points' '4' 'earzsql.se.id_sessions' '35' 'Using index'
I'd love to hear it if anyone knows further optimization tricks.
Presumably the top 50 scores don't change very often?
So run the query into a TopScore table, and index it. When a user's score changes, check it against the high scores table, and only update the TopScore table if the user's score is better than the 50th.
I would also suggest that adding a lot of indexes to a table that is frequently updated will probably have adverse performance effects on that table.

mysql query optimization (wrong indexes?) avoid filesort

I will try to explain myself quickly.
I have a database called 'artikli' which has about 1M records.
On this table i run a lots of different queryies but 1 particular is causing problems (long execution time) when ORDER by is present.
This is my table structure:
CREATE TABLE IF NOT EXISTS artikli (
id int(11) NOT NULL,
name varchar(250) NOT NULL,
datum datetime NOT NULL,
kategorije_id int(11) default NULL,
id_valute int(11) default NULL,
podogovoru int(1) default '0',
cijena decimal(10,2) default NULL,
valuta int(1) NOT NULL default '0',
cijena_rezerva decimal(10,0) NOT NULL,
cijena_kupi decimal(10,0) default NULL,
cijena_akcija decimal(10,2) NOT NULL,
period int(3) NOT NULL default '30',
dostupnost enum('svugdje','samobih','samomojgrad','samomojkanton') default 'svugdje',
zemlja varchar(10) NOT NULL,
slike varchar(500) NOT NULL,
od_s varchar(34) default NULL,
od_id int(10) unsigned default NULL,
vrsta int(1) default '0',
trajanje datetime default NULL,
izbrisan int(1) default '0',
zakljucan int(1) default '0',
prijava int(3) default '0',
izdvojen decimal(1,0) NOT NULL default '0',
izdvojen_kad datetime NOT NULL,
izdvojen_datum datetime NOT NULL,
sajt int(1) default '0',
PRIMARY KEY (id),
KEY brend (brend),
KEY kanton (kanton),
KEY datum (datum),
KEY cijena (cijena),
KEY kategorije_id (kategorije_id,podogovoru,sajt,izdvojen,izdvojen_kad,datum)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And this is the query:
SELECT artikli.datum as brojx,
artikli.izdvojen as i,
artikli.izdvojen_kad as ii,
artikli.cijena as cijena, artikli.name
FROM artikli
WHERE artikli.izbrisan=0 and artikli.prodano!=3
and artikli.zavrseno=0 and artikli.od_id!=0
and (artikli.sajt=0 or (artikli.sajt=1 and artikli.dostupnost='svugdje'))
and kategorije_id IN (18)
ORDER by i DESC, ii DESC, brojx DESC
LIMIT 0,20
What i want to do is to avoid Filesort which is very slow.
It would have been a big help if you'd provided the explain plan for the query.
Why do you think its the filesort which is causing the problem? Looking at the query you seem to be applying a lot filtering - which should reduce the output set significantly - but none of can use the available indexes.
artikli.izbrisan=0 and artikli.prodano!=3
and artikli.zavrseno=0 and artikli.od_id!=0
and (artikli.sajt=0 or (artikli.sajt=1 and artikli.dostupnost='svugdje'))
and kategorije_id IN (18)
Although I don't know what the pattern of your data is, I suspect that you might get a lot more benefit by adding an index on :
kategorije_id,izbrisan,sajt
Are all those other indexes really being used already?
Although you'd get a LOT more bang for your buck by denormalizing all those booleans (assuming that the table is normalised to start with and there are not hidden functional dependencies in there).
C.
The problem is that you don't have an index on the izdvojen, izdvojen_kad and datum columns that are used by the ORDER BY.
Note that the large index you have starting with kategorije_id can't be used for sorting (although it will help somewhat with the where clause) because the columns you are sorting by are at the end of the index.
Actually, the order by is not the basis for the index you want... but the CRITERIA you want to mostly match the query... Filter the smaller set of data out, you'll get smaller set of the table... I would change the WHERE clause a bit, but you'll know your data best. Put your smallest expected condition first and ensure an index is based on that... something like
WHERE
artikli.izbrisan = 0
and artikli.zavrseno = 0
and artikli.kategorije_id IN (18)
and artikli.prodano != 3
and artikli.od_id != 0
and ( artikli.sajt = 0
or ( artikli.sajt = 1
and artikli.dostupnost='svugdje')
)
and having a compound index on (izbrisan, zavrseno, kategorije_id)... I've mode the other != comparisons after as they are not specific key values, instead, they are ALL EXCEPT the value in question.