Oracle: Mixing NVL with Outer Join - sql

I've got the following query.
The only "left" table is the one with the alias "o".
I want to specify the following. How can I do? Should I use a WITH temp construct?
AND ( NVL (domb.DOMB_CONTO_CORRENTE, ' ') != o.campo43
OR NVL (abi.abi_descrizione, ' ') != o.campo41
OR NVL (cab.cab_descrizione, ' ') != o.campo42)
Here's the complete statement:
SELECT /*+ parallel(o 64) */
o.stato, COUNT (1)
FROM CONF_RAGGRUPPAMENTI_FORN rgf,
CRD_RID_REL_DOMICILIAZIONE crrd,
CRD_DOMICILIAZIONI domb,
uff_abi abi,
uff_abi_cab cab,
CONTO_CLIENTE_T809 o,
eni_flussi_hub c,
eni_monitor mon
WHERE 1 = 1
--RGF - OUT
AND rgf.RGF_CODICE_RAGGRUPPAMENTO(+) = o.campo1
--Join tra OUT e la ENI_FLUSSI_HUB
AND o.id_messaggio = c.flh_id_messaggio(+)
AND o.d_pubblicazione = c.flh_data_elaborazione(+)
--Join tra ENI_FLUSSI_HUB e ENI_MONITOR
AND c.FLH_ID_MESSAGGIO = MON.MON_ID_MESSAGGIO(+)
AND c.FLH_TIPO_PROCESSO_COD = MON.MON_COD_TP_PROCESSO(+)
AND c.flh_flag_ann(+) = 'N'
AND mon_flag_ann(+) = 'N'
--Join da RGF a DOMB
AND rgf.UITR_IDENT_TIPI_RAGGR_ID(+) = 'MP'
AND rgf.RGF_RAGGRUPPAMENTO_FORN_ID = crrd.RGF_RAGGRUPPAMENTO_FORN_ID(+)
AND crrd.DOMB_DOMICILIAZIONE_ID = domb.DOMB_DOMICILIAZIONE_ID(+)
AND CRRD.CRRD_RID_REL_DOM_ID = crrd.crrd_storico_id
AND CRRD.CRRD_FLAG_ANN (+) = 'N'
AND domb.domb_flag_ann (+) = 'N'
AND rgf.rgf_flag_ann(+) = 'N'
--Join tra domb e abi e cab
AND DOMB.ABI_ID = abi.ABI_ID(+)
AND DOMB.CAB_ID = cab.CAB_ID(+)
--Filtro sulle date
AND o.d_pubblicazione BETWEEN TO_DATE ('06-apr-2013')
AND TO_DATE ('14-apr-2013')
--Solo i flussi che producono variazioni
AND ( NVL (domb.DOMB_CONTO_CORRENTE, ' ') != o.campo43
OR NVL (abi.abi_descrizione, ' ') != o.campo41
OR NVL (cab.cab_descrizione, ' ') != o.campo42)
GROUP BY o.stato

If you can, I would recommend rewriting your query in modern ANSI syntax. This makes the query not only more readable, but it's easier to apply predicates on the optional table in a clear manner. I've rewritten 'old' Oracle queries and it's usually a quick cut and paste job to move the join conditions from the WHERE clause to the FROM ... JOIN ... ON ... clause.
Then, any predicates that apply to the optional table are listed under the OUTER JOIN condition, not under the WHERE. For example (in a very rough example):
SELECT
mt.col1,
mt.col2,
ot.col3
-- other columns ...
FROM main_table mt
LEFT OUTER JOIN optional_table ot ON mt.col1 = ot.col1
AND ot.col2 = 'N'
AND NVL (ot.some_column, 'x') != mt.col5
-- Now the where for the result set and main table
WHERE
AND mt.col4 BETWEEN TO_DATE ('06-apr-2013') AND TO_DATE ('14-apr-2013')
-- Other conditions on the total result set.
I've found this easiest way to apply conditions to the optional table without excluding rows from the final set.

You do not need to use the + on the NVLs because you establish the outer join already on the ID of domp, abi, and cab.
I agree with the others that using JOIN statement makes more readable SQL Selects.
Do you have any trouble with the query?

Related

Compare two columns with one coalesce function

