How to improve the performance of SQL query in Oracle DB - sql

one of the tab is broken in the Application (when hit that tab it keeps on rotating and calling the particular service and getting 404 error after sometime). we created indexes for few columns in Application DB to see if that improves performance. Creating indexes didn't make much difference.
so We planned to rewrite the sql query to improve the performance and fix it.
select distinct
editedUser.*
from
users editedUser,
relationship_ref editedUserRelationship,
users approvingUser,
user_role approvingUserRoles,
role_permission approvingUserRolePermissions,
account approvingUserAccount
where
approvingUser.user_id = 175263
and approvingUser.user_id = approvingUserRoles.user_id
and approvingUserRoles.role_id = approvingUserRolePermissions.role_id
and approvingUserRoles.user_role_status_id = 2
and editedUserRelationship.relationship_id =
editedUser.submitted_relationship_id
and (approvingUser.account_id = approvingUserAccount.account_id
or approvingUser.account_id is null)
and editedUser.review_status = 'R'
and approvingUserRolePermissions.permission_id =
editedUserRelationship.view_pending_permission_id;
It is taking nearly 6 mins.So can one please suggest how to use the proper joins in this query. It has 36 columns and 30,000 records.

Rearranged in a style that is easier to read (to me at least), I get this:
select distinct ed.*
from users ap
join user_role ro
on ro.user_id = ap.user_id
join account ac
on ac.account_id = ap.account_id or ap.account_id is null
join role_permission rp
on rp.role_id = ro.role_id
join relationship_ref re
on re.view_pending_permission_id = rp.permission_id
join users ed
on ed.submitted_relationship_id = re.relationship_id
where au.user_id = 175263
and ro.user_role_status_id = 2
and ed.review_status = 'R'
How many approving users have null account_id? For any of those, you retrieve all accounts in the system. Is that actually the business requirement? I'm not sure that it makes any sense. The query does not make any further use of that table, so perhaps you can remove the account join entirely.

Related

script optimization in sql

I am running simple select script, which inner join with other 3 table . all the tables are big ( lots of data ) its taking around 20 sec to run. want to optimized it.
I tried to used nolock , but not much deference
SELECT RR.ReportID,
RR.RequestFormat,
RRP.SequenceNumber,
RRP.ParameterName,
RRP.ParameterValue
CASE WHEN RP.ParameterLabelOvrrd IS NULL THEN P.ParameterLabel ELSE .ParameterLabelOvrrd END AS ParameterLabelChosen,
RRP.ParameterValueEntered
FROM ReportRequestParameters AS RRP WITH (NOLOCK)
INNER JOIN ReportRequests AS RR WITH (NOLOCK) ON RRP.RequestID = RR.RequestID
INNER JOIN ReportParameter AS RP WITH (NOLOCK) ON RP.ReportID = RR.ReportID
AND RP.SequenceNumber = RRP.SequenceNumber
INNER JOIN Parameter AS P WITH (NOLOCK) ON P.ParameterID = RP.ParameterID
WHERE RRP.RequestID = '2226765'
ORDER BY SequenceNumber;
Please advice.
This is your query:
SELECT RR.ReportID, RR.RequestFormat, RRP.SequenceNumber,
RRP.ParameterName, RRP.ParameterValue
COALESCE(RP.ParameterLabelOvrrd, P.ParameterLabel) as ParameterLabelChosen,
RRP.ParameterValueEntered
FROM ReportRequestParameters RRP JOIN
ReportRequests RR
ON RRP.RequestID = RR.RequestID JOIN
ReportParameter RP
ON RP.ReportID = RR.ReportID AND
RP.SequenceNumber = RRP.SequenceNumber JOIN
Parameter P
ON P.ParameterID = RP.ParameterID
WHERE RRP.RequestID = 2226765
ORDER BY RRP.SequenceNumber;
I have removed the single quotes on 2226765, assuming that the id is a number. Mixing types can impede the optimizer.
Then, I recommend an index on ReportRequestParameters(RequestID, SequenceNumber). I assume the other tables have indexes on the appropriate columns, but these are:
ReportRequests(RequestID, ReportID, SequenceNumber)
ReportParameter(ReportID, SequenceNumber, ParameterID)
Parameter(ParameterID)
I strongly advise you not to use nolock, unless you know what you are doing. Aaron Bertrand has a good blog post on this subject.
I would suggest running with the execution plan turned on and see if SSMS can advise you on additional indexing.
Other than that your query looks straight-forward, nothing code wise that is going to help make it faster, other than perhaps getting rid of the case statement and definitely getting rid of the NOLOCK statements.

