I have a view which has the below definition
create view mydashboard as
SELECT distinct
cu.CrimeID,
ad.DeptID,
ad.CrimeDate,
cd.DeptIncidentID,
ad.crime,
u.username
from alldatescrimes ad
inner join crimeslist cl
on
ad.crime = cl.crime
inner join users u
on
u.DeptID = ad.DeptID
left join crimelookup cu
on cu.CrimesListID = cl.CrimeListID
left join crimesdetail cd
on
ad.CrimeDate = cast(cd.CrimeDate as date)
and
ad.DeptID = cd.DeptID
and
cu.CrimeID = cd.CrimeID
My problem is that If I put a where clause outside the view, the query runs very slowly. See the below example
select *
from mydashboard
where
(username = 'john'
or
DeptIncidentID is null
)
and
CrimeDate = '2014-06-16'
On the contrary If I put the same where clause inside the view, the query runs very fast..like in 2-3 seconds
My question is what steps can I take so that the query runs fast if I put the where clause outside view. I am using this view in a report and the query runs real slow
Regards
Arif
MySQL has two options to process a view that is used inside a query: MERGE or TEMPTABLE.
For MERGE, the text of a statement that refers to the view and the view definition are merged such that parts of the view definition replace corresponding parts of the statement.
For TEMPTABLE, the results from the view are retrieved into a temporary table, which then is used to execute the statement.
Because of the DISTINCT clause in the view definition, MySQL cannot use the MERGE algorithm. It must falback to the less efficient TEMPTABLE algorithm.
The temporary table has no index, therefore the whole table must be scanned to process your outer WHERE conditions.
You may want to remove the DISTINCT clause from the view definition, and put it in your outer query instead.
This is a bit long for a comment.
MySQL does a poor job of optimizing views. In fact, one part of the documentation starts:
View processing is not optimized:
One possible issue is that MySQL has determined that a temporary table is needed for the view. If so, all the processing needs to be done. Then at the very last stage, the where clause is being added. Here is more information on "merge" versus "temporary tables" for views.
Related
I have a MySQL database for an investor to track his investments:
the 'deal' table has info about the investments, including different categories for the investment (asset_class).
Another table ('updates') tracks updates on a specific investment (investment name, date, and lots of financial details.)
I want to write a query that allows the user to select all updates from 'updates' under a specific asset_class. However, as mentioned, asset_class is in the investment table. I wrote the following query:
SELECT *
FROM updates
WHERE updates.invest_name IN (SELECT deal.deal_name
FROM deal
WHERE deal.asset_class = '$asset_class'
);
I'm using PHP, so $asset_class is the selected variable of asset_class.
However, the query only returns unique update names, but I want to see ALL updates for the given asset_class, even if several updates are made under one investment name.
Any advice? Thanks!
Your query should do what you intend. In general, though, this type of query would be written using a JOIN. More importantly use parameter placeholders instead of munging query strings:
SELECT u.*
FROM updates u JOIN
deal d
ON u.invest_name = d.deal_name
WHERE d.asset_class = ?;
This can take advantage of indexes on deal(asset_class, deal_name) and updates(invest_name).
The ? represents a parameter that you pass into the query when you run it. The exact syntax depends on how you are making the call.
i have a MySQL statement which works - i can get the records requested - movies.* & groups.name.
$stmt= $mysqli->query("SELECT DISTINCT ebooks.*, groups.name FROM ebooks
INNER JOIN ebooks_groups ON ebooks.uuid = ebooks_groups.ebookuuid
INNER JOIN groups_users ON ebooks_groups.groupuuid = groups_users.groupuuid
INNER JOIN groups ON groups_users.groupuuid = groups.uuid
WHERE useruuid=".$get_useruuid."
ORDER BY groups.name");
1/ However i need to grab another column from the groups table - namely groups.uuid
i tried
SELECT DISTINCT movies.*, groups.* FROM movies, groups
&
SELECT DISTINCT movies.*, groups.name, groups.uuid FROM movies, groups
but it retrieved no records.
2/ Then I had another look at my original code - ... FROM movies ... - how is this even working if i'm not selecting FROM movies, groups tables?
AFAIK, this is pure MySQL. PHP or not doesn't come into play.
First to understand is the implicit join:
Explicit vs implicit SQL joins
That understanding should solve at least half of your problem.
Secondly, I'd never code a SELECT * without a very good reason (and there's few). It makes much more sense to select just the columns you need instead of getting them all and even if you need all that are currently there, if you work on the database model later on, there might be more (or less!!) columns in the database and it'll be much harder to detect that your code needs updating if you don't have them explicitly listed.
For the rest I build my SQL queries slowly step by step. That helps a lot to debugging your queries esp. as you have the actual tables and some sample data ...
[That should solve your other half of the question]
I am creating a booking application and have normalised the database as much as is possible. My question is the following, should I create Views in the database that combine the tables again before I do selective selects on the combined view in the WHERE clause or is it better to filter the tables with selects before joining them in a view?
EDIT: Included example.
The first scenario creates the combined view first, and then performs the SELECT on the combined view (this view may have thousands of records):
CREATE VIEW appc as
SELECT * FROM appointment
LEFT OUTER JOIN chair
ON appointment.chair_idchair = chair.idchair
SELECT * FROM appc
WHERE chair_idchair = 1;
The second scenario will first filter the table in the left of the join and then create a view based on the filtered table, then the join will be made using this much smaller view:
CREATE VIEW appf as
SELECT * FROM appointment
WHERE chair_idchair = 1;
SELECT * FROM appf
LEFT OUTER JOIN chair
ON appf.chair_idchair = chair.idchair
It makes little difference to MySQL. MySQL (unlike some other RDBMS brands) does not store anything in the view itself. Think of a MySQL view more like a macro. In most cases, querying the view is exactly like executing the query you defined as the view.
Applying subsequent conditions in the WHERE clause when querying the view is combined pretty transparently with the view's query, as if you had written the full query and given it one more condition.
Exception: you can optionally create the view with ALGORITHM=TEMPTABLE, to force it to store the results of the view in a temp table, and then apply extra conditions you specified in your query. In this case, it would be better to build the conditions into the view's query, to reduce the size of the resulting temp table.
See http://dev.mysql.com/doc/refman/5.6/en/view-algorithms.html for more details.
As a general rule, the optimizer is pretty smart, and can see right through the views when constructing a query plan. If you try to "help" the optimizer by pre-selecting some data, you may be hiding information from the optimizer that would have allowed it to create a smarter, more-optimal plan.
I have created a complex view which gives me output within a second on Oracle 10g DBMS.. but the same view takes 2,3 minutes on MYSQL DBMS.. I have created indexes on all the fields which are included in the view definition and also increased the query_cache_size but still failed to get answer in less time. My query is given below
select * from results where beltno<1000;
And my view is:
create view results as select person_biodata.*,current_rank.*,current_posting.* from person_biodata,current_rank,current_posting where person_biodata.belt_no=current_rank.belt_no and person_biodata.belt_no=current_posting.belt_no ;
The current_posting view is defined as follows:
select p.belt_no belt_no,ps.ps_name police_station,pl.pl_name posting_as,p.st_date from p_posting p,post_list pl,police_station ps where p.ps_id=ps.ps_id and p.pl_id=pl.pl_id and (p.belt_no,p.st_date) IN(select belt_no,max(st_date) from p_posting group by belt_no);
The current_rank view is defined as follows:
select p.belt_no belt_no,r.r_name from p_rank p,rank r where p.r_id=r.r_id and (p.belt_no,p.st_date) IN (select belt_no,max(st_date) from p_rank group by belt_no)
Some versions of MySQL have a particular problem with in and subqueries, which you have in this view:
select p.belt_no belt_no,ps.ps_name police_station,pl.pl_name posting_as,p.st_date
from p_posting p,post_list pl,police_station ps
where p.ps_id=ps.ps_id and p.pl_id=pl.pl_id and
(p.belt_no,p.st_date) IN(select belt_no,max(st_date) from p_posting group by belt_no)
Try changing that to:
where exists (select 1
from posting
group by belt_no
having belt_no = p.belt_no and p.st_date = max(st_date)
)
There may be other issues, of course. At the very least, you could format your queries so they are readable and use ANSI standard join syntax. Being able to read the queries would be the first step to improving their performance. Then you should use explain in MySQL to see what the query plans are like.
Muhammad Jawad it's so simple. you have already created indexes on table that allow database application to find data fast, but if you change/update the (indexes tables) table (e.g: inserst,update,delete) then it take more time that of which have no indexes applied on table because the indexes also need updation so each index will be updated that take too much time. So you should apply indexes on columns or tables that we use it only for search purposes only. hope this will help u. thank u.
I'm trying to index my views since the data is relatively static and it could increase performance.
I cannot index the view because it contains a "ranking or aggregate window function". Is there a workaround for that?
SELECT r.Id, r.Value, r.TestSessionId, t.Type AS TestType, r.StudentId, ROW_NUMBER() OVER (partition BY r.StudentId, r.TestSessionId ORDER BY r.Id) AS AttemptNumber
FROM dbo.Responses r
INNER JOIN dbo.TestSessions ts ON r.TestSessionId = ts.Id
INNER JOIN dbo.Tests t ON ts.TestId = t.Id
This view just adds an attempt number to student responses to questions, and I thought this would be a perfect scenario for an indexed view, but SQL Server doesn't support indexes on views with window functions.
I could generate a cache table manually, but I want this to be low maintenance so I don't have to remember to do something like that:
For example, perhaps I could create some kind of trigger (I'm not familiar with triggers) that inserts the view into a cache table when the base table is changed... which is basically what an index on a view is supposed to do under the hood (although more efficiency because it can update the index rather than completely replacing it when the base table data changes).