I need help to fine tune below Oracle SQL query, which is running for long hours.
SELECT
MIN( E.MKT_PRC),
MAX( E.MKT_PRC)
FROM
GL_VESTINGPRIME_RPT VP
INNER JOIN GRANTZ G
ON G.GRANT_NUM = VP.GRANT_NUM
OR G.XFER_ORIG = VP.GRANT_NUM
INNER JOIN EXERCISE E
ON E.GRANT_NUM = G.GRANT_NUM
WHERE
VP.RUNTIME_ID = :B4
AND VP.PLAN_NUM = NVL(:B3, VP.PLAN_NUM)
AND E.EXER_DT BETWEEN :B2 + 1
AND :B1
The problem is probably the "or" in the join condition. This tends to be optimized very poorly.
This version does an explicit union of the two results:
SELECT MIN( E.MKT_PRC), MAX( E.MKT_PRC)
from ((select E.MKT_PRC, E.MKT_PRC
FROM GL_VESTINGPRIME_RPT VP INNER JOIN GRANTZ G
ON G.GRANT_NUM = VP.GRANT_NUM OR
EXERCISE E
ON E.GRANT_NUM = G.GRANT_NUM
WHERE VP.RUNTIME_ID = :B4 AND
VP.PLAN_NUM = NVL(:B3, VP.PLAN_NUM) AND
E.EXER_DT BETWEEN :B2 + 1 AND :B1
) union all
(select E.MKT_PRC, E.MKT_PRC
FROM GL_VESTINGPRIME_RPT VP INNER JOIN GRANTZ G
ON G.XFER_ORIG = VP.GRANT_NUM OR
EXERCISE E
ON E.GRANT_NUM = G.GRANT_NUM
WHERE VP.RUNTIME_ID = :B4 AND
VP.PLAN_NUM = NVL(:B3, VP.PLAN_NUM) AND
E.EXER_DT BETWEEN :B2 + 1 AND :B1
)) t
If you have appropriate indexes on your tables, this should be pretty fast.
Related
I'm in the middle of a warehouse migration from Oracle to SQL Datawarehouse Azure and ran into an issue with this query.
The original query from Oracle - it returns 1872520 rows.
SELECT
*
FROM
STG_REV_APPORTION_CSC_NO t1,
STG_SEP_VL t2,
STG_SEP_VL t3
WHERE
t3.BUSINESS_DATE(+) = t1.BUSINESS_DATE
AND t3.CSC_APP_NO(+) = t1.CSC_APP_NO
AND t3.JOURNEY_NO(+) = t1.JOURNEY_NO
AND t3.PURSE_TXN_CTR(+) = t1.PURSE_TXN_CTR
AND t2.BUSINESS_DATE(+) = t1.BUSINESS_DATE
AND t2.CSC_APP_NO(+) = t1.CSC_APP_NO
AND t2.JOURNEY_NO(+) = t1.JOURNEY_NO
AND
(
t2.TRIP_NO(+) + 1
)
= t1.TRIP_NO
AND
(
t2.MSG_TYPE_CD(+) = 13070
AND t3.MSG_TYPE_CD(+) = 4357
);
Taking clues from documentation, I tried query re-write to ANSI:
SELECT COUNT(*)
FROM STG_REV_APPORTION_CSC_NO t1
RIGHT OUTER JOIN STG_SEP_VL t3 ON t3.BUSINESS_DATE = t1.BUSINESS_DATE
AND t3.CSC_APP_NO = t1.CSC_APP_NO
AND t3.JOURNEY_NO = t1.JOURNEY_NO
AND t3.PURSE_TXN_CTR = t1.PURSE_TXN_CTR
RIGHT OUTER JOIN STG_SEP_VL t2 ON t2.BUSINESS_DATE = t1.BUSINESS_DATE
AND t2.CSC_APP_NO = t1.CSC_APP_NO
AND t2.JOURNEY_NO = t1.JOURNEY_NO
AND (t2.TRIP_NO + 1) = t1.TRIP_NO
WHERE t2.MSG_TYPE_CD = 13070 AND t3.MSG_TYPE_CD = 4357
It returns zero rows. The ANSI version should work on oracle instance - it returns zero rows there too.
I then tried to convert plus join to ANSI using refactor option on toad. I get the following
SELECT *
FROM STG_SEP_VL T2
RIGHT OUTER JOIN STG_REV_APPORTION_CSC_NO T1
ON (T2.BUSINESS_DATE = T1.BUSINESS_DATE)
AND (T2.CSC_APP_NO = T1.CSC_APP_NO)
AND (T2.JOURNEY_NO = T1.JOURNEY_NO)
RIGHT OUTER JOIN STG_SEP_VL T3
ON (T3.PURSE_TXN_CTR = T1.PURSE_TXN_CTR)
AND (T3.BUSINESS_DATE = T1.BUSINESS_DATE)
AND (T3.CSC_APP_NO = T1.CSC_APP_NO)
AND (T3.JOURNEY_NO = T1.JOURNEY_NO)
WHERE ( ( (T2.TRIP_NO /*(+)*/
) + 1) = T1.TRIP_NO)
AND ( ( (T2.MSG_TYPE_CD /*(+)*/
) = 13070) AND ( (T3.MSG_TYPE_CD /*(+)*/
) = 4357));
Now this query should run on Oracle and return the same number of rows before I can run it on SQL Server. But it doesn't - it returns zero rows.
I looked at the explain plan for both of these queries. Here is how (+) join plan looks like:
Here is how ANSI version of this query looks like:
Am I missing something?
Here's what I came up with:
SELECT *
FROM stg_rev_apportion_csc_no t1
LEFT JOIN stg_sep_vl t3
ON t1.business_date = t3.business_date AND
t1.csc_app_no = t3.csc_app_no AND
t1.journey_no = t3.journey_no AND
t1.purse_txn_ctr = t3.purse_txn_no AND
4357 = t3.msg_type_cd
LEFT JOIN stg_sep_vl t2
ON t1.business_date = t2.business_date AND
t1.csc_app_no = t2.csc_app_no AND
t1.journey_no = t2.journey_no AND
t1.trip_no = t2.trip_no + 1 AND
13070 = t2.msg_type_cd;
Tables t2 and t3 are outer joined to t1, so you either list t1 first and do a left join, or list t2 and t3 first and do a right join.
Without sample data it is hard to be sure but I think the where clause is to blame.
Including fields from t2 and t3 in the where clause negates the effect of the outer join, unless you also allow nulls (t2.MSG_TYPE_CD = 13070 OR 2.MSG_TYPE_CD IS NULL). Moving those filters into the join allows non-matching records into the results.
SELECT
COUNT(*)
FROM
STG_REV_APPORTION_CSC_NO t1
RIGHT OUTER JOIN STG_SEP_VL t3 ON t3.BUSINESS_DATE = t1.BUSINESS_DATE
AND t3.CSC_APP_NO = t1.CSC_APP_NO
AND t3.JOURNEY_NO = t1.JOURNEY_NO
AND t3.PURSE_TXN_CTR = t1.PURSE_TXN_CTR
AND t3.MSG_TYPE_CD = 4357
RIGHT OUTER JOIN STG_SEP_VL t2 ON t2.BUSINESS_DATE = t1.BUSINESS_DATE
AND t2.CSC_APP_NO = t1.CSC_APP_NO
AND t2.JOURNEY_NO = t1.JOURNEY_NO
AND (t2.TRIP_NO + 1) = t1.TRIP_NO
AND t2.MSG_TYPE_CD = 13070
;
I'm not 100% convinced this query is correct. I suspect the right outer joins should be replaced with left outer joins. That would return every record from t1 and only those from t2 and t3 that match.
It is difficult to find the exact reason for this mismatch but I think you have interchanged the joining condition for the column PURSE_TXN_CTR in table STG_SEP_VL.
SELECT *
FROM STG_REV_APPORTION_CSC_NO t1
RIGHT
JOIN STG_SEP_VL t2
ON t2.BUSINESS_DATE = t1.BUSINESS_DATE
AND t2.CSC_APP_NO = t1.CSC_APP_NO
AND t2.JOURNEY_NO = t1.JOURNEY_NO
AND ( t2.TRIP_NO + 1 ) = t1.TRIP_NO
RIGHT
JOIN STG_SEP_VL t3
ON t3.BUSINESS_DATE = t1.BUSINESS_DATE
AND t3.CSC_APP_NO = t1.CSC_APP_NO
AND t3.JOURNEY_NO = t1.JOURNEY_NO
AND t3.PURSE_TXN_CTR = t1.PURSE_TXN_CTR
WHERE ( t2.MSG_TYPE_CD = 13070 AND t3.MSG_TYPE_CD = 4357 );
I have a view as follows - It compiles just fine but when I try to select from it, I get a ORA-00979: not a GROUP BY expression error. If I eliminate the subquery (column 4) from the view, all works fine. Any ideas would be greatly appreciated! Thanks!!
select
l.LAB_GROUP,
l.NAME as LAB,
b.NAME as BENCH,
(select count(distinct s2.SAMPLE_NUMBER)
from SAMPLE s2 inner join TEST t2 on s2.SAMPLE_NUMBER = t2.SAMPLE_NUMBER and t2.STATUS in ('C', 'R') and s2.TEMPLATE <> 'QC_SAMPLE'
inner join LABORATORY_ENTRY le2 on t2.ANALYSIS = le2.ANALYSIS
where s2.LAB_GROUP = l.LAB_GROUP and le2.NAME = l.NAME and t2.X_BENCH = b.NAME and
((select count(t1.TEST_NUMBER)
from TEST t1
where t1.SAMPLE_NUMBER = t2.SAMPLE_NUMBER and t1.ANALYSIS = t2.ANALYSIS and t1.STATUS <> 'R') = 0)) as RFR
from LABORATORY l
inner join LABORATORY_ENTRY le on le.NAME = l.NAME
inner join X_BENCH b on b.NAME = le.X_BENCH
left join (SAMPLE s inner join TEST t on s.SAMPLE_NUMBER = t.SAMPLE_NUMBER and s.STATUS <> 'U'
and s.TEMPLATE <> 'QC_SAMPLE' and t.STATUS in ('I', 'P')) on t.ANALYSIS = le.ANALYSIS and s.LAB_GROUP = l.LAB_GROUP
left join V_LOC_DEPT_FAC ldf on ldf.LOCATION_NUMBER = s.STORAGE_LOC_NO
group by l.LAB_GROUP, l.NAME, b.NAME
If you need to use the group by (which will be the case if you add aggregating functions, but not as the query is currently written) you need to include the subquery in the group by as well. You can add this easiest by adding a SELECT outside your main query and appyling the GROUP BY at that level:
select lab_group, lab, bench, rfr
from
(
select
l.LAB_GROUP as lab_group,
l.NAME as LAB,
b.NAME as BENCH,
(select .....) as RFR
from LABORATORY l
inner join LABORATORY_ENTRY le on le.NAME = l.NAME
inner join X_BENCH b on b.NAME = le.X_BENCH
left join (SAMPLE s inner join TEST t on ...) on
t.ANALYSIS = le.ANALYSIS and
s.LAB_GROUP = l.LAB_GROUP
left join V_LOC_DEPT_FAC ldf on ldf.LOCATION_NUMBER = s.STORAGE_LOC_NO
) x
group by lab_group, lab, bench, rfr
Try removing the...
group by l.LAB_GROUP, l.NAME, b.NAME
As I don't think this is neccesary if you're doing the count within the subquery.
However, for performance reasons I would suggest rewriting your query so that you move the subquery into the FROM section rather than the SELECT one.
I'm struggling with an Oracle query. I want to send a value to a inner query but I don't succeed. My query looks as follows:
SELECT * FROM Pro u
LEFT JOIN (SELECT * FROM PROLOG d
WHERE d.Id = (SELECT MAX(Id) FROM PROLOG t
WHERE t.Project = **u.Id**
AND t.Prodstatus IN (5,40)))z ON (u.ID = z.Project)
WHERE u.Id = 22;
I want to replace the u.Id with 22. The value 22 comes from Pro u table. Please give me some hints.
I do not believe you can correlate a join to an inline view, however you can simplify your SQL statement to a simple outer join with a correlated predicate:
SELECT *
FROM pro u
LEFT OUTER JOIN prolog d
ON d.project = u.id
AND d.Id = (SELECT MAX(Id)
FROM prolog t
WHERE t.project = u.id
AND t.prodstatus IN (5,40))
WHERE u.id = 22;
You don't need to pass that value to sub query from Pro table, You can use prolog table value
SELECT * FROM Pro u
LEFT JOIN (SELECT * FROM PROLOG d
WHERE d.Id = (SELECT MAX(Id) FROM PROLOG t
WHERE t.Project = d.Project
AND t.Prodstatus IN (5,40)))z ON (u.ID = z.Project)
WHERE u.Id = 22;
I'd like to know why INNER JOINs are generated instead of LEFT and why the whole view is selected before join instead of just adding LEFT JOIN view.
I'm trying to post a table of information which is spread out over several tables. Basically I want to search by the date and return all the information for events happening today, yesterday, this month - whatever the user selects. The query is quite long. I added DefaultIfEmpty to all the tables except the main one in an attempt to get LEFT JOINs but it just made a mess.
using (TransitEntities t = new TransitEntities())
{
var charters = from c in t.tblCharters
join v in t.tblChartVehicles.DefaultIfEmpty()
on c.Veh
equals v.ChartVehID
join n in t.tblNACharters.DefaultIfEmpty()
on c.Dpt.Substring(c.Dpt.Length - 1)
equals SqlFunctions.StringConvert((double)n.NAID)
join r in t.tblChartReqs.DefaultIfEmpty()
on c.ChartReqID
equals r.ChartReqID
join f in t.tblCharterCustomers.DefaultIfEmpty()
on c.Dpt
equals (f.DptID == "NONAFF" ? SqlFunctions.StringConvert((double)f.CustID) : f.DptID)
join d in t.tblChartReqDocs.DefaultIfEmpty()
on c.Attach
equals SqlFunctions.StringConvert((double)d.DocID)
join s in t.tblChartSupAttaches.DefaultIfEmpty()
on c.SupAttach
equals SqlFunctions.StringConvert((double)s.DocID)
join p in (from e in t.v_EmpData select new {e.UIN, e.First, e.Last}).DefaultIfEmpty()
on c.TakenUIN
equals p.UIN
where c.BeginTime > EntityFunctions.AddYears(DateTime.Now,-1)
select new
{
ChartID = c.ChartID,
Status = c.Status,
...
Website = r.Website,
};
//select today's events
gvCharters.DataSource = charters.Where(row => (row.BeginTime.Value >= midnight && row.BeginTime.Value < midnight1));
This results in very convoluted SQL:
SELECT
[Extent1].[ChartID] AS [ChartID],
[Extent1].[Status] AS [Status],
...
[Join5].[Website] AS [Website],
FROM [dbo].[tblCharters] AS [Extent1]
INNER JOIN (SELECT [Extent2].[ChartVehID] AS [ChartVehID], [Extent2].[Descr] AS [Descr]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
LEFT OUTER JOIN [dbo].[tblChartVehicles] AS [Extent2] ON 1 = 1 ) AS [Join1] ON ([Extent1].[Veh] = [Join1].[ChartVehID]) OR (([Extent1].[Veh] IS NULL) AND ([Join1].[ChartVehID] IS NULL))
INNER JOIN (SELECT [Extent3].[NAID] AS [NAID], [Extent3].[Descr] AS [Descr]
FROM ( SELECT 1 AS X ) AS [SingleRowTable2]
LEFT OUTER JOIN [dbo].[tblNACharter] AS [Extent3] ON 1 = 1 ) AS [Join3] ON ((SUBSTRING([Extent1].[Dpt], ((LEN([Extent1].[Dpt])) - 1) + 1, (LEN([Extent1].[Dpt])) - ((LEN([Extent1].[Dpt])) - 1))) = (STR( CAST( [Join3].[NAID] AS float)))) OR ((SUBSTRING([Extent1].[Dpt], ((LEN([Extent1].[Dpt])) - 1) + 1, (LEN([Extent1].[Dpt])) - ((LEN([Extent1].[Dpt])) - 1)) IS NULL) AND (STR( CAST( [Join3].[NAID] AS float)) IS NULL))
INNER JOIN (SELECT [Extent4].[ChartReqID] AS [ChartReqID], [Extent4].[Event] AS [Event], [Extent4].[ContactName] AS [ContactName], [Extent4].[ContactPhone] AS [ContactPhone], [Extent4].[Website] AS [Website]
FROM ( SELECT 1 AS X ) AS [SingleRowTable3]
LEFT OUTER JOIN [dbo].[tblChartReq] AS [Extent4] ON 1 = 1 ) AS [Join5] ON ([Extent1].[ChartReqID] = [Join5].[ChartReqID]) OR (([Extent1].[ChartReqID] IS NULL) AND ([Join5].[ChartReqID] IS NULL))
INNER JOIN (SELECT [Extent5].[CustID] AS [CustID], [Extent5].[Dpt] AS [Dpt], [Extent5].[DptID] AS [DptID]
FROM ( SELECT 1 AS X ) AS [SingleRowTable4]
LEFT OUTER JOIN [dbo].[tblCharterCustomers] AS [Extent5] ON 1 = 1 ) AS [Join7] ON ([Extent1].[Dpt] = (CASE WHEN (N'NONAFF' = [Join7].[DptID]) THEN STR( CAST( [Join7].[CustID] AS float)) ELSE [Join7].[DptID] END)) OR (([Extent1].[Dpt] IS NULL) AND (CASE WHEN (N'NONAFF' = [Join7].[DptID]) THEN STR( CAST( [Join7].[CustID] AS float)) ELSE [Join7].[DptID] END IS NULL))
INNER JOIN (SELECT [Extent6].[DocID] AS [DocID], [Extent6].[FileName] AS [FileName]
FROM ( SELECT 1 AS X ) AS [SingleRowTable5]
LEFT OUTER JOIN [dbo].[tblChartReqDocs] AS [Extent6] ON 1 = 1 ) AS [Join9] ON ([Extent1].[Attach] = (STR( CAST( [Join9].[DocID] AS float)))) OR (([Extent1].[Attach] IS NULL) AND (STR( CAST( [Join9].[DocID] AS float)) IS NULL))
INNER JOIN (SELECT [Extent7].[DocID] AS [DocID], [Extent7].[FileName] AS [FileName]
FROM ( SELECT 1 AS X ) AS [SingleRowTable6]
LEFT OUTER JOIN [dbo].[tblChartSupAttach] AS [Extent7] ON 1 = 1 ) AS [Join11] ON ([Extent1].[SupAttach] = (STR( CAST( [Join11].[DocID] AS float)))) OR (([Extent1].[SupAttach] IS NULL) AND (STR( CAST( [Join11].[DocID] AS float)) IS NULL))
INNER JOIN (SELECT [Extent8].[First] AS [First], [Extent8].[Last] AS [Last], [Extent8].[UIN] AS [UIN]
FROM ( SELECT 1 AS X ) AS [SingleRowTable7]
LEFT OUTER JOIN (SELECT
[v_EmpData].[First] AS [First],
[v_EmpData].[Last] AS [Last],
[v_EmpData].[Legal] AS [Legal],
[v_EmpData].[Name] AS [Name],
[v_EmpData].[Email] AS [Email],
[v_EmpData].[UIN] AS [UIN],
[v_EmpData].[UserNM] AS [UserNM],
[v_EmpData].[Worker] AS [Worker],
[v_EmpData].[SUPERVISORNUM] AS [SUPERVISORNUM],
[v_EmpData].[Supervisor] AS [Supervisor],
[v_EmpData].[EmpArea] AS [EmpArea],
[v_EmpData].[Title] AS [Title],
[v_EmpData].[FullName] AS [FullName],
[v_EmpData].[HireDate] AS [HireDate],
[v_EmpData].[WORKERTYPENM] AS [WORKERTYPENM],
[v_EmpData].[Birth] AS [Birth],
[v_EmpData].[HOMESTREET] AS [HOMESTREET],
[v_EmpData].[HOMECITY] AS [HOMECITY],
[v_EmpData].[HOMEZIP] AS [HOMEZIP],
[v_EmpData].[HOMESTATE] AS [HOMESTATE],
[v_EmpData].[PicID] AS [PicID],
[v_EmpData].[WorkPhone] AS [WorkPhone],
[v_EmpData].[HomePhone] AS [HomePhone],
[v_EmpData].[WorkCellPhone] AS [WorkCellPhone]
FROM [dbo].[v_EmpData] AS [v_EmpData]) AS [Extent8] ON 1 = 1 ) AS [Join13] ON ([Extent1].[TakenUIN] = [Join13].[UIN]) OR (([Extent1].[TakenUIN] IS NULL) AND ([Join13].[UIN] IS NULL))
WHERE ([Extent1].[BeginTime] > (DATEADD (year, -1, SysDateTime())))
AND ('C' <> [Extent1].[Status])
AND ([Extent1].[BeginTime] >= '11/28/2012 12:00:00 AM')
AND ([Extent1].[BeginTime] < '11/29/2012 12:00:00 AM')
This is what my original SQL query looked like and what I was hoping it would be closer to:
SELECT
ChartID,
c.Status,
...
r.Website As Website,
FROM tblChartersNew c
LEFT JOIN (SELECT [Dpt],[DptID] FROM [DRVRDiscipline].[dbo].[tblCharterCustomers] Where Valid=1 and DptID <> 'NONAFF' UNION SELECT Dpt, CONVERT(nvarchar,CustID) AS DptID FROM [DRVRDiscipline].[dbo].[tblCharterCustomers] Where Valid=1 and DptID = 'NONAFF') f
ON RTRIM(c.Dpt) = f.DptID LEFT JOIN [tskronos].WfcSuite.dbo.VP_ALLPERSONV42 p ON p.PersonNUM = c.TakenUIN
LEFT JOIN tblChartVehicles v ON v.ChartVehID = c.Veh
LEFT JOIN tblNACharter n ON CAST(n.NAID AS varchar) = RIGHT(c.Dpt, LEN(c.Dpt)-1)
LEFT JOIN tblChartReq r
ON r.ChartReqID = c.ChartReqID
WHERE CONVERT(datetime,CONVERT(char(10),c.BeginTime,101)) = (SELECT TOP 1 CONVERT(datetime,CONVERT(char(10),BeginTime,101)) from tblChartersNew WHERE CONVERT(datetime,CONVERT(char(10),BeginTime,101)) >= CONVERT(datetime,CONVERT(char(10),GETDATE(),101)) ORDER BY BeginTime)
AND NOT c.ChartReqID IS NULL
ORDER BY BeginTime, ISNULL(f.Dpt,c.Dpt)
I also add a Select New on the view to avoid selecting all of the columns when I only need three but it didn't seem to make a difference. Instead of adding LEFT JOIN v_EmpData it adds LEFT OUTER JOIN and then selects all of the columns in the view. It seems to be ignoring the Select New.
I'd really like to transition to using Linq to Entities for the majority of my queries because intellisense makes it so much easier to make sure it's right and to have variations of queries without having to have separate functions for each but maybe I need to stick with plain old SQL. I know just enough to make a big mess. Any suggestions?
For complex queries like what you need.
I would suggest looking into FunctionImport.
MSDN Function Import
This would save you the headache of creating a LINQ that would be 1:1 to your expected generated SQL.
I want to convert this code to linq:
select t1.title, COUNT(*)as num
from t1 INNER join t2 on t2.gId = t1.Id
group by t1.title, t1.cId
having t1.cId = 2
I tried this below code:
from p in db.t1s join r in db.t2s on p.Id equals r.gId
where p.cId == 2
group p by p.title into g
select new{ name = from o in g select o.title, num = g.Count()}
But this doesn't return COUNT correctly.
please guide me how can I solve the problem
thanks
Without sample data its hard to get it right, but try this snippet
from p in db.t1s
join r in db.t2s on p.Id equals r.gId
where p.cId == 2
group p by new {p.title, p.cId} into grouped
select new{ name = grouped.Key.title, num = grouped.Count()}
Also, note that this sql:
select t1.title, COUNT(*)as num
from t1 INNER join t2 on t2.gId = t1.Id
group by t1.title, t1.cId
having t1.cId = 2
Will always return 1 as result of COUNT(*). The reason is that you have filtering t1.cId = 2 and grouping by t1.cId as second parameter.