SQL Server : multi-join with tuple IN clause

I'm trying to join 4 tables that have a somewhat complex relationship. Because of where this will be used, it needs to be contained in a single query, but I'm having trouble since the primary query and the IN clause query both join 2 tables together and the lookup is on two columns.
The goal is to input a SalesNum and SalesType and have it return the Price
Tables and relationships:
sdShipping
SalesNum[1]
SalesType[2]
Weight[3]
sdSales
SalesNum[1]
SalesType[2]
Zip[4]
spZones
Zip[4]
Zone[5]
spPrices
Zone[5]
Price
Weight[3]
Here's my latest attempt in T-SQL:
SELECT
spp.Price
FROM
spZones AS spz
LEFT OUTER JOIN
spPrices AS spp ON spz.Zone = spp.Zone
WHERE
(spp.Weight, spz.Zip) IN (SELECT ship.Weight, sales.Zip
FROM sdShipping AS ship
LEFT OUTER JOIN sdSales AS sales ON sales.SalesNum = ship.SalesNum
AND sales.SalesType = ship.SalesType
WHERE sales.SalesNum = (?)
AND ship.SalesType = (?));
SQL Server Management Studio says I have an error in my syntax near ',' (appropriately useless error message). Does anybody have any idea whether this is even allowed in Microsoft's version of SQL? Is there perhaps another way to accomplish it? I've seen the multi-key IN questions answered on here, but never in the case where both sides require a JOIN.
Many databases do support IN on tuples. SQL Server is not one of them.
Use EXISTS instead:
SELECT spp.Price
FROM spZones spz LEFT OUTER JOIN
spPrices spp
ON spz.Zone = spp.Zone
WHERE EXISTS (SELECT 1
FROM sdShipping ship LEFT JOIN
sdSales sales
ON sales.SalesNum = ship.SalesNum AND
sales.SalesType = ship.SalesType
WHERE spp.Weight = ship.Weight AND spz.Zip = sales.Zip AND
sales.SalesNum = (?) AND
ship.SalesType = (?)
);

SQL - SELECT with JOINED Table

I'm trying to do a selection from the user tabel. For each user i would like to sum the Timediff for all posts in tblregtime for current user for given date parameters.
Problem is that i need to get information even if there is no registration done at the given date for current user. If no registration i need output that current user has TotalDiff=0. My current SQL doesn't work in this way. It will just give the fname,lname and TotalDiff if there is a post in tblregtime
sql:
select u.fname,u.lname, sum(cast(TIME_TO_SEC(TIMEDIFF(r.edate,r.sdate)) AS UNSIGNED)-r.break_time) as TotalDiff
from tbluser u
RIGHT OUTER JOIN tblregtime r on r.userid=u.id where r.projectid=21
and year(r.sdate)=2013 and month(r.sdate)=10 and day(r.sdate)=7
If you are trying to keep everything in tbluser, then you want a left join instead of a right join. However, you also need to move the where conditions into the on clause. Otherwise, when there is no match, the comparisons will fail (because the r. values will be NULL):
select u.fname,u.lname,
sum(cast(TIME_TO_SEC(TIMEDIFF(r.edate,r.sdate)) AS UNSIGNED)-r.break_time) as TotalDiff
from tbluser u LEFT JOIN
tblregtime r
on r.userid = u.id and
r.projectid = 21 and
year(r.sdate) = 2013 and month(r.sdate) = 10 and day(r.sdate) = 7;
I would also recommend that you change the final date comparison to something like:
r.sdate = '2013-10-07'
This form would allow the use of an index on r.sdate. As you have written it, the SQL engine (at least the SQL engines I am familiar with) would not be smart enough to use the index.

Optimize SQL query with many left join