I tried this query but it doesn't work, ORA-00904 error.
SELECT
C.BUSINESS_UNIT
,COALESCE(L.CHILD_CONTR_ID
,L.PARENT_CONTR_ID) AS CONTRAT
, C.BILL_TO_CUST_ID
FROM C_LIE_VW L
,CONTR_HDR C
WHERE 1=1
AND CONTRAT=C.CONTRACT_NUM
AND (L.PARENT_CONTR_ID <> ' '
OR L.CHILD_CONTR_ID IS NOT NULL)
It seems I can't do this: AND CONTRAT=C.CONTRACT_NUM
How can I compare the coalesce column with another column?
Please use below query, you cannot use alias to join a value, instead you have to use complete expression. And you no need to use 1=1 condition as well.
SELECT
C.BUSINESS_UNIT
,COALESCE(L.CHILD_CONTR_ID
,L.PARENT_CONTR_ID) AS CONTRAT
, C.BILL_TO_CUST_ID
FROM C_LIE_VW L
,CONTR_HDR C
WHERE COALESCE(L.CHILD_CONTR_ID,L.PARENT_CONTR_ID)=C.CONTRACT_NUM
AND (L.PARENT_CONTR_ID <> '' OR L.CHILD_CONTR_ID IS NOT NULL);
Below is the join query if required,
SELECT
C.BUSINESS_UNIT
,COALESCE(L.CHILD_CONTR_ID
,L.PARENT_CONTR_ID) AS CONTRAT
, C.BILL_TO_CUST_ID
FROM C_LIE_VW L inner join
CONTR_HDR C
on (COALESCE(L.CHILD_CONTR_ID,L.PARENT_CONTR_ID)=C.CONTRACT_NUM )
WHERE (L.PARENT_CONTR_ID <> ' '
OR L.CHILD_CONTR_ID IS NOT NULL);

Can anyone spot the error with the WITH() clause?

