SQL Aggregate Function Failing - sql

I've come across a strange behavior that I can't diagnose.
I've got an Oracle Database (11g) and I've built a view within this database. This view returns a list of information which was fine before the issue and is currently fine after the issue.
Let's say that this view returns 10 rows. When I run an aggregate function over this view, e.g. lets say a COUNT(*) I get a list of 10 rows each displaying 'Null' instead of one row with the number 10.
This was an issue that started abruptly without me knowingly making changes to the database or the view (however this is a big corporate database and changes could have been made, by a select few other people - all of which are currently denying any changes were made) There are also no know triggers that effect the view
This issue does not appear in other tables or other views. I've tried running an aggregate function on each table or view that makes up the final view and each of those returns a single number as expected.
I'm not sure what else to try. My Google searches are not offering any leads either. I would upload the code, but it is large and unwieldy, I also couldn't begin to explain how it was looking at data (suffice it to say that the database is French built)
What is really getting me is the fact that the view returns the data as expect but when I run an aggregate function over it (e.g. SELECT COUNT(*) FROM VIEW_THAT_IS_FAILING) it fails in this weird way. Any ideas?
View that is failing definition:
SELECT x.OBJEXCDE,
x.OBJDSC,
x.CLASSIFICATION,
x.COUNTRY,
NVL(y.MONTH_USAGE, 0) AS MONTH_USAGE,
x.STKQTY,
x.STANDARD_COST
FROM (SELECT a.OBJINCDE, a.OBJEXCDE, a.OBJDSC, NVL(b.STKQTY, 0) AS STKQTY, NVL(c.OBJPRC1VAL, 0) AS STANDARD_COST, NVL(d.OBJCHARVAL, 'Exception') AS CLASSIFICATION,
CASE WHEN TO_CHAR(b.STOREINCDE, '0000') || TO_CHAR(c.OBJRTEINCDE, '000') = ' 0615 130' THEN 'UK'
ELSE 'IRE' END AS COUNTRY
FROM P_OBJ a, P_OBJSTORE b, P_OBJPRC c,
(SELECT a.OBJINCDE, b.OBJCHARVAL
FROM P_OBJ a, (SELECT OBJINCDE, OBJCHARVAL FROM P_OBJCHAR WHERE CHARINCDE = 524) b
WHERE a.OBJINCDE = b.OBJINCDE (+)) d
WHERE a.OBJINCDE = b.OBJINCDE (+)
AND a.OBJINCDE = c.OBJINCDE (+)
AND a.OBJINCDE = d.OBJINCDE (+)
AND TO_CHAR(b.STOREINCDE, '0000') || TO_CHAR(c.OBJRTEINCDE, '000') IN (' 0615 130', ' 1158 284')
AND a.CLASSINCDE IN (83, 84, 126) --New Parts, Repairable Parts, Installation Parts
AND (d.OBJCHARVAL IS NULL OR NVL(c.OBJPRC1VAL, 0) = 0)) x,
W_USAGE_1_MONTH y
WHERE x.OBJINCDE = y.OBJINCDE (+)
AND x.COUNTRY = y.COUNTRY (+)
AND (NVL(y.MONTH_USAGE, 0) + x.STKQTY) > 0
Definition for W_USAGE_1_MONTH:
SELECT a.OBJINCDE,
a.OBJEXCDE,
a.OBJDSC,
NVL(b.STKQTY, 0) AS STKQTY,
NVL(c.OBJPRC1VAL, 0) AS STANDARD_COST,
NVL(d.OBJCHARVAL, 'Exception') AS CLASSIFICATION,
CASE WHEN TO_CHAR(b.STOREINCDE, '0000') || TO_CHAR(c.OBJRTEINCDE, '000') = ' 0615 130' THEN 'UK' ELSE 'IRE' END AS COUNTRY
FROM P_OBJ a,
P_OBJSTORE b,
P_OBJPRC c,
(SELECT a.OBJINCDE, b.OBJCHARVAL FROM P_OBJ a, (SELECT OBJINCDE, OBJCHARVAL FROM P_OBJCHAR WHERE CHARINCDE = 524) b WHERE a.OBJINCDE = b.OBJINCDE (+)) d
WHERE a.OBJINCDE = b.OBJINCDE (+)
AND a.OBJINCDE = c.OBJINCDE (+)
AND a.OBJINCDE = d.OBJINCDE (+)
AND TO_CHAR(b.STOREINCDE, '0000') || TO_CHAR(c.OBJRTEINCDE, '000') IN (' 0615 130', ' 1158 284')
AND a.CLASSINCDE IN (83, 84, 126) --New Parts, Repairable Parts, Installation Parts
AND (d.OBJCHARVAL IS NULL OR NVL(c.OBJPRC1VAL, 0) = 0)