I have a SQL query with many left joins
SELECT COUNT(DISTINCT po.o_id)
FROM T_PROPOSAL_INFO po
LEFT JOIN T_PLAN_TYPE tp ON tp.plan_type_id = po.Plan_Type_Fk
LEFT JOIN T_PRODUCT_TYPE pt ON pt.PRODUCT_TYPE_ID = po.cust_product_type_fk
LEFT JOIN T_PROPOSAL_TYPE prt ON prt.PROPTYPE_ID = po.proposal_type_fk
LEFT JOIN T_BUSINESS_SOURCE bs ON bs.BUSINESS_SOURCE_ID = po.CONT_AGT_BRK_CHANNEL_FK
LEFT JOIN T_USER ur ON ur.Id = po.user_id_fk
LEFT JOIN T_ROLES ro ON ur.roleid_fk = ro.Role_Id
LEFT JOIN T_UNDERWRITING_DECISION und ON und.O_Id = po.decision_id_fk
LEFT JOIN T_STATUS st ON st.STATUS_ID = po.piv_uw_status_fk
LEFT OUTER JOIN T_MEMBER_INFO mi ON mi.proposal_info_fk = po.O_ID
WHERE 1 = 1
AND po.CUST_APP_NO LIKE '%100010233976%'
AND 1 = 1
AND po.IS_STP <> 1
AND po.PIV_UW_STATUS_FK != 10
The performance seems to be not good and I would like to optimize the query.
Any suggestions please?
Try this one -
SELECT COUNT(DISTINCT po.o_id)
FROM T_PROPOSAL_INFO po
WHERE PO.CUST_APP_NO LIKE '%100010233976%'
AND PO.IS_STP <> 1
AND po.PIV_UW_STATUS_FK != 10
First, check your indexes. Are they old? Did they get fragmented? Do they need rebuilding?
Then, check your "execution plan" (varies depending on the SQL Engine): are all joins properly understood? Are some of them 'out of order'? Do some of them transfer too many data?
Then, check your plan and indexes: are all important columns covered? Are there any outstandingly lengthy table scans or joins? Are the columns in indexes IN ORDER with the query?
Then, revise your query:
- can you extract some parts that normally would quickly generate small rowset?
- can you add new columns to indexes so join/filter expressions will get covered?
- or reorder them so they match the query better?
And, supporting the solution from #Devart:
Can you eliminate some tables on the way? does the where touch the other tables at all? does the data in the other tables modify the count significantly? If neither SELECT nor WHERE never touches the other joined columns, and if the COUNT exact value is not that important (i.e. does that T_PROPOSAL_INFO exist?) then you might remove all the joins completely, as Devart suggested. LEFTJOINs never reduce the number of rows. They only copy/expand/multiply the rows.

SQL Need advice how to add timestamp to this query

I have this code:
select Users.phoneMac, Users.apMac, Locations.Lon, Locations.Lat
from Locations, Users
inner join (
select u.phoneMac, max(u.strenght) as most
from Users u, Locations l
where u.apMac = l.apMac
group by u.phoneMac
) as ij on ij.phoneMac=Users.phoneMac and Users.strenght = ij.most
where Locations.apMac = Users.apMac;
It worked for me fine but when I added more data to users table this query calculated results from all the data and I wanted to get results just from latest data. So I added timestamp to Users table.
So can you help me fix this code so it first take only data from latest timestamp for every user(users.phoneMac)(there can be more then 1 row of data for same phoneMac) and then do the rest of calculations.
You're already picking the max value of the "strenght" field and joining on that, so why not use the same approach again for your timestamp field? Something like:
SELECT Users.phoneMac, Users.apMac, Locations.Lon, Locations.Lat
FROM Locations
INNER JOIN Users
ON Users.apMac = Locations.apMac
INNER JOIN (
SELECT u.phoneMac, max(u.strenght) AS most
FROM Locations l
INNER JOIN Users u ON u.apMac = l.apMac
GROUP BY u.phoneMac) AS ij
ON ij.phoneMac = Users.phoneMac
AND Users.strenght = ij.most
INNER JOIN (
SELECT u2.phoneMac, max(u2.timestampfield) AS latest
FROM Locations l2
INNER JOIN Users u2 ON u2.apMac = l2.apMac
GROUP BY u2.phoneMac) AS ijk
ON ijk.phoneMac = Users.phoneMac
AND Users.timestampfield = ij.latest;
(By the way, using the old join syntax with comma and the WHERE clause makes it harder to understand the logic, and occasionally makes the logic wrong. The new join syntax with ON is really a lot better.)