SQL is not recognizing the variable I am calling in the main SELECT statement. The variable I am calling is the execs.block_order_quantity. I name this variable in the WITH statement at the beginning of the query. The code is below, and the error is attached as a picture. I have run the select statement without the WITH execs as( piece and it works just fine.
WITH execs as(SELECT th.exec_id, SUM(eha.tck_ord_qty) as "BLOCK_ORDER_QUANTITY"
FROM t1 eha
join t2 th
on eha.exec_id = th.exec_id
where th.trdng_desk_sname IN ('NAME')
and th.trd_dt between to_date('201840101', 'YYYYMMDD')
and to_date ('20140301', 'YYYYMMDD')
and exists
(SELECT 1
FROM t2t eth
WHERE eth.TRD_ID = eha.TRD_ID
AND eth.trd_stat_cd = 'EX')
group by th.exec_id)
SELECT DISTINCT
th.trd_dt as "TRADE_DATE",
eah.ord_cap_qty as "CAP_AMOUNT",
execs.block_order_quantity as "BLOCK_ORDER_QUANTITY",
eah.alloc_ovrrd_rsn_cd as "ALLOC_OVRRD_RSN_CD",
CASE --create allocation case id
WHEN(eh.manl_alloc_ind = 'Y'
OR NVL (eah.trdr_drct_qty, 0) > 0
OR NVL (eah.trdr_drct_wgt_ratio, 0) > 0)
THEN
'Y'
ELSE
'N'
END
AS "ALLOCATION_OVRRD_IND",
CASE
WHEN (eh.manl_alloc_ind = 'Y'
OR NVL (eah.trdr_drct_qty, 0) > 0
OR NVL (eah.trdr_drct_wgt_ratio, 0) > 0)
THEN
TH.EXEC_TMSTMP
ELSE
NULL
END
AS "ALLOCATION_OVRRD_TIMESTAMP",
eah.alloc_adj_assets_rt_curr_amt as "FUND_ADJ_NET_ASSETS",
eah.as_alloc_exec_hld_qty as "FUND_HOLDINGS_QUANTITY",
th.as_trd_iv_lname as "SECURITY_NAME",
th.as_trd_fmr_ticker_symb_cd as "TICKER",
CASE
WHEN NVL(th.limit_prc_amt, 0) > 0 THEN 'LIMIT' ELSE NULL END
AS "FUND_ORDER_TYPE"
from t1 eah
join t3 eh
on eah.exec_id = eah.exec_id
join t2 th
on th.trd_id = eah.trd_id
join t4 tk
on tk.tck_id = eah.tck_id
join t5 pm
on eah.pm_person_id_src = pm.person_id_src
where th.trdng_desk_sname IN('NAME')
and th.trd_dt between to_date('20140101', 'YYYYMMDD')
and to_date ('20140301', 'YYYYMMDD')
and rownum < 15
You need to join to the execs common table expression (CTE) name in your main query, e.g.:
...
from t1 eah
join t3 eh
on eah.exec_id = eah.exec_id
join t2 th
on th.trd_id = eah.trd_id
join execs
on execs.exec_id = eh.exec_id
join t4 tk
...
I'm not sure you actually want a CTE here, it looks like you could just do the aggregation in the main query as you're referencing the same tables; but there may be something I'm missing, like duplicates introduced by later joins.
Incidentally, the first on clause there looks wrong, as both sides refer to the same column in the same table; so it should probably be:
...
from t1 eah
join t3 eh
on eh.exec_id = eah.exec_id
join t2 th
Having distinct is sometimes a sign that there joins aren't right and there are duplicates you want to suppress which shouldn't really be there, so you may not need that once the join condition is fixed; and that might allow a simple aggregate to work too if it was giving the wrong result before. (Or there could stil be some other reason it's not appropriate.)
Also, the where rownum < 15 will give an indeterminate set of rows as you aren't ordering the result set before applying that filter.

Convert value ' ' to null in SQL or BigQuery

I have the following extraction query
select A.documentid.Docid, A.documentId.Appid, A.timestamp, A.EventStatus, D.Sequence, D.EventAppName, E.Value as Federation
from `dbo.events` A left join `dam.eventsroot` B on A.documentid.docid = B.docid left join `dbo.documentroot` C on B.rootdocid=C.rootdocid
inner join `dbo.reference_status` D
on A.DocumentID.AppID=D.EventAppID and A.EventStatus = D.EventStatus left join unnest(C.metadata) E on E.Key='Federation'
where A.timestamp > TIMESTAMP_TRUNC(CURRENT_TIMESTAMP(),DAY)
The result is that in column FEDERATION i get different values and values of '' (nothing) and i want to change them to Null.
How can i do this?
Thank you very much
I could use this in simple way like
select A.documentid.Docid, A.documentId.Appid, A.timestamp, A.EventStatus,
D.Sequence, D.EventAppName,
Case
When LTrim(RTrim(E.Value)) = '' Then Null
Else E.Value
End AS Federation
from dbo.events A
left join dam.eventsroot B on A.documentid.docid = B.docid
left join dbo.documentroot C on B.rootdocid=C.rootdocid
inner join dbo.reference_status D on A.DocumentID.AppID=D.EventAppID and
A.EventStatus = D.EventStatus
left join unnest(C.metadata) E on E.Key='Federation'
where A.timestamp > TIMESTAMP_TRUNC(CURRENT_TIMESTAMP(),DAY)
there are functions like NULLIF(column , '') and REPLACE(column,'',NULL) which you can them in such this cases. u can use LTRIM(RTRIM(column)) so any extra spaces will be removed.
so like:
NULLIF(LTRIM(RTRIM(federation)), '')
REPLACE(LTRIM(RTRIM(federation)),'',NULL)
and so many other ways...
You can use nullif() in any database:
select nullif(federation, '')
You might want to remove white space in general, and do:
select nullif(trim(federation), '')
Or, perhaps:
select (case when trim(federation) <> '' then federation end)

Is it possible to insert an IF clause inside of a WHERE clause in a SELECT?

sorry if this is a dumb question, I'm new to PL/SQL.
I have a PL/SQL Stored Procedure which retrieves data from a table.
There are 2 optional parameters which can be null. If they're not null they should be part of the WHERE clause.
This is the SELECT:
SELECT DISTINCT PN.PART_NUMBER, PN.SHORT_CODE, DES.DESCRIPTION
FROM MAS_PART PN
LEFT JOIN CF_DESCRIPTION DES
ON PN.LOCAL_DESCRIPTION_ID = DES.DESCRIPTION_ID
WHERE PN.PART_CODE = 'M'
AND PN.PART_TYPE_ID = IN_PART_TYPE_ID
AND PN.PART_GROUP_ID = IN_PART_GROUP_ID
ORDER BY PN.PART_NUMBER;
The two variables are IN_PART_TYPE_ID and IN_PART_GROUP_ID. Is there any way to evaluate the WHERE clauses regarding these two variables only when they're NOT NULL? Or the only way is to repeat the SELECT 3 times using IF clauses and changing the WHERE clauses?
OR operatior will help:
SELECT DISTINCT PN.PART_NUMBER, PN.SHORT_CODE, DES.DESCRIPTION
FROM MAS_PART PN
LEFT JOIN CF_DESCRIPTION DES
ON PN.LOCAL_DESCRIPTION_ID = DES.DESCRIPTION_ID
WHERE PN.PART_CODE = 'M'
AND (PN.PART_TYPE_ID = IN_PART_TYPE_ID or IN_PART_TYPE_ID is null)
AND (PN.PART_GROUP_ID = IN_PART_GROUP_ID or IN_PART_GROUP_ID is null)
ORDER BY PN.PART_NUMBER;
Another option is to use NVL:
SELECT DISTINCT PN.PART_NUMBER, PN.SHORT_CODE, DES.DESCRIPTION
FROM MAS_PART PN
LEFT JOIN CF_DESCRIPTION DES
ON PN.LOCAL_DESCRIPTION_ID = DES.DESCRIPTION_ID
WHERE PN.PART_CODE = 'M'
AND PN.PART_TYPE_ID = NVL(IN_PART_TYPE_ID, PN.PART_TYPE_ID)
AND PN.PART_GROUP_ID = NVL(IN_PART_GROUP_ID, PN.PART_GROUP_ID)
ORDER BY PN.PART_NUMBER;
But it will work only if PN.PART_TYPE_ID and PN.PART_GROUP_ID is not null

Trouble with case statement

I am having problems with this case statement. I don't know what I am doing wrong but I get the error:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
I have a case when the field equals a value then do a left outer join but if the field equals a different value then do a inner join.
This is my query:
SELECT
case
when oqt = '26' then
(Select qm.id_oqm, cast(isNull(id_eval, '') as varChar(50)) + ' - ' + qm.methodName as methodName, qm.methodName as actualMethod,cv.*
FROM OQMethods QM left outer join courseversions cv on cv.evalid = QM.id_eval and cv.courselanguage = 'EN' and cv.courseactive='Y' and cv.id_cp > 0
WHERE QM.id_oqt in (SELECT tempoq.oqt FROM tempoq INNER JOIN OQMethods ON tempoq.oqt = OQMethods.id_oqt)and active = 1)
END,
case
when oqt = '31' then
(Select qm.id_oqm, cast(isNull(id_eval, '') as varChar(50)) + ' - ' + qm.methodName as methodName, qm.methodName as actualMethod,cv.*
FROM OQMethods QM inner join courseversions cv on cv.evalid = QM.id_eval and cv.courselanguage = 'EN' and cv.courseactive='Y' and cv.id_cp > 0
where QM.id_oqt in (SELECT tempoq.oqt FROM tempoq INNER JOIN OQMethods ON tempoq.oqt = OQMethods.id_oqt) and active = 1)
END
from tempoq
The case is an expression that must evaluate to a value. The Select statements that you have return multiple values.
It would seem that you're trying to use Case like it's a C# switch? If that's the case, then you're likely better off with an IF ELSE IF construction.
It looks like you want to do something like this rather than using a CASE statement.
DECLARE #t int
-- This would contain your oqt value
SET #t =1
IF #t = 1
BEGIN
SELECT * FROM tableA
END
ELSE IF #t = 2
BEGIN
SELECT * FROM TableB
END
Select qm.id_oqm, cast(isNull(id_eval, '') as varChar(50)) + ' - ' + qm.methodName as methodName, qm.methodName as actualMethod,cv.*
FROM OQMethods QM
inner join tempoq on tempoq.oqt = QM.id_oqt
left outer join courseversions cv on cv.evalid = QM.id_eval and cv.courselanguage = 'EN' and cv.courseactive='Y' and cv.id_cp > 0
WHERE active = 1 and (tempoq.oqt = '26' or (tempoq.oqt = '31' and courseversions.* is not null))
left outer join means join OQMethods's data which even no match data from courseversions,
then filter the data with null courseversions.* that is inner join.
Hope I have the right understanding.