SQL query takes too long to run in SSMS - sql

I've the following query which works absolutely fine in Oracle developer
select TRIM(a.filterh)
from CHANNEL a,GENRE b
where b.label = 'M001CL01_ABC'
and a.s_r_id = b.r_id
and a.filterh in (select c.filterh
from CHANNEL c, GENRE d
where d.label = 'M001AL03' and c.s_r_id = d.r_id)
I just tried to simplify the above query and some syntax change for SQL developer and the below query takes a lot of time to run in SSMS
select TRIM(a.filterh)
from CHANNEL a
inner join GENRE b on a.s_r_id = b.r_id
where b.label = 'M001CL01_ABC'
and a.filterh in (select c.filterh
from CHANNEL c
inner join GENRE d on c.s_r_id = d.r_id
where d.label = 'M001AL03')
I just wish to understand what am I doing wrong here, how can I improve my query and why the SQL query takes a lot of time.
Thank you.

See if EXISTS instead of IN improves the situation any better.
select TRIM(a.filterh)
from CHANNEL a
inner join GENRE b on a.s_r_id = b.r_id and b.label = 'M001CL01_ABC'
where exists(select *
from CHANNEL c
inner join GENRE d on c.s_r_id = d.r_id and d.label = 'M001AL03'
where a.filterh = c.filterh)

It looks like you can probably do the following, hard to say for sure without seeing the data and being able to test:
select TRIM(a.filterh)
from CHANNEL a
inner join GENRE b on a.s_r_id = b.r_id
where b.label = 'M001CL01_ABC'
and exists (select * from GENRE g where g.r_id=a.s_r_id and g.label='M001AL03')

Here is my version of the query. Please keep in mind that since there is no data to test on, I couldn't test it propertly, so it is up to you to check it out, whether it could be compiled.
Here what I've done is just removed the subquery because they often lead to mistakes of query optimizer
select TRIM(a.filterh)
from CHANNEL a
join GENRE b on a.s_r_id = b.r_id
join CHANNEL c on a.filterh = c.filterh
join GENRE d on c.s_r_id = d.r_id
where b.label = 'M001CL01_ABC'
and d.label = 'M001AL03'
Another point to mention is that performance issues may depend on many things. For example quantity of rows, indexes, storage device etc. If you post a similar quesion in future it is better to provide us with execution plans and statistics turned on while executing the query.
If query runs slowly, ON A TEST INSTANCE try to create an index on channel.filterg and another one on genre.label columns.

I think you can use aggregation in either database:
select TRIM(c.filterh)
from CHANNEL c join
GENRE g
on g.s_r_id = c.r_id
where g.label in ('M001CL01_ABC', 'M001AL03')
group by TRIM(c.filterh)
having count(distinct g.label) = 2;
You should learn to use meaningful table aliases. Arbitrary letters such as a and b do not help anyone understand the query.

Related

Long query execution - SSP Datatables

