How to write the mdx equivalent to following sql - sql-server

I have the following SQL code in the Stored procedure in SQL server which feeds data to the reports. Now we are implementing the SSAS cube to process the data. I have a Fact table which has all the measures and degenerate dimensions. We are facing issues while converting below calculation into the MDX. This calculation purely depends on the Start Date and End Date parameters so it can not be done at the ETL time. It has to be performed when the client selects the Start and End date on the report.
Can any one help/ guide me how i should convert below code into MDX? As a novice in MDX world, i am facing following issues
a. Lack of temp table like structure which help storing the temp data during processing.
b. Stored procedure are supported as the CLR or COM objects. Why SQL/ MDX procedure are missing?
Is it possible to perform the following calculations from the cube data?
Select
rundate ,Entity ,MasterEntity ,TransactionDate ,BuyOrSell ,Quantity2 ,Quantity1 ,Quantity3 ,Quantity4 ,Profit1
,Profit2 ,Profit3 ,transactionId ,closingtransactionId
into #TempData from [Fact_Test]
Where datediff(d,#StartDate,rundate)>=0 and datediff(d,rundate,#EndDate )>=0 order by rundate
Select Rundate,TransactionDate,BuyOrSell,Quantity2,transactionId,closingtransactionId
into #ClosedTransactions from #TempData Where BuyOrSell = 'C'
Select
affected.Rundate AS 'OpenTransactionDate', affected.BuyOrSell as 'OpenTransaction_BuyOrSell',
affected.Quantity3 as 'OpenTransaction_Quantity1' , affected.Quantity1 as 'OpenTransaction_Quantity2',
affected.Profit2 as 'OpenTransaction_Profit2', affected.transactionId as 'OpenTransaction_transactionId',
affected.closingtransactionId as 'ClosedTaxlot_ClosedingtransactionId'
into #AffectedOpenTransaction from #TempData affected
where affected.transactionId in (select distinct transactionId from #ClosedTransactions)
and affected.rundate between #StartDate and #PreviousDateFromEndDate and affected.BuyOrSell = 'O'
select
CLOSED.Rundate as 'ClosingDate', CLOSED.Quantity2 as 'ClosedQuantity',
CLOSED.transactionId as 'closingtransactionId', AFFECTED.OpenTransaction_transactionId as 'OpeningtransactionId',
AFFECTED.OpenTransaction_Quantity2 as 'AffectedQuantity', AFFECTED.OpenTransaction_Profit2 as 'Affected_Profit2',
AFFECTED.OpenTransactionDate as 'AffectedTransactionDate',Convert(float,0) as 'Profit2_Adjusted'
into #TempProfit2
from #ClosedTransactions CLOSED
inner join #AffectedOpenTransaction AFFECTED
on ( CLOSED.transactionId = AFFECTED.OpenTaxlot_transactionId and datediff(d,AFFECTED.OpenTransactionDate,CLOSED.Rundate)>0 )
update #TempProfit2
set Profit2_Adjusted = case when AffectedQuantity <>0 then(ClosedQuantity * 1.0/(AffectedQuantity*1.0)) * Affected_Profit2 else 0.0 end
select
closingtransactionId, ClosingDate,
Sum( Case When datediff(d,AffectedTransactionDate,ClosingDate)> 0 Then Convert(float,Profit2_Adjusted) else 0.0 End ) as UnrealizedPNL_Adjusted
into #Profit2_Adjusted
from
#TempProfit2
group by closingtransactionId,ClosingDate
select
MasterEntity,Entity, sum(Profit1 + isnull(Profit2_Adjusted,0)) as FinalProfit1,
sum(Profit2 - isnull(Profit2_Adjusted,0)) as FinalProfit12,sum(Profit3) as Profit3,
sum(Profit1 + isnull(Profit2_Adjusted,0)) + sum(Profit2 - isnull(Profit2_Adjusted,0)) + sum(Profit3) as TotalMTMPNL
from #TempData
left outer join #Profit2_Adjusted
on (#TempData.transactionId = #Profit2_Adjusted.closingtransactionId
and datediff(d, #Profit2_Adjusted.ClosingDate, #TempData.Rundate) = 0 and #TempData.BuyOrSell = 'C' )
group by MasterEntity,Entity
order by MasterEntity,Entity

Related

Inserting records into the table based on the last insert

I am executing a SSIS SQL task that would execute a stored procedure and that would read records from one table and dump the records in the destination table
Following is the statement that I am using to achieve that
INSERT INTO dbo.GetParties
EXEC dbo.getParties_SSIS
I am trying to make the stored procedure smart enough so that when it is executed the second or n number of times it checks for the max of the destination table and fetches records based on that from the source table.
How do I handle the first time the stored procedure is executed as there would be no records in the destination table? What's is the best way of handling that?
This is my procedure:
CREATE PROCEDURE [dbo].[getParties_SSIS]
AS
DECLARE #lastCompanyId INT
SELECT #lastCompanyId = MAX(companyId) FROM GetParties
SELECT
c.companyId,
cf.identifierValue dunsId,
c.companyName,
ct.companyTypeId, ct.companyTypeName,
cst.companyStatusTypeId, cst.companyStatusTypeName,
si.simpleIndustryId, si.simpleIndustryDescription,
ROW_NUMBER() OVER (ORDER BY c.companyId) AS position
FROM
ciqcompany c
LEFT JOIN
ciqSimpleIndustry si ON si.simpleIndustryId = c.simpleIndustryId
LEFT JOIN
ciqCompanyType ct ON ct.companyTypeId = c.companyTypeId
LEFT JOIN
ciqCompanyStatusType cst ON cst.companyStatusTypeId = c.companyStatusTypeId
LEFT JOIN
ciqCompanyCrossRef cf ON cf.companyId = c.companyId
AND cf.identifierTypeId = 6 AND cf.primaryFlag = 1
WHERE
c.companyId > #lastCompanyId
You can solve quite easily with ISNULL or COALESCE.
WHERE c.companyId > ISNULL(#lastCompanyId, 0)
or
WHERE c.companyId > COALESCE(#lastCompanyId, 0)

Running 3-Tier T-SQL Query for Report

First question here so please excuse any mistakes...
I am trying to write a SQL Query for an SSRS report and I am totally confused when it comes to my joins.
Background: I have 3 tables that are relevant
Publishers - This is essentially a list of people
Publisher Reports - This is a list of records (related to the Publisher table) that details the work they have completed in a month period.
Report Months - This is a list of records (related to the Publisher Reports table) that relates to a specific month and year. On these records they have an indicator to show whether they relate to the previous six month period.
What i am trying to do is get a list of Publishers who have not submitted a publisher report that is related to a Report Month record within the last 6 months. My desired output is a list of Publishers in on column with the Report Month(s) that they are missing in the next column.
I am really struggling how to do it... I had thought of it as a three step process...
--STEP 1 - Get list of report months that are included in the last 6 months
WITH ACTIVE6MREPM AS
(
SELECT r.jajw_name,
r.jajw_CalendarDate
FROM jajw_reportmonthBase r
WHERE r.jajw_IncludedIn6MonthReport = '1'
),
--STEP 2 - Get list of all publishers
ACTIVEPUBS AS
(
SELECT c.FullName,
c.ContactId
FROM ContactBase c
WHERE c.statecode = '0'
AND c.jajw_CongregationAssignment != 640840001
AND c.jajw_CongregationAssignment != 640840006
AND c.jajw_CongregationAssignment != 640840005
--AND q.jajw_FieldServiceGroups = (#Field_Service_Group)
),
--STEP 3 - Get List of Publisher Reports for the selected Report Months
RELEVANTREPORTS AS
(
SELECT r.jajw_reportId AS Publisher_Report_GUID,
r.jajw_PublisherId AS Publisher_GUID,
r.jajw_ReportMonthId AS ReportMonth_GUID,
m.jajw_name AS ReportMonth_Name
FROM jajw_reportBase r
INNER JOIN jajw_reportmonthBase m ON r.jajw_ReportMonthId = m.jajw_reportmonthId
WHERE r.jajw_ReportPeriod6Month = '1'
ORDER BY m.jajw_CalendarDate
After these three, I want to create my list as described above and this is the bit that has me stumped! Any help would be greatly appreciated!
Thanks!
I think you can shorten your code a lot... here's a shot at it without having test data... be sure to read the comments and add in the join condition and check the where clause.
with cte as(
SELECT r.jajw_reportId AS Publisher_Report_GUID,
r.jajw_PublisherId AS Publisher_GUID,
r.jajw_ReportMonthId AS ReportMonth_GUID,
m.jajw_name AS ReportMonth_Name,
c.FullName,
c.ContactId
FROM jajw_reportBase r
INNER JOIN jajw_reportmonthBase m ON
r.jajw_ReportMonthId = m.jajw_reportmonthId
INNER JOIN ContactBase c on --what ever condition is appropiate
WHERE r.jajw_ReportPeriod6Month = '1'
and m.jajw_IncludedIn6MonthReport = '1' --maybe put this here, or does the above do the same thing?
ORDER BY m.jajw_CalendarDate)
select
p2.FullName,
p2.ReportMonth_Name
from cte p
right join(select distinct
ReportMonth_Name, FullName
from ContactBase
left join jajw_reportmonthBase on 1=1) p2 on p2.FullName = p.FullName and p2.ReportMonth_Name = p.ReportMonth_Name
where
ContactId in (select ContactId from cte group by ContactId having count(distinct ReportMonth_GUID) < 6)
and p.FullName is null
Here is an example using test data.
declare #pub table (pubname varchar(56), ReportMonthName varchar(16))
insert into #pub (pubname,ReportMonthName) values
('a','Jan'),
('a','Feb'),
('a','Mar'),
('a','Apr'),
--publisher a is missing May and Jun
('b','Jan'),
('b','Feb'),
('b','Mar'),
('b','Jun')
--publisher b is missing Apr and May
declare #dt table (ReportMonthName varchar(16))
insert into #dt (ReportMonthName) values
('Jan'),
('Feb'),
('Mar'),
('Apr'),
('May'),
('Jun')
select
p2.pubname
,p2.ReportMonthName
from #pub p
right join(
select distinct
p.pubname
,d.ReportMonthName
from #pub p
left join #dt d on 1=1)p2 on p2.pubname = p.pubname and p2.ReportMonthName = p.ReportMonthName where p.pubname is null

Sql Server update based on an aggregate from two tables with two variables each

this is my first time posting a question and I hope not to waste any of your time. Thanks in advance for any help.
I am attempting to calculate the distance between every zip-code in the United States and a list of 495 buildings (384 unique). I already have the latitude/longitude and an approximation for the distance. My problem comes in the form of not being sure how to update the rows in the table individually. For instance, the set function I use will change all the rows in the table to the most recently calculated value. I am using Sql Server 2008 R2, and have the following tables with the following columns: tbl_Zip_Code_Coordinates w/ Zip, Lat, Long, State, Closest_Building, Zip_ID; Tbl_Building_Coordinates w/ Zip, Lat, Long, Store_ID
Here is the code I am currently attempting
Declare #ZipID int = 1
DECLARE #ZipLat Decimal (12,6)
DECLARE #ZipLong Decimal (12,6)
while (#ZipID < 43194)
Begin
Set #ZipLat = (Select Lat from Zip_Code_Coordinates where Zip_ID = #ZipID)
Set #ZipLong= (Select Long from Zip_Code_Coordinates where Zip_ID = #ZipID)
Update Zip_Code_Coordinates
set Closest_Building = (select min(Sqrt( SQUARE((#ZipLat - Buildings_Coordinates.lat)*68.96799738887665)
+SQUARE((#ZipLong - Buildings_Coordinates.Long)*54.69366983621222)))
from Buildings_Coordinates, Zip_Code_Coordinates
where Zip_ID = #ZipID
)
set #ZipID = #ZipID + 1
end
I suppose you also need the ID for the closest building. If so, you could use the following code as is or with some modifications:
;WITH mycte
AS (SELECT Z.zipid,
B.bldgid,
(( Sqrt(Square((Z.lat - B.lat)*68.96799738887665)
+ Square((Z.long - B.long)*54.69366983621222)) )) Dist,
Row_number()
OVER (
partition BY zipid
ORDER BY ((Sqrt( Square((Z.lat - B.lat)*68.96799738887665) +
Square((
Z.long
- B.long)*54.69366983621222)))))
DistOrder
FROM Buildings_Coordinates B
CROSS JOIN Zip_Code_Coordinates Z)
UPDATE z
SET closest_buildingid = bldgid,
closest_bldgdistance = dist
FROM Zip_Code_Coordinates Z
INNER JOIN mycte C
ON Z.zipid = C.zipid
WHERE distorder = 1
I eventually found a code that worked:
Select Sub2.Zip_ID, Sub2.Distance into Temp_Table from
(
select
Sub1.Zip_ID as Zip_ID,
MIN(Sub1.Distance) as Distance from
(
SELECT
Z.Zip_ID,
((Sqrt(Square((Z.lat - S.lat) *68.96799738887665)
+ Square((Z.long - S.long)*54.69366983621222)))) Distance
FROM
Tbl_Stores_Coordinates S
CROSS JOIN Zip_Code_Coordinates Z
)Sub1
group by Sub1.Zip_ID) Sub2
Update Zip_Code_Coordinates
Set Closest_Store = temp_Table.Distance
from temp_Table
where Zip_Code_Coordinates.Zip_ID = Temp_Table.Zip_ID;
drop table temp_Table;
select * from Zip_Code_Coordinates
select * from Temp_Table
The only problem I had with this was that I couldn't pass the which Building has the shortest distance to each Zip Code through the Subquery. What I ended up doing was creating a temporary table with another query and performed a Join where the Distances and Zip_ID's were the same.

SQL query distict count using inner join

Need help ensuring the below query doesn't return inaccurate results.
select #billed = count(a.[counter]) from [dbo].cxitems a with (nolock)
inner join [dbo].cxitemhist b with (nolock) on a.[counter] = b.cxlink
where b.[eventtype] in ('BILLED','REBILLED')
and b.[datetime] between #begdate and #enddate
The query is "mostly" accurate as is, however there is a slight possibility that cxitemhist table could contain more than 1 "billed" record for given date range. I only need to count item as "Billed" once during given date range.
You can join on a sub query the limits you to one row for each combination of fields used for the join:
select #billed = count(a.[counter])
from [dbo].cxitems a
inner join (
select distinct cxlink
from [dbo].cxitemhist
where [eventtype] in ('BILLED','REBILLED')
and [datetime] between #begdate and #enddate
) b on a.[counter] = b.cxlink
You can also use the APPLY operator instead of a join here, but you'll have to check against your data to see which gives better performance.
If you only need to count records from the cxitems table, that have any corresponding records from the cxitemhist table, you can use the exists clause with a subquery.
select #billed = count(a.[counter]) from [dbo].cxitems a
where exists(select * from [dbo].cxitemhist b
where a.[counter] = b.cxlink
and b.[eventtype] in ('BILLED','REBILLED')
and b.[datetime] between #begdate and #enddate)
Cannot really say how this will affect performance, without specific data, though, but it should be comparably fast with your code.

Query runs quickly in Oracle SQL Developer, but slowly in SSRS 2008 R2

It's that simple: a query that runs in just a few seconds in SQL Developer connecting to Oracle 11g takes 15-25 minutes in SSRS 2008 R2. I haven't tried other versions of SSRS. So far I'm doing all the report execution from VS 2008.
I'm using the OLE DB Provider "OraOLEDB.Oracle.1" which in the past has seemed to give me better results than using the Oracle provider.
Here's what I've been able to determine so far:
• The delay is during the DataSet execution phase and has nothing to do with the result set or rendering time. (Proving by selecting the same rowset directly from a table I inserted it to.)
• SSRS itself is not hung up. It is truly waiting for Oracle which is where the delay is (proven by terminating the DB session from the Oracle side, which resulted in a prompt error in SSRS about the session being killed).
• I have tried direct queries with parameters in the form :Parameter. Very early versions of my query that were more simple worked okay for direct querying, but it seemed like past a certain complexity, the query would start taking forever from SSRS.
• I then switched to executing an SP that inserts the query results to a table or global temp table. This helped for a little while, getting me farther than direct querying, but again, it almost seems like increased query complexity or length eventually broke this method, too. Note: running a table-populating SP works because with option "use single transaction" checked in the DataSource options, DataSets are then run in the order of their appearance in the rdl file. DataSets that return no Fields are still run, as long as all their parameters are satisfied.
• I just tried a table-returning function and this still made no improvement, even though direct calls with literal parameters in SQL Developer return in 1-5 seconds.
• The database in question does not have statistics. It is part of a product created by a vendor and we have not had the time or management buy-in to create/update statistics. I played with the DYNAMIC_SAMPLING hint to calculate statistics on the fly and got a better execution plan: without statistics the cost-based optimizer had been poorly using a LOOP join instead of a HASH join, causing similar many-minute execution times. Thus I put in query hints to force join order and also to cause it to use the strategic hash join, bringing the execution time back down to just seconds. I did not go back and try direct querying in SSRS using these execution hints.
• I got some help from our Oracle DBA who set up a trace (or whatever the Oracle equivalent is) and he was able to see things being run, but he hasn't found anything useful so far. Unfortunately his time is limited and we haven't been able to really dig in to find out what's being executed server-side. I don't have the experience to do this quickly or the time to study up on how to do this myself. Suggestions on what to do to determine what's going on would be appreciated.
My only hypotheses are:
• The query is somehow getting a bad execution plan. E.g., improperly using a LOOP join instead of a HASH join when there are tens of thousands of "left" or outer-loop rows rather than just a few hundred.
• SSRS could be submitting the parameters as nvarchar(4000) or something instead of something reasonable, and as Oracle SP & function parameters don't have length specifications but get their execution lengths from the query call, then some process such as parameter sniffing is messing up the execution plan as in the previous point.
• The query is somehow being rewritten by SSRS/the provider. I AM using a multi-valued parameter, but not as is: the parameter is being submitted as expression Join(Parameters!MultiValuedParameter.Value, ",") so it shouldn't need any rewriting. Just a simple binding and submitting. I don't see how this could be true in the SP and the function calls, but gosh, what else could it be?
I realize it is a very complicated and lengthy query, but it does exactly what I need. It runs in 1-5 seconds depending on how much data is asked for. Some of the reasons for the complexity are:
Properly handling the comma-separated cost center list parameter
Allowing the weekly breakdown to be optional and if included, ensuring all the weeks in a month are shown even if there is no data for them.
Showing "No Invoices" when appropriate.
Allowing a variable number of summary months.
Having an optional YTD total.
Including previous/historical comparison data means I can't simply use this month's vendors, I have to show all the vendors that will be in any historical column.
Anyway, so here's the query, SP version (though I don't think it will be much help).
create or replace
PROCEDURE VendorInvoiceSummary (
FromDate IN date,
ToDate IN date,
CostCenterList IN varchar2,
IncludeWeekly IN varchar2,
ComparisonMonths IN number,
IncludeYTD IN varchar2
)
AS
BEGIN
INSERT INTO InvoiceSummary (Mo, CostCenter, Vendor, VendorName, Section, TimeUnit, Amt)
SELECT
Mo,
CostCenter,
Vendor,
VendorName,
Section,
TimeUnit,
Amt
FROM (
WITH CostCenters AS (
SELECT Substr(REGEXP_SUBSTR(CostCenterList, '[^,]+', 1, LEVEL) || ' ', 1, 15) CostCenter
FROM DUAL
CONNECT BY LEVEL <= Length(CostCenterList) - Length(Replace(CostCenterList, ',', '')) + 1
), Invoices AS (
SELECT /*+ORDERED USE_HASH(D)*/
TRUNC(I.Invoice_Dte, 'YYYY') Yr,
TRUNC(I.Invoice_Dte, 'MM') Mo,
D.Dis_Acct_Unit CostCenter,
I.Vendor,
V.Vendor_VName,
CASE
WHEN I.Invoice_Dte >= FromDate AND I.Invoice_Dte < ToDate
THEN (TRUNC(I.Invoice_Dte, 'W') - TRUNC(I.Invoice_Dte, 'MM')) / 7 + 1
ELSE 0
END WkNum,
Sum(D.To_Base_Amt) To_Base_Amt
FROM
ICCompany C
INNER JOIN APInvoice I
ON C.Company = I.Company
INNER JOIN APDistrib D
ON C.Company = D.Company
AND I.Invoice = D.Invoice
AND I.Vendor = D.Vendor
AND I.Suffix = D.Suffix
INNER JOIN CostCenters CC
ON D.Dis_Acct_Unit = CC.CostCenter
INNER JOIN APVenMast V ON I.Vendor = V.Vendor
WHERE
D.Cancel_Seq = 0
AND I.Cancel_Seq = 0
AND I.Invoice_Dte >= Least(ADD_MONTHS(FromDate, -ComparisonMonths), TRUNC(FromDate, 'YYYY'))
AND I.Invoice_Dte < ToDate
AND V.Vendor_Group = '1 ' -- index help
GROUP BY
TRUNC(I.Invoice_Dte, 'YYYY'),
TRUNC(I.Invoice_Dte, 'MM'),
D.Dis_Acct_Unit,
I.Vendor,
V.Vendor_VName,
CASE
WHEN I.Invoice_Dte >= FromDate AND I.Invoice_Dte < ToDate
THEN (TRUNC(I.Invoice_Dte, 'W') - TRUNC(I.Invoice_Dte, 'MM')) / 7 + 1
ELSE 0
END
), Months AS (
SELECT ADD_MONTHS(Least(ADD_MONTHS(FromDate, -ComparisonMonths), TRUNC(FromDate, 'YYYY')), LEVEL - 1) Mo
FROM DUAL
CONNECT BY LEVEL <= MONTHS_BETWEEN(ToDate, Least(ADD_MONTHS(FromDate, -ComparisonMonths), TRUNC(FromDate, 'YYYY')))
), Sections AS (
SELECT 1 Section, 1 StartUnit, 5 EndUnit FROM DUAL
UNION ALL SELECT 2, 0, ComparisonMonths FROM DUAL
UNION ALL SELECT 3, 1, 1 FROM DUAL WHERE IncludeYTD = 'Y'
), Vals AS (
SELECT LEVEL - 1 TimeUnit
FROM DUAL
CONNECT BY LEVEL <= (SELECT Max(EndUnit) FROM Sections) + 1
), TimeUnits AS (
SELECT S.Section, V.TimeUnit
FROM
Sections S
INNER JOIN Vals V
ON V.TimeUnit BETWEEN S.StartUnit AND S.EndUnit
), Names AS (
SELECT DISTINCT
M.Mo,
Coalesce(I.Vendor, '0') Vendor,
Coalesce(I.Vendor_VName, 'No Paid Invoices') Vendor_VName,
Coalesce(I.CostCenter, ' ') CostCenter
FROM
Months M
LEFT JOIN Invoices I
ON Least(ADD_MONTHS(M.Mo, -ComparisonMonths), TRUNC(M.Mo, 'YYYY')) < I.Mo
AND M.Mo >= I.Mo
WHERE
M.Mo >= FromDate
AND M.Mo < ToDate
)
SELECT
N.Mo,
N.CostCenter,
N.Vendor,
N.Vendor_VName VendorName,
T.Section,
T.TimeUnit,
Sum(I.To_Base_Amt) Amt
FROM
Names N
CROSS JOIN TimeUnits T
LEFT JOIN Invoices I
ON N.CostCenter = I.CostCenter
AND N.Vendor = I.Vendor
AND (
(
T.Section = 1 -- Weeks for current month
AND N.Mo = I.Mo
AND T.TimeUnit = I.WkNum
) OR (
T.Section = 2 -- Summary months
AND ADD_MONTHS(N.Mo, -T.TimeUnit) = I.Mo
) OR (
T.Section = 3 -- YTD
AND I.Mo BETWEEN TRUNC(N.Mo, 'YYYY') AND N.Mo
)
)
WHERE
N.Mo >= FromDate
AND N.Mo < ToDate
AND NOT ( -- Only 4 weeks when a month is less than 28 days long
T.Section = 2
AND T.TimeUnit = 5
AND TRUNC(N.Mo + 28, 'MM') <> N.Mo
AND I.CostCenter IS NULL
) AND (
T.Section <> 1
OR IncludeWeekly = 'Y'
)
GROUP BY
N.Mo,
N.CostCenter,
N.Vendor,
N.Vendor_VName,
T.Section,
T.TimeUnit
) X;
COMMIT;
END;
UPDATE
Even after learning all about Oracle execution plans and hints (to translate my SQL Server knowledge), I still could not get the query to run quickly in SSRS until I made it run in two steps, first to put the real table results into a GLOBAL TEMPORARY TABLE and then second to extract the data from that. DYNAMIC_SAMPLING gave me a good execution plan, which I then copied using join and access hints. Here's the final SP (it couldn't be a function because in Oracle you can't do DML in a function when that function is called inside of a SELECT statement):
Sometimes I swear it was ignoring my join hints such as swap_join_inputs and no_swap_join_inputs but from my reading apparently Oracle only ignores hints when they can't actually be used or you're doing something wrong. Fortunately, the tables swapped appropriately (as in the case of USE_NL(CC) it reliably puts the CC table as the swapped, left input, even though it's joined last).
CREATE OR REPLACE
PROCEDURE VendorInvoicesSummary (
FromDate IN date,
ToDate IN date,
CostCenterList IN varchar2,
IncludeWeekly IN varchar2,
ComparisonMonths IN number,
IncludeYTD IN varchar2
)
AS
BEGIN
INSERT INTO InvoiceTemp (Yr, Mo, CostCenter, Vendor, WkNum, Amt) -- A global temporary table
SELECT /*+LEADING(C I D CC) USE_HASH(I D) USE_NL(CC)*/
TRUNC(I.Invoice_Dte, 'YYYY') Yr,
TRUNC(I.Invoice_Dte, 'MM') Mo,
D.Dis_Acct_Unit CostCenter,
I.Vendor,
CASE
WHEN I.Invoice_Dte >= FromDate AND I.Invoice_Dte < ToDate
THEN (TRUNC(I.Invoice_Dte, 'W') - TRUNC(I.Invoice_Dte, 'MM')) / 7 + 1
ELSE 0
END WkNum,
Sum(D.To_Base_Amt) To_Base_Amt
FROM
ICCompany C
INNER JOIN APInvoice I
ON C.Company = I.Company
INNER JOIN APDistrib D
ON C.Company = D.Company
AND I.Invoice = D.Invoice
AND I.Vendor = D.Vendor
AND I.Suffix = D.Suffix
INNER JOIN (
SELECT Substr(REGEXP_SUBSTR(CostCenterList, '[^,]+', 1, LEVEL) || ' ', 1, 15) CostCenter
FROM DUAL
CONNECT BY LEVEL <= Length(CostCenterList) - Length(Replace(CostCenterList, ',', '')) + 1
) CC ON D.Dis_Acct_Unit = CC.CostCenter
WHERE
D.Cancel_Seq = 0
AND I.Cancel_Seq = 0
AND I.Invoice_Dte >= Least(ADD_MONTHS(FromDate, -ComparisonMonths), TRUNC(FromDate, 'YYYY'))
AND I.Invoice_Dte < ToDate
GROUP BY
TRUNC(I.Invoice_Dte, 'YYYY'),
TRUNC(I.Invoice_Dte, 'MM'),
D.Dis_Acct_Unit,
I.Vendor,
CASE
WHEN I.Invoice_Dte >= FromDate AND I.Invoice_Dte < ToDate
THEN (TRUNC(I.Invoice_Dte, 'W') - TRUNC(I.Invoice_Dte, 'MM')) / 7 + 1
ELSE 0
END;
INSERT INTO InvoiceSummary (Mo, CostCenter, Vendor, VendorName, Section, TimeUnit, Amt)
SELECT
Mo,
CostCenter,
Vendor,
VendorName,
Section,
TimeUnit,
Amt
FROM (
WITH Months AS (
SELECT ADD_MONTHS(Least(ADD_MONTHS(FromDate, -ComparisonMonths), TRUNC(FromDate, 'YYYY')), LEVEL - 1) Mo
FROM DUAL
CONNECT BY LEVEL <= MONTHS_BETWEEN(ToDate, Least(ADD_MONTHS(FromDate, -ComparisonMonths), TRUNC(FromDate, 'YYYY')))
), Sections AS (
SELECT 1 Section, 1 StartUnit, 5 EndUnit FROM DUAL
UNION ALL SELECT 2, 0, ComparisonMonths FROM DUAL
UNION ALL SELECT 3, 1, 1 FROM DUAL WHERE IncludeYTD = 'Y'
), Vals AS (
SELECT LEVEL - 1 TimeUnit
FROM DUAL
CONNECT BY LEVEL <= (SELECT Max(EndUnit) FROM Sections) + 1
), TimeUnits AS (
SELECT S.Section, V.TimeUnit
FROM
Sections S
INNER JOIN Vals V
ON V.TimeUnit BETWEEN S.StartUnit AND S.EndUnit
), Names AS (
SELECT DISTINCT
M.Mo,
Coalesce(I.Vendor, '0') Vendor,
Coalesce(I.CostCenter, ' ') CostCenter
FROM
Months M
LEFT JOIN InvoiceTemp I
ON Least(ADD_MONTHS(M.Mo, -ComparisonMonths), TRUNC(M.Mo, 'YYYY')) <= I.Mo
AND I.Mo <= M.Mo
WHERE
M.Mo >= FromDate
AND M.Mo < ToDate
)
SELECT
N.Mo,
N.CostCenter,
N.Vendor,
Coalesce(V.Vendor_VName, 'No Paid Invoices') VendorName,
T.Section,
T.TimeUnit,
Sum(I.Amt) Amt
FROM
Names N
INNER JOIN APVenMast V ON N.Vendor = V.Vendor
CROSS JOIN TimeUnits T
LEFT JOIN InvoiceTemp I
ON N.CostCenter = I.CostCenter
AND N.Vendor = I.Vendor
AND (
(
T.Section = 1 -- Weeks for current month
AND N.Mo = I.Mo
AND T.TimeUnit = I.WkNum
) OR (
T.Section = 2 -- Summary months
AND ADD_MONTHS(N.Mo, -T.TimeUnit) = I.Mo
) OR (
T.Section = 3 -- YTD
AND I.Mo BETWEEN TRUNC(N.Mo, 'YYYY') AND N.Mo
)
)
WHERE
N.Mo >= FromDate
AND N.Mo < ToDate
AND V.Vendor_Group = '1 '
AND NOT ( -- Only 4 weeks when a month is less than 28 days long
T.Section = 2
AND T.TimeUnit = 5
AND TRUNC(N.Mo + 28, 'MM') <> N.Mo
AND I.CostCenter IS NULL
) AND (
T.Section <> 1
OR IncludeWeekly = 'Y'
)
GROUP BY
N.Mo,
N.CostCenter,
N.Vendor,
V.Vendor_VName,
T.Section,
T.TimeUnit
) X;
COMMIT;
END;
This has been a long, painful ride, but if there's one thing I've learned it's that working in a database without properly updated statistics (which I'm going to look into getting our DBA to add even though the vendor doesn't care about them) can be a real disaster for someone who wants to get things done in a reasonable amount of time.
Posting the query may help.
Your DBA should be able to identify the session in a view called v$session, and the columns EVENT and WAIT_CLASS should give an indication of what is happening on the Oracle end.
He would also be able to identify the SQL (SQL_ID from v$session) and use that in a SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(sql_id)) to determine the plan.
If it is a development/test instance, see if he will grant you permissions to do that yourself if he (or she) is busy.
I know this is old but we had a similar problem and had to set the nsl_sort to binary instead of binary_ci. People could try setting the session to binary: alter session set nls_sort=binary

Resources