Just for fun, try the following:
SELECT x.OBJEXCDE,
x.OBJDSC,
x.CLASSIFICATION,
x.COUNTRY,
NVL(y.MONTH_USAGE, 0) AS MONTH_USAGE,
x.STKQTY,
x.STANDARD_COST
FROM (SELECT a.OBJINCDE,
a.OBJEXCDE,
a.OBJDSC,
NVL(b.STKQTY, 0) AS STKQTY,
NVL(c.OBJPRC1VAL, 0) AS STANDARD_COST,
NVL(d.OBJCHARVAL, 'Exception') AS CLASSIFICATION,
CASE
WHEN TO_CHAR(b.STOREINCDE, '0000') || TO_CHAR(c.OBJRTEINCDE, '000') = ' 0615 130' THEN 'UK'
ELSE 'IRE'
END AS COUNTRY
FROM P_OBJ a
LEFT OUTER JOIN P_OBJSTORE b
ON (a.OBJINCDE = b.OBJINCDE)
LEFT OUTER JOIN P_OBJPRC c
ON (a.OBJINCDE = c.OBJINCDE)
LEFT OUTER JOIN (SELECT a.OBJINCDE, b.OBJCHARVAL
FROM P_OBJ a
LEFT OUTER JOIN (SELECT OBJINCDE, OBJCHARVAL
FROM P_OBJCHAR
WHERE CHARINCDE = 524) b
ON (a.OBJINCDE = b.OBJINCDE)) d
ON (a.OBJINCDE = d.OBJINCDE)
WHERE TO_CHAR(b.STOREINCDE, '0000') || TO_CHAR(c.OBJRTEINCDE, '000') IN (' 0615 130', ' 1158 284') AND
a.CLASSINCDE IN (83, 84, 126) AND --New Parts, Repairable Parts, Installation Parts
(d.OBJCHARVAL IS NULL OR
NVL(c.OBJPRC1VAL, 0) = 0)) x
LEFT OUTER JOIN W_USAGE_1_MONTH y
ON (x.OBJINCDE = y.OBJINCDE AND
x.COUNTRY = y.COUNTRY)
WHERE (NVL(y.MONTH_USAGE, 0) + x.STKQTY) > 0
which is just the original view reworked to use ANSI join syntax. Give it a try and see if it changes anything.
It could also be that the DBA's applied a patch or made some other change which is affecting this.
Share and enjoy.

While the support request still hasn't been responded to, I had a crack at figuring out this problem.
While breaking the view back to it's essentials I found that when when I was attempting to make a aggregation of a smaller aggregate part the SQL was failing. So the aggregation in W_USAGE_1_MONTH was causing the aggregation of the larger few to fail in a way that would out put a series of NULL values to the count of how many rows, instead of one line with the number of counted rows.
While I did a bunch of tinkering with the view what appears to have fixed the behavior was when I encapsulated the W_USAGE_1_MONTH in a FROM Clause Select statement:
(SELECT OBJINCDE, MONTH_USAGE, COUNTRY FROM W_USAGE_1_MONTH) b
My feeling is that the DBA (whom I don't have immediate access to) made a change that had an effect on the order of processing SQL statements. Forcing the the W_USAGE_1_MONTH view to be evaluated before it is used by the statement results in the operation successfully completing.
Not a very exciting end to a strange occurrence and an answer that doesn't explain the underlying cause as I don't know what changes could have been made that effect evaluation order of statements. However perhaps this would help someone else to look at evaluation order for weird behavior.

Related

Snowflake unsupported subquery when using function

This is the function I created:
CREATE OR REPLACE FUNCTION NS_REPORTS.AP."COUPA_GET_EXCH_RATE"("from_curr_id" NUMBER(38,0), "to_curr_id" NUMBER(38,0), "date" DATE)
RETURNS FLOAT
LANGUAGE SQL
AS '
SELECT
COALESCE((
SELECT
RATE
FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION BY DATE(RATE_DATE) ORDER BY RATE_DATE DESC) ROW_NUM
, RATE
FROM
CONNECTORS.COUPA.EXCHANGE_RATE
WHERE
FROM_CURRENCY_ID = from_curr_id
AND TO_CURRENCY_ID = to_curr_id
AND DATE(RATE_DATE) = date
) R
WHERE
ROW_NUM = 1
), 1)
';
I'm using the ROW_NUMBER function because the RATE_DATE field is actually datetime and so there are multiple records per date.
When I call the function by itself, it works fine. However, when I try to use it in a view, I get the unsupported subquery type error. The view works fine without it. Can anyone think of what I can do to either fix the error or work around it by rewriting the query?
EDIT 1: View code and exact error message
CREATE OR REPLACE VIEW COUPA_REQUISITION
AS
SELECT
RH.ID REQ_NUM
, RL.LINE_NUM REQ_LINE_NUM
, OH.PO_NUMBER
, REPLACE(REPLACE(OH.CUSTOM_FIELDS:"legacy-po-number", '"', ''), '.0', '') LEGACY_PO_NUMBER
, S."NAME" SUPPLIER
, OH.STATUS
, UR.FULLNAME REQUESTED_BY
, UC.FULLNAME CREATED_BY
, OL.RECEIVED
, DATE(RH.SUBMITTED_AT) ORDER_DATE
, DATE(RH.NEED_BY_DATE) NEEDED_BY_DATE
, RL."DESCRIPTION" ITEM
, CAST(NULL AS VARCHAR) CHART_OF_ACCOUNTS
, REPLACE(OH.CUSTOM_FIELDS:"purchase-type", '"', '') PURCHASE_TYPE
, COM."NAME" COMMODITY
, ACT.NS_SUB_NAME SUBSIDIARY
, ACT.NS_ACCT_NAME_FULL "ACCOUNT"
, ACT.NS_DEPT_NAME_FULL DEPARTMENT
, ACT.NS_L3_DEPT_NAME L3_DEPARTMENT
, ACT.NS_LOC_NAME "LOCATION"
, RL.QUANTITY QTY
, OL.LINE_NUM ORDER_LINE_NUM
, RL.TOTAL * NS_REPORTS.AP.COUPA_GET_EXCH_RATE(RL.CURRENCY_ID, 1, DATE(RH.SUBMITTED_AT)) LINE_TOTAL
, RL.TOTAL - OL.INVOICED UNINVOICED_AMOUNT
, OL.INVOICED INVOICED_TOTAL
, RLSUM.TOTAL TOTAL
, REPLACE(IL.CUSTOM_FIELDS:"amortization-schedule"."name", '"', '') AMORTIZATION_SCHEDULE
, CASE WHEN COALESCE(IL.CUSTOM_FIELDS:"amortization-start-date", '') <> '' THEN DATE(REPLACE(IL.CUSTOM_FIELDS:"amortization-start-date", '"', '')) ELSE NULL END AMORTIZATION_START_DATE
, CASE WHEN COALESCE(IL.CUSTOM_FIELDS:"amortization-end-date", '') <> '' THEN DATE(REPLACE(IL.CUSTOM_FIELDS:"amortization-end-date", '"', '')) ELSE NULL END AMORTIZATION_END_DATE
, CASE WHEN COALESCE(OH.CUSTOM_FIELDS:"contract-start-date", '') <> '' THEN DATE(REPLACE(OH.CUSTOM_FIELDS:"contract-start-date", '"', '')) ELSE NULL END CONTRACT_START_DATE
, CASE WHEN COALESCE(OH.CUSTOM_FIELDS:"contract-end-date", '') <> '' THEN DATE(REPLACE(OH.CUSTOM_FIELDS:"contract-end-date", '"', '')) ELSE NULL END CONTRACT_END_DATE
FROM
CONNECTORS.COUPA.REQUISITION_HEADER RH
JOIN CONNECTORS.COUPA.REQUISITION_LINE RL ON RL.REQUISITION_HEADER_ID = RH.ID
JOIN NS_REPORTS.AP.COUPA_ACCOUNT ACT ON ACT.COUPA_ACCT_ID = RL.ACCOUNT_ID
JOIN CONNECTORS.COUPA."USER" UR ON UR.ID = RH.REQUESTED_BY_ID
JOIN CONNECTORS.COUPA."USER" UC ON UC.ID = RH.CREATED_BY_ID
JOIN (
SELECT
REQUISITION_HEADER_ID
, SUM(TOTAL) TOTAL
FROM
CONNECTORS.COUPA.REQUISITION_LINE
GROUP BY
REQUISITION_HEADER_ID
) RLSUM ON RLSUM.REQUISITION_HEADER_ID = RH.ID
LEFT JOIN CONNECTORS.COUPA.ORDER_LINE OL ON OL.ID = RL.ORDER_LINE_ID
LEFT JOIN CONNECTORS.COUPA.ORDER_HEADER OH ON OH.ID = OL.ORDER_HEADER_ID
LEFT JOIN CONNECTORS.COUPA.COMMODITY COM ON COM.ID = OL.COMMODITY_ID
LEFT JOIN CONNECTORS.COUPA.SUPPLIER S ON S.ID = OH.SUPPLIER_ID
LEFT JOIN CONNECTORS.COUPA.INVOICE_LINE IL ON IL.ORDER_LINE_ID = OL.ID
Error message:
SQL Error [2031] [42601]: SQL compilation error:
Unsupported subquery type cannot be evaluated
The error is a correlated subquery and the are not supported (beyond some tiny toy examples)
But the basic form is
SELECT a.a
(select b.b from b where b.a = a.a order by b.y limit 1)
FROM a;
in effect for each row, a sub-query is run on another table to get a value. There are many tricks done in other DB's to make this "work" but in effect there is work done on each row. Snowflake does not types of for each row operations.
The good news is there are other patterns that are effectively the same, that snowflake does support, the two patterns are really the same use a CTE or join to a sub-select which is the same thing.
so the above becomes:
WITH subquery AS (
SELECT b.a, b.b FROM b
QUALIFY row_number() over (partition by b.a order by b.y) = 1
)
SELECT a.a
sq.b
FROM a
JOIN subquery AS sq
ON sq.a = a.a
So we first process/shape "all records" from the other/sub table, and we only keep the rows that have the count/shape we want, and then join to that result. The is very parallelizable, so performs well. The reason Snowflake does not auto translate a sub-query for you, is it rather easy to get it wrong, and they presently are spending there development efforts working on features that do not exist at all, etc etc, and it can be rewritten by you, given you understand your model.
What if you move this to the FROM clause? I would phrase this as:
SELECT COALESCE(MAX(er.rate), 1)
FROM (SELECT er.*
FROM CONNECTORS.COUPA.EXCHANGE_RATE er
WHERE er.FROM_CURRENCY_ID = in_from_curr_id AND
er.TO_CURRENCY_ID = in_to_curr_id AND
DATE(er.RATE_DATE) = in_date
ORDER BY RATE_DATE DESC
LIMIT 1
) er;
Notice that I changed the names of the parameters so they are more obviously input parameters.