I use Datables with SSP Class. And i have query with result 2000 lines.
But when I try running query I got error 500/504 error (if I have 504 table is didn't load)
I use OVH CloudDB with MariadDB 10.2 and on server I have php 7.2
My joinQuery looks like:
FROM data_platforms p
LEFT JOIN game_platforms gp ON gp.platform_id = p.platform_id
LEFT JOIN games g ON g.game_id = gp.game_id
LEFT JOIN tastings t ON t.tasting_game_id = g.game_id
LEFT JOIN notes n ON n.note_game_id = g.game_id
LEFT JOIN ratings r ON r.rating_game_id = g.game_id
LEFT JOIN images i ON i.image_type_id = g.game_id AND (i.image_type = 2 || i.image_type = 1)
LEFT JOIN game_generes gg ON gg.game_id = g.game_id
LEFT JOIN generes gen ON gen.id = gg.genere_id
And extraWhere
p.platform_id = '.$platformID.' AND g.game_status = 1
And groupBy
gp.game_id
Is there any way to be able to optimize this query?
Am I doomed to fail at this point, or should I use a different SSP class?
Don't use LEFT unless you really expect the 'right' table to be missing.
Don't "over-normalize". For example, I would expect genre to simply be a column, not a many-to-many mapping table plus a genre table.
(i.image_type = 2 || i.image_type = 1) --> i.image_type IN (1,2) might optimizer better.
See this for a likely improvement in many-to-many table indexes: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
Please provide SHOW CREATE TABLE so we can check other indexes, such as on platform_id.
Let's see the entire query, plus EXPLAIN SELECT .... GROUP BY gp.game_id may be invalid (unless everything else is dependent on it).

Need help in optimizing sql query

I am new to sql and have created the below sql to fetch the required results.However the query seems to take ages in running and is quite slow. It will be great if any help in optimization is provided.
Below is the sql query i am using:
SELECT
Date_trunc('week',a.pair_date) as pair_week,
a.used_code,
a.used_name,
b.line,
b.channel,
count(
case when b.sku = c.sku then used_code else null end
)
from
a
left join b on a.ma_number = b.ma_number
and (a.imei = b.set_id or a.imei = b.repair_imei
)
left join c on a.used_code = c.code
group by 1,2,3,4,5
I would rewrite the query as:
select Date_trunc('week',a.pair_date) as pair_week,
a.used_code, a.used_name, b.line, b.channel,
count(*) filter (where b.sku = c.sku)
from a left join
b
on a.ma_number = b.ma_number and
a.imei in ( b.set_id, b.repair_imei ) left join
c
on a.used_code = c.code
group by 1,2,3,4,5;
For this query, you want indexes on b(ma_number, set_id, repair_imei) and c(code, sku). However, this doesn't leave much scope for optimization.
There might be some other possibilities, depending on the tables. For instance, or/in in the on clause is usually a bad sign -- but it is unclear what your intention really is.

Additional select if one select result is not null

I am interested in the results found in table g, which shares a key, sample_name, with tables s and l. In this question the tables are
s - samples,
p - projects,
l - analyses, and
g, analysis g,
all within schema a.
In the interest of optimization, I only want to look for table g after having confirmed that l.analysis_g is NOT NULL.
Given: The only information that I start out with is the project names. The project table, p is linked with other tables by the samples table s. s is linked to every table. Table l contains types of analysis and each column is either NULL or 1.
In the example below I am trying a case but I realize this may be totally incorrect.
SELECT s.sample_name,
s.project_name,
g.*
FROM a.samples s
JOIN a.analyses l
ON s.sample_name = l.sample_name
JOIN a.analysis_g g
ON s.sample_name = g.sample_name
WHERE s.project_name IN (SELECT p.project_name
FROM a.projects p
WHERE p.project_name_other
IN ('PROJ_1',
'PROJ_2'))
;
Then perhaps in the where clause? It's still really hard to understand what you want . . .
SELECT s.sample_name,
s.project_name,
g.*
FROM a.samples s
JOIN a.analyses l
ON s.sample_name = l.sample_name
JOIN a.analysis_g g
ON s.sample_name = g.sample_name
WHERE s.project_name IN (SELECT p.project_name
FROM a.projects p
WHERE p.project_name_other
IN ('PROJ_1',
'PROJ_2'))
and l.analysis_g IS NOT NULL
;
As a side note, I think you could join p.project_name and avoid the where clause. AND I think you might want some inner joins -- but I'm not sure.
SELECT s.sample_name,
s.project_name,
g.*
FROM a.samples s
JOIN a.analyses l ON s.sample_name = l.sample_name
JOIN a.analysis_g g ON s.sample_name = g.sample_name
JOIN a.projects p ON s.project_name = p.project_name
WHERE p.project_name_other IN ('PROJ_1', 'PROJ_2')
and l.analysis_g IS NOT NULL
Again: Please show an example! We can't help if we have to guess, but I'll give it a try...
If l.analysis_g contains an ID from table g, then you can just use:
SELECT * FROM g
JOIN l on g.id = l.analysis_g
WHERE blah, blah, blah...
I removed your WHERE clause because you haven't provided enough information to allow anyone to help optimize it (if needed).

How do I optimize my query in MySQL?

I need to improve my query, specially the execution time.This is my query:
SELECT SQL_CALC_FOUND_ROWS p.*,v.type,v.idName,v.name as etapaName,m.name AS manager,
c.name AS CLIENT,
(SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(duration)))
FROM activities a
WHERE a.projectid = p.projectid) AS worked,
(SELECT SUM(TIME_TO_SEC(duration))
FROM activities a
WHERE a.projectid = p.projectid) AS worked_seconds,
(SELECT SUM(TIME_TO_SEC(remain_time))
FROM tasks t
WHERE t.projectid = p.projectid) AS remain_time
FROM projects p
INNER JOIN users m
ON p.managerid = m.userid
INNER JOIN clients c
ON p.clientid = c.clientid
INNER JOIN `values` v
ON p.etapa = v.id
WHERE 1 = 1
ORDER BY idName
ASC
The execution time of this is aprox. 5 sec. If i remove this part: (SELECT SUM(TIME_TO_SEC(remain_time)) FROM tasks t WHERE t.projectid = p.projectid) AS remain_time
the execution time is reduced to 0.3 sec. Is there a way to get the values of the remain_time in order to reduce the exec.time ?
The SQL is invoked from PHP (if this is relevant to any proposed solution).
It sounds like you need an index on tasks.
Try adding this one:
create index idx_tasks_projectid_remaintime on tasks(projectid, remain_time);
The correlated subquery should just use the index and go much faster.
Optimizing the query as it is written would give significant performance benefits (see below). But the FIRST QUESTION TO ASK when approaching any optimization is whether you really need to see all the data - there is no filtering of the resultset implemented here. This is a HUGE impact on how you optimize a query.
Adding an index on the query above will only help if the optimizer is opening a new cursor on the tasks table for every row returned by the main query. In the absence of any filtering, it will be much faster to do a full table scan of the tasks table.
SELECT ilv.*, remaining.rtime
FROM (
SELECT p.*,v.type, v.idName, v.name as etapaName,
m.name AS manager, c.name AS CLIENT,
SEC_TO_TIME(asbq.worked) AS worked, asbq.worked AS seconds_worked,
FROM projects p
INNER JOIN users m
ON p.managerid = m.userid
INNER JOIN clients c
ON p.clientid = c.clientid
INNER JOIN `values` v
ON p.etapa = v.id
LEFT JOIN (
SELECT a.projectid, SUM(TIME_TO_SEC(duration)) AS worked
FROM activities a
GROUP BY a.projectid
) asbq
ON asbq.projectid=p.projectid
) ilv
LEFT JOIN (
(SELECT t.project_id, SUM(TIME_TO_SEC(remain_time)) as rtime
FROM tasks t
GROUP BY t.projectid) remaining
ON ilv.projectid=remaining.projectid

How do I use rows-as-fields in a SQL database

I've got a SQL related question regarding a general database structure that seems to be somewhat common. I came up with it one day while trying to solve a problem and (later on) I've seen other people do the same thing (or something remarkably similar) so I think the structure itself makes sense. I just have trouble trying to form certain queries against it.
The idea is that you've got a table with "items" in it and you want to store a set of fields and their values for each item. Normally this would be done by simply adding columns to the items table, the problem is that the field(s) themselves (not just the values) vary from item to item. For example, I might have two items:
Article 1
product_id = aproductid
hidden_key = ahiddenkeyvalue
Article 2
product_id = anotherproductid
address = anaddress
You can see that both items have a product_id field (with different values) but the data stored for each item is different.
The structure in the database ends up something like this:
ItemsTable
id
itemdata_1
itemdata_2
...
FieldsTable
id
field_name
...
And the table that relates them and makes it work
FieldsItemRelationsTable
field_id
item_id
value
Well when I'm trying to do something that involves just one "dynamic field" value there's no problem. I usually do something similar to:
SELECT i.* FROM ItemsTable i
INNER JOIN FieldsItemRelationsTable v ON v.item_id = i.id
INNER JOIN FieldsTable f ON f.id = v.field_id
WHERE v.value = 50 AND f.name = 'product_id';
Which selects all items where product_id=50
The problem arises when I need to do something involving multiple "dynamic field" values. Say I want to select all items where product_id = 50 AND hidden_key = 30. Is it possible with a single SQL statement? I've tried:
SELECT i.* FROM ItemsTable i
INNER JOIN FieldsItemRelationsTable v ON v.item_id = i.id
INNER JOIN FieldsTable f ON f.id = v.field_id
WHERE (v.value = 50 AND f.name = 'product_id')
AND (v.value = 30 AND f.name = 'hidden_key');
But it just returns zero rows.
You'll need to do a seperate join for each value you are bringing back...
SELECT i.* FROM ItemsTable i
INNER JOIN FieldsItemRelationsTable v ON v.item_id = i.id
INNER JOIN FieldsTable f ON f.id = v.field_id
INNER JOIN FieldsItemRelationsTable v2 ON v2.item_id = i.id
INNER JOIN FieldsTable f2 ON f2.id = v2.field_id
WHERE v.value = 50 AND f.name = 'product_id'
AND (v2.value = 30 AND f2.name = 'hidden_key');
er...that query might not function (a bit of a copy/paste sludge job on my part), but you get the idea...you'll need the second value held in a second instance of the table(s) (v2 and f2 in my example here) that is seperate than the first instance. v1.value = 30 and v2.value = 50. v1.value = 50 and v1.value = 30 should never return rows as nothing will equal 30 and 50 at the same time
As an after thought...the query will probably read easier had you put the where clause in the join statement
SELECT i.* FROM ItemsTable i
INNER JOIN FieldsItemRelationsTable v ON v.item_id = i.id and v.value = 50
INNER JOIN FieldsTable f ON f.id = v.field_id and f.name = 'product_id'
INNER JOIN FieldsItemRelationsTable v2 ON v2.item_id = i.id and v2.value = 30
INNER JOIN FieldsTable f2 ON f2.id = v2.field_id and f2.name = 'hidden_key'
Functionally both queries should operate the same though. I'm not sure if there's a logical limit...in scheduling systems you'll often see a setup for 'exceptions'...I've got a report query that's joining like this 28 times...one for each exception type returned.
It's called EAV
Some people hate it
There are alternatives (SO)
Sorry to be vague, but I would investigate your options more.
Try doing some left or right joins to see if you get any results. inner joins will not return results sometimes if there are null fields.
its a start.
Dont forget though, outer join = cartesian product