Sorry I need to hide

Elon Reeve Musk FRS is an entrepreneur and business magnate. He is the founder, CEO, and Chief Engineer at SpaceX; early-stage investor, CEO, and Product Architect of Tesla, Inc.; founder of The Boring Company; and co-founder of Neuralink and OpenAI.
Your inner select returns a table. That can't be used as parameter to match a WHERE IN condition. Instead try using an INNER JOIN
sum(decode(
select sum(dou.noukn)
from dou
join v_kzeiritsu on
dou.zeiritsu = v_kzeiritsu.zeiritsu
)) as noukn2;
Just move your sum logic inside select as follows:
(SELECT SUM(DOU$2.NOUKN)
FROM SDNISHI.V_KZEIRITSU V
WHERE DOU$2.ZEIRITSU = V.ZEIRITSU) AS NOUKN2
In case If it gives aggregation error then use sum(above query) AS NOUKN2
Your code is very strange. For instance, it seems to assume that V_KZEIRITSU has one row. But, you can move this to the FROM clause:
SELECT SUM(CASE WHEN DOU.ZEIRITSU = K.ZEIRITSU THEN DOU.NOUKN ELSE 0 END) AS NOUKN2
FROM DOU LEFT JOIN
V_KZEIRITSU K
ON 1=1 -- in case the table is really empty
A slightly more reasonable version would be:
SELECT SUM(DOU.NOUKN) AS NOUKN2
FROM DOU LEFT JOIN
V_KZEIRITSU K
ON DOU.ZEIRITSU = K.ZEIRITSU -- in case the table is really empty
It seems rather unlikely to me that this is what you really intend. If not, I would suggest that you ask a new question with appropriate same data, desired results, and explanation of the results. A non-working query should not be expected to provide the same level of information.
I'd say that it is, actually, as simple as
select sum(dou.noukn)
from dou
where dou.zeiritsu in (select zeiritsu from v_kzeiritsu)
(I'm not sure what dou is (table? alias?), but I hope you do.)
After you edited the question, I'm editing the answer. I marked with "--> this" two lines that - in my opinion - might help. As previously, the whole sum(case ...) as noukn2 is replaced by a simple sum(dou$2.noukn).
Note that - in Oracle - you can't use as keyword for table alias. For example:
no: from employees as e
yes: from employees e
Here's your query:
SELECT DOU$2.CUSTCD AS CUSTCD,
DOU$2.CHUNO AS CHUNO,
DOU$2.LINNO AS LINNO,
DOU$2.SHIPDAYYM AS SHIPDAYYM,
SUM (DOU$2.NOUKN) AS NOUKN,
SUM (DOU$2.ZEIKN) AS ZEIKN,
SUM (dou$2.noukn) AS noukn2 --> this
FROM SDNISHI.T_HCHUMON_DOUSOU DOU$2
INNER JOIN SDNISHI.SY_KANRI KNR ON KNR.SHIPDAYYM = DOU$2.SHIPDAYYM
INNER JOIN SDNISHI.T_HCHUMON_MEI MEI
ON MEI.CUSTCD = DOU$2.CUSTCD
AND MEI.CHUNO = DOU$2.CHUNO
AND MEI.LINNO = DOU$2.LINNO
AND MEI.SHIPDAYYM = DOU$2.SHIPDAYYM
AND MEI.USEDNGKBN = '0'
AND MEI.CANCELKBN = '0'
LEFT OUTER JOIN SDNISHI.T_HCHUMON_HD HD
ON HD.CUSTCD = MEI.CUSTCD
AND HD.CHUNO = MEI.CHUNO
AND HD.LINNO = MEI.LINNO
AND HD.USEDNGKBN = '0'
AND HD.CANCELKBN = '0'
AND isnull (HD.CANKBN, '00') = '00'
JOIN v_keziritsu vk ON vk.zeiritsu = dou$2.zeiritsu --> this
WHERE DOU$2.USEDNGKBN = '0'
AND DOU$2.CANCELKBN = '0'
AND ( ( MEI.CHGDELKBN = '1'
AND MEI.HDOUSOUKBN = '02'
AND ( MEI.CHUSU > 0
OR MEI.BCHUSU > 0))
OR ( MEI.CHGDELKBN != '1'
AND HD.HDOUSOUKBN = '02'
AND ( MEI.CHKBTNFGA = '1'
AND HD.CHUSU > 0)
OR ( MEI.CHKBTNFGB = '1'
AND HD.BCHUSU > 0)))
GROUP BY DOU$2.CUSTCD,
DOU$2.CHUNO,
DOU$2.LINNO,
DOU$2.SHIPDAYYM

How to default to a value in SQL to use for a join if conditions aren't met

I hope I can explain this clearly.
I have a four digit NAICS code lookup table that I am trying to join some records to. The root table has a six-digit NAICS code that I trim and compare to the table (emd) that I want to join to. There are three conditions that I want to join to emd on:
State from root table (x) matches emd
The year in the emd table is the max year in emd for that state
The NAICS code in table x matches the NAICS code in table emd
WHEN #3 is not met, I want the query to use '0000' as the NAICS code.
I.E. - If a record from table X with NAICS "6242" and State "AZ" exists, but table emd doesn't have a corresponding row for "AZ' with "6242", I want to use the "AZ" row with "0000".
I will paste the query below. To initially get this to kind of work, on the join to table emd, I added a case statement, basically saying "If the NAICS codes match, use it. Otherwise, use "0000". However, that is creating duplication.
When I test the query and run a query for a state that does have a NAICS code in the table (not 0000), it still returns a row for the '0000' entry for the corresponding state.
Let me know if you need more information! I can't figure this out.
select x.NAICS, x.StateProvince, x.StateProvinceCode,
emd.FinalDemandOutput,
(x.JobsMaintained * isnull(wd.AvgSalary2017, 0)) * emd.FinalDemandOutput TotalEconomicImpact,
(x.HeadcountExtrapolated * isnull(wd.AvgSalary2017, 0)) * emd.FinalDemandOutput TotalEconomicImpact_HC,
st.MinWage,
x.JobsMaintained * isnull(wd.AvgSalary2017, 0) TotalWages,
x.HeadcountExtrapolated * isnull(wd.AvgSalary2017, 0) TotalWages_HC,
from(
select c.CorporationKey, c.SupplierKey, c.SurveyPeriodKey, sp.year, sp.Quarter, sp.QuarterNum, c.Headcount, c.PYAllocatedRevenue,
c.PYAllocatedJob, c.CYAllocatedRevenue, c.extrapolationfactor, left(concat(ic.Code, '000000'), 6) NAICS, sl.Description StateProvince,
sl.StateCode StateProvinceCode,
(jm.Pct * c.Headcount) as JobsMaintained,
c.CSId_, c.CSSupplierId_, c.ImpactFactor3_Annual, ((c.Headcount/c.extrapolationfactor) * c.ImpactFactor3_Annual) HeadcountExtrapolated
from x
left join WageData wd on Substring(left(concat(x.NAICS, '000000'), 6), 1,4) = substring(wd.NAICS, 1, 4)
left join EconomicMultiplierData4DigitNAICS emd on x.StateProvince = emd.statename
and emd.year = (select max(emd2.year) from EconomicMultiplierData4DigitNAICS emd2 where emd2.statename = x.StateProvince)
and (case when Substring(left(concat(x.NAICS, '000000'), 6), 1,4) = substring(emd.NAICS, 1, 4)
then Substring(left(concat(x.NAICS, '000000'), 6), 1,4)
else '0000'
end) = substring(emd.NAICS, 1, 4)
That is incredibly hard to read, but I think you want something like this:
For the join:
left join EconomicMultiplierData4DigitNAICS emd on x.StateProvince = emd.statename
and emd.year = (select max(emd2.year) from EconomicMultiplierData4DigitNAICS emd2 where emd2.statename = x.StateProvince)
and emd.NAICS = x.NAICS
For the Select:
ISNULL(emd.NAICS,'000000')
Left join will return NULL, then you can use ISNULL to instead select your default value of '000000' in that situation.
EDIT: After comment below, this instead may be closer to the desired result:
For the join:
left join EconomicMultiplierData4DigitNAICS emd on x.StateProvince = emd.statename
and emd.year = (select max(emd2.year) from EconomicMultiplierData4DigitNAICS emd2 where emd2.statename = x.StateProvince)
and emd.NAICS = x.NAICS
left join EconomicMultiplierData4DigitNAICS emdFallback on x.StateProvince = emdFallback.statename
and emdFallback.year = (select max(emd2.year) from EconomicMultiplierData4DigitNAICS emd2 where emd2.statename = x.StateProvince)
and emdFallback.NAICS = '000000'
For the Select:
ISNULL(emd.[FieldName],emdFallback.[FieldName]) [FieldName]
This does make the select a bit more tedious, and may need a DISTINCT.
I imagine there is a better/more clever way to do what you want, though I can't think of it at the moment.

Adding WHERE clause to outer query causes inner query to error

The below query worked until I added a WHERE clause.
SELECT
PersonTable.FullName,
View_PersonToHead.DirectorId
FROM PersonTable
LEFT JOIN View_PersonToDirector ON PersonTable.PersonId = View_PersonToDirector.PersonId
WHERE View_PersonToDirector.DirectorId = 12345 --No error if this line is removed
The error message is:
Invalid length parameter passed to the RIGHT function.
This leads me to believe that there was an error with how I wrote the View_PersonToDirector view. Something about adding the WHERE clause causes the query to be evaluated/optimized in a different way, exposing some issue.
Internals of View_PersonToDirector:
WITH items AS (
SELECT
PersonId,
0 AS [Level],
CAST(PersonId AS VARCHAR(255)) AS [Path]
FROM PersonTable
UNION ALL
SELECT
e.PersonId,
[Level] + 1,
CAST([Path] + ' < ' + CAST(e.PersonId AS VARCHAR(255)) AS VARCHAR(255))
FROM PersonTable e
INNER JOIN items itms ON itms.PersonId = e.ManagerId
)
SELECT
A.PersonId,
CASE
WHEN A.[Level] = 1
THEN A.PersonId
ELSE CAST(LEFT(A.PathToDirector, CHARINDEX(' ', A.PathToDirector)) AS INT)
END AS DirectorId
FROM (
SELECT
items.PersonId,
items.[Level],
RIGHT(items.[Path], LEN(items.[Path])-7) AS PathToDirector
FROM items
WHERE Path LIKE '1111 < %' --Id of director
) A
I suspect that having a WITH cte in the view causes the query optimizer to work differently, applying the WHERE filtering in a different order. Is this bad practice?
One of your problems is definitely in this code:
RIGHT(items.[Path], LEN(items.[Path])-7) AS PathToDirector
I appreciate that you think that this WHERE clause fixes the problem:
WHERE Path LIKE '1111 < %'
But it does not. The problem is that SQL Server does not guarantee the order of evaluation of expressions. This true even for subqueries, CTEs, and views. These can be evaluated in any order. In fact, SQL Server sometimes pushes the evaluation to the node that reads from the table -- which is why you get an error.
Personally, I think this is a bug in SQL Server. Those powers that be do not agree. Here are two solutions:
(CASE WHEN Path LIKE '1111 < %' THEN RIGHT(items.[Path], LEN(items.[Path])-7) END) AS PathToDirector
Or:
(CASE WHEN Path LIKE '1111 < %' THEN RIGHT(items.[Path], LEN(items.[Path] + '1234567')-7) END AS PathToDirector
You may have the same problem with the CHARINDEX().
The use of a where condition related to column from left join table generate and implicit inner join
In this case add the condition to the ON clause for let the left join work
SELECT
PersonTable.FullName,
View_PersonToHead.DirectorId
FROM PersonTable
LEFT JOIN View_PersonToDirector ON PersonTable.PersonId = View_PersonToDirector.PersonId
AND View_PersonToDirector.DirectorId = 12345
for the length problem try use conditional eg using case
SELECT
items.PersonId,
items.[Level],
case when LEN(items.[Path]) > 7 then RIGHT(items.[Path], LEN(items.[Path])-7)
ELSE items.[Path] END AS PathToDirector
FROM items
The base part of your rCTE has:
CAST(PersonId AS VARCHAR(255)) AS [Path]
Which means base rows of the rCTE would contain values such as 1234 which are shorter than 7 characters and cause RIGHT(x, LEN(x) - 7) to fail. The condition:
RIGHT(items.[Path], LEN(items.[Path])-7) AS PathToDirector
Could be safely written as:
SUBSTRING(items.[Path], NULLIF(CHARINDEX(' < ', items.[Path]), 0) + 3, LEN(items.[Path]))

Tereadata Error 3782 Improper column reference in the search condition of a joined table

I made some changes (highlighted below) to some sql I inherited and I'm getting
Error 3782 Improper column reference in the search condition of a
joined table
I'm not sure why I'm getting this message. Any help would be greatly appreciated.
with dqry_cc (mop_cd, mop_desc, group_branch_id, ecr_tmz_name, stn_stn_id , stn_id, grp_brn_id ) as
(select
a.mop_cd ,
a.mop_desc,
so.group_branch_id ,
t.ecr_tmz_name,
so.stn_stn_id,
s.stn_id ,
s.grp_brn_id
from
rfs.stn_ops_hierarchies so,
rfs.mthd_of_pymts a,
rfs.stns s,
rfs.tmzs_map t
where s.stn_id = so.stn_stn_id
and a.mpt_mop_type_code = 'CC'
and s.TMZ = t.TMZS_TMZ
and so.group_id = '01'
and so.region_cd = 'RGN_stl_99'
and so.group_branch_id ='0101')
select *
from
(select
cc.group_branch_id as GpBr,
cc.mop_desc as Descr,
cast(SYSLIB.ERAC_GMT_TO_LCL(ph.pymt_dt, cc.ecr_tmz_name) AS timestamp(6)) as LocalPaydate,
cast(SYSLIB.ERAC_GMT_TO_LCL(ph.pymt_dt, cc.ecr_tmz_name) as date) as ph_pymt_dt,
dr.dvr_srnm as DriverFName,
dr.dvr_frst_name as DriverLName,
ph.name as HolderName,
pd.ticket_no as ECARS2,
cast( case when rb.ecr_lgcy_resv_nbr is null then
case when substr(pd.ticket_no,1,1) = 'D' then
'D'||substr(pd.ticket_no, 2,6)
else
null
end
else 'D'||rb.ecr_lgcy_resv_nbr
end as CHAR(7)) as ECARS1,
rb.rnt_agr_nbr as RntAgrNo,
case when ph.cr_card_trans_typ_cde='R' then
-1*pd.pymt_amt
else
pd.pymt_amt
end as AMOUNT,
pd.cur_curr_cd as Curr,
ph.CR_CARD_NBR as CardNo,
cast(ph.exp_dt as date format 'mm/yy') as ExpireDate,
cast(0 as decimal(15,3)) as AUTH_AMT,
cast(null as date format 'mm/dd/yyyy') as AuthDate,
ph.auth_nbr as AuthNo,
cast(null as char(2)) as Swipe,
cast(null as varchar(60)) as PType,
**pft.Settlement,
pft.First_Six**
from
dqry_cc cc,
rfs_rv.pre_applied_pymts_hdr ph,
rfs_rv.pre_applied_pymts_det pd
left outer join
(select
ra.rnt_agr_nbr,
ra.ecr_ticket_no,
ra.ecre_rent_cntrct_nbr,
ra.ecr_lgcy_resv_nbr,
cc.grp_brn_id --
from
rfs_rv.rnt_agrs ra,
dqry_cc cc
where ra.sta_stn_id_orig_co = cc.stn_id QUALIFY ROW_NUMBER() OVER(PARTITION BY ra.rnt_agr_nbr ORDER BY ra.rnt_agr_nbr) = 1
) rb
on pd.ticket_no = rb.ecr_ticket_no
left outer join
(select
dvr.dvr_srnm,
dvr.dvr_frst_name,
dvr.rdy_rnt_agr_nbr
from rfs_rv.dvr_rras dvr
where dvr.main_dvr_flg = 'MR'
) dr
on rb.rnt_agr_nbr = dr.rdy_rnt_agr_nbr
**left outer join
(select
ft.paymt_mdia_proc_sys_cde as Settlement,
ft.prim_acct_frst_six_dgt_nbr as First_Six,
ft.fin_tran_ref_id
from paymt.fin_tran ft) pft
on pft.fin_tran_ref_id = cast(ph.paph_fin_trans_ref_id as decimal(19,0))**
where ph.pymt_stn_id = cc.stn_stn_id
and ph.mop_mop_cd = cc.mop_cd
and ph.pymt_id = pd.pap_pymt_id
and ph_pymt_dt = 8/5/2015
) z
Your a mixing old and new style join syntax. Your problem would never exist if you followed this simple rule: Never use commas the FROM clause. Always use explicit JOIN syntax.
The problem is the scoping of around the comma operator. Although it is functionally equivalent to a CROSS JOIN, the first part is not a known alias for the second part.
If you fix FROM to remove all commas, your problem will be fixed, even if you only change the comma to a CROSS JOIN. However, I recommend using the proper JOIN.