How to compare multiple columns under same row in a table? - oracle

Following columns of a table should not be equal in my where clause.
cd_delivery_address
cd_mail_delivery_address
cd_st_code
cd_mail_st_code
cd_zip
cd_mail_zip
Please find my code snippet to achieve this:
select * from table cd
where
(
(cd_mail_delivery_address <> cd_delivery_address or
(cd_mail_delivery_address is null and cd_delivery_address is not null) or
(cd_mail_delivery_address is not null and cd_delivery_address is null)
)
and (
cd.cd_city <> cd.cd_mail_city or
(cd.cd_city is null and cd_mail_city is not null) or
(cd_city is not null and cd_mail_city is null))
and (
cd.st_code <> cd.cd_mail_st_code or
(cd.st_code is null and cd_mail_st_code is not null) or
(st_code is not null and cd_mail_st_code is null)
)
and (
cd.cd_zip <> cd.cd_mail_zip or
(cd.cd_zip is null and cd_mail_zip is not null) or
(cd_zip is not null and cd_mail_zip is null)
)
)
All columns are varchar2 and i get correct output for this code. But is it a better way to compare multiple columns in pl sql? can i improve this code? Any suggestion would be helpful.

You could replace your null checks with NVL function something like this:
...
NVL(cd_mail_delivery_address,'_') <> NVL(cd_delivery_address,'_')
...
it's definitively more readable but I'm not sure about query efficency

I have done it for two columns using a join:
select a.cd_delivery_address,b.cd_mail_delivery_address
from cd a inner join cd b
where a.cd_delivery_address <> b.cd_mail_delivery_address and
a.cd_delivery_address = b.cd_delivery_address
Here null checking condition will be omitted and will reduce the number of conditions, but there is a performance impact since join is involved.

Related

Oracle Join with operation returning null values

I'm trying to Right join two table on a column named "compte"
I need to do an addition after. The problem is that some "compte" doesn't exist in one of the table and as a result, the addition return null instead of keeping the based value
Here's the query
SELECT t.compte,t.posdev+x.mnt
FROM (
SELECT compte,SUM(mntdev) as mnt FROM mvtc22
WHERE compte IN ('11510198451','00610198451','40010198451','40010198453','00610198461','00101980081','00101980094',
'00101980111','40010198461','40010198462','40010198466','40010198463')
AND datoper BETWEEN '01/01/22' AND '06/01/22'
GROUP BY compte
)x
RIGHT OUTER JOIN
(
SELECT c.compte,c.posdev
FROM v_sldoper c
WHERE c.compte IN ('11510198451','00610198451','40010198451','40010198453','00610198461','00101980081','00101980094',
'00101980111','40010198461','40010198462','40010198466','40010198463')
AND datpos = '31/12/21'
)t
ON t.compte = x.compte
And the results :
I'm expecting to keep the results from the second subquery if there's no "compte" in the first subquery.
Thanks In advance,
Alex
You are very close, the problem is that in oracle SQL the result of any value + null value is null, so you need to handle potential null values from each column before applying the + operator betwen them.
To solve the issue, you can apply functions like NVL or decode or even CASE WHEN for that purpose.
Below I use NVL function to solve it (I assume t.posdev column cannot contain null values, otherwise apply nvl function to both columns).
SELECT t.compte, t.posdev + NVL(x.mnt, 0)
FROM (
SELECT compte,SUM(mntdev) as mnt FROM mvtc22
WHERE compte IN ('11510198451','00610198451','40010198451','40010198453','00610198461','00101980081','00101980094',
'00101980111','40010198461','40010198462','40010198466','40010198463')
AND datoper BETWEEN '01/01/22' AND '06/01/22'
GROUP BY compte
)x
RIGHT OUTER JOIN
(
SELECT c.compte,c.posdev
FROM v_sldoper c
WHERE c.compte IN ('11510198451','00610198451','40010198451','40010198453','00610198461','00101980081','00101980094',
'00101980111','40010198461','40010198462','40010198466','40010198463')
AND datpos = '31/12/21'
)t
ON t.compte = x.compte

why count(*) is slow even with index?

This is my query:
select count(*)
FROM TB_E2V_DOCUMENTOS_CICLO D
WHERE (D.TIPOCLIENTE = null or null is null)
AND (D.TIPODOCUMENTOCLIENTE = null or null is null)
AND (D.NUMDOCUMENTOCLIENTE = null or null is null)
AND (D.BA = null or null is null)
AND (D.FA = null or null is null)
AND (D.NOMBRECLIENTE = null or null is null)
AND (D.NUMTELEFONO = null or null is null)
AND (D.NUMSUSCRIPCION = null or null is null)
AND (D.TIPORECIBO in ('Recibo'))
AND (D.NUMRECIBO = null or null is null)
AND (TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd') BETWEEN TO_DATE('2019-5-1', 'yyyy-MM-dd') AND TO_DATE('2020-2-18', 'yyyy-MM-dd'))
AND (D.MONTORECIBO = null or null is null)
AND (D.NUMPAGINAS = 0 or 0 = 0)
AND (D.NOMBREARCHIVO = null or null is null)
AND (D.NEGOCIO = null or null is null)
AND (D.NOMBREMETADATACARGA = null or null is null)
AND (D.FECHACARGA = TO_DATE(null) or TO_DATE(null) is null);
This query returns
And when I do a Xplain For:
The cost is very high, but this query uses the index. The query lasts 10 seconds approximately.
How can I improve the performance of the query?
I'm using Oracle 12c
Notes: All of the " and ( = null or null is null)" predicates will always evaluate to true; Oracle does not define null so null does not equal null, so instead if you want to check for null then use "is null"
select * from dual where null = null; -- returns no rows
select * from dual where not (null <> null); -- returns no rows
select * from dual where null is null; -- returns 1 row
select * from dual where not(null is not null); -- returns 1 row
As far as indexing goes, you need an index that is selective (i.e. return much fewer rows) and is present in the where clause predicate. In this case it looks like a function-based index on TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd')
along with D.TIPORECIBO is in order. The INDEX SKIP SCAN is used in this case probably because D.TIPORECIBO is not the leading column; INDEX SKIP SCANs are slower then INDEX RANGE SCANs because it needs to read more index blocks.
There are a few factors involved here:
First, this query is using the second (or third) part of a composite index, resulting in the SKIP SCAN.
Take a look at all indexes on the table and see what kind of index is on TIPORECIBO.
It is likely that this isn't the leading column. You might improve the performance by creating an index with TIPORECIBO as leading column, but it is unlikely--this appears to be a "type" column that might have only a few values, and not a good candidate for an index.
The second issue is that Oracle uses the index to get a set of candidate rows, then goes to the data blocks themselves to get the rows for further filtering.
A select count(*) will perform much better if Oracle doesn't need to fetch the data blocks. This can be achieved by creating an index that contains all of the data needed for the filter.
In your case, an index on TIPORECIBO and FECHAEMISION would mean that Oracle could go to the index alone without needing to access the data blocks.
The third issue is that you are applying TO_DATE to the FECHAEMISION column. If this is a DATE datatype, then you don't need the conversion and it is causing you trouble. If you do need the conversion, an option would be a function-based index on TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd').
To tune this particular query, you can try a function-based composite index:
CREATE INDEX TB_E2V_DOCUMENTOS_CICLO_FX1 ON TB_E2V_DOCUMENTOS_CICLO(FECHAEMISION, TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd'))
Finally, this query is clearly being generated from code:
lines like AND (D.BA = null or null is null) seem to be a way of excluding portions of the WHERE clause when the front-end passes a NULL. This would possibly be AND (D.BA = 'X' or 'X' is null) if a value were provided for that parameter.
As such, be careful when tuning for the current set of parameters, as any change in what generated this query will impact the effectiveness of your tuning.
If you have a way to influence how this query is generated, it would be nice to simply exclude those non-event filters when the values are not provided, though Oracle ought to be able to handle them as-is.

remove rows from table having full outer join condition

I have used full outer join condition in my joining query for joining two tables and the sample code is as below
select * from(
select TO_CHAR(ROUND(col11)) as today1,TO_CHAR(ROUND(col12)) as today2 from T1
full outer join
select TO_CHAR(ROUND(col21)) as yes1,TO_CHAR(ROUND(col22)) as yes2 from T2
) MAIN
where MAIN.today1<>0 and MAIN.today2<>0 and MAIN.yes1<>0 and MAIN.yes2<>0
sample output expected as below
today1 today2 yes1 yes2
somevalue somevalue null null
null null some value somevalue
somevalue somevalue somevalue some value
0 0 0 0
I am trying to remove rows having all zero values using above where clause but output doubles and also row with zero values apperas. Can I know where I am going wrong. Any help much appreciated.
trying to remove rows having all zero values
use:
AND NOT ( MAIN.today1=0 and MAIN.today2=0 and MAIN.yes1=0 and MAIN.yes2=0 )
which can be transformed, using De Morgan's laws: ===> https://en.wikipedia.org/wiki/De_Morgan%27s_laws into:
AND ( NOT MAIN.today1=0 OR NOT MAIN.today2=0 OR NOT MAIN.yes1=0 OR NOT MAIN.yes2=0 )
which can be further simplified to:
AND ( MAIN.today1<>0 OR MAIN.today2<>0 OR MAIN.yes1<>0 OR MAIN.yes2<>0 )
Note: if you want to also get NULL values, you must use IS NULL operator:
where ( MAIN.today1<>0 OR MAIN.today1 IS NULL )
and ( MAIN.today2<>0 OR MAIN.today2 IS NULL )
and ( MAIN.yes1<>0 OR MAIN.yes1 IS NULL )
and ( MAIN.yes2<>0 OR MAIN.yes2 IS NULL )

Oracle procedure 'with query' insert into table

I have created an Oracle SQL query in TOAD which works fine. I now need to put this in a procedure.
The query has to create two counts based on different criteria (I have used With Select) and insert these plus a date and location to a table.
The query that works is
with
Selected_animals as
( SELECT TO_CHAR(SYSDATE,'DD/MM/YYYY' ) as Report_Date,
loc.name location,
count(rran.id) as Count_exported
FROM rr_animals rran,
contact con,
locations loc,
names nam
WHERE con.connum = rran.connum
AND con.loc_id = loc.id
AND con.connum = nam.connum
AND nam.name_type = 'STAND'
AND nam.dob IS NOT NULL
AND rran.sex IS NOT NULL
AND rran.web_display = 'Y'
AND rran.web_description IS NOT NULL
AND rran.visit_end_date IS NULL
AND con.loc_id IS NOT NULL
AND con.datedl IS NULL
AND rran.hold_user IS NULL
AND rran.assess_status IS NULL
AND EXISTS (SELECT rrim.id
FROM rr_images rrim
WHERE rrim.image_type = 'KENNEL'
AND rrim.rran_id = rran.id
AND DBMS_LOB.GETLENGTH(rrim.image_object) >0
AND rrim.image_object IS NOT NULL)
group by loc.NAME ),
total_animals as
(select vbav.sitename as location,
count(vbav.rran_ID) as Count_available
from v_bx_all_animal_visits vbav
where visit_end_date is null
and concat != 'DELTD'
and concat != 'DSCD'
group by vbav.sitename)
select Total_animals.location,
Selected_animals.Report_date,
Selected_animals.count_exported,
Total_animals.count_available
from Selected_animals, total_animals
where total_animals.location = selected_animals.location(+)
I have looked at several ways that seem to write the procedure but nothing seems to work. Including which was added under the CREATE or REPLACE and before BEGIN:
( o_location out bx_webstats_export_available.LOCATION%TYPE,
o_date out bx_webstats_export_available.REPORT_DATE%TYPE,
o_exported out bx_webstats_export_available.COUNT_EXPORTED%TYPE,
o_available out bx_webstats_export_available.COUNT_AVAILABLE%TYPE )
Also added after the last where statement and before End:
INSERT INTO bx_webstats_export_available(location, report_date, count_export, count_available)
values (Total_animals.location,
Selected_animals.Report_date,
Selected_animals.count_exported,
Total_animals.count_available);
Can anyone help me get this query in a Procedure please?
This is the first time I have written a Procedure from scratch and I'm struggling with it.
Many thanks,
What is it you're trying to do? Insert the results of that select into a table? If so, the following ought to suffice:
create or replace procedure your_proc_name
as
begin
insert into bx_webstats_export_available(location, report_date, count_export, count_available)
with selected_animals as (select to_char(sysdate,'DD/MM/YYYY' ) as report_date,
loc.name location,
count(rran.id) as count_exported
from rr_animals rran,
contact con,
locations loc,
names nam
where con.connum = rran.connum
and con.loc_id = loc.id
and con.connum = nam.connum
and nam.name_type = 'STAND'
and nam.dob is not null
and rran.sex is not null
and rran.web_display = 'Y'
and rran.web_description is not null
and rran.visit_end_date is null
and con.loc_id is not null
and con.datedl is null
and rran.hold_user is null
and rran.assess_status is null
and exists (select rrim.id
from rr_images rrim
where rrim.image_type = 'KENNEL'
and rrim.rran_id = rran.id
and dbms_lob.getlength(rrim.image_object) >0
and rrim.image_object is not null)
group by loc.name),
total_animals as (select vbav.sitename as location,
count(vbav.rran_id) as count_available
from v_bx_all_animal_visits vbav
where visit_end_date is null
and concat != 'DELTD'
and concat != 'DSCD'
group by vbav.sitename)
select total_animals.location,
selected_animals.report_date,
selected_animals.count_exported,
total_animals.count_available
from selected_animals, total_animals
where total_animals.location = selected_animals.location(+);
end your_proc_name;
/
If not, then please explain a bit more about the requirements you're trying to satisfy.

Convert this SQL query to Linq (Not Exists + sub query)

I would like this SQL to be converted to LINQ. (it shouldl select rows from input which do not exist in table production based on 3 columns. If a column in both tables contains NULL, it should be considered as having the same value)
SELECT i.* FROM INPUT AS i
WHERE NOT EXISTS
(SELECT p.Agent FROM Production AS p
WHERE ISNULL(i.CustID,'') <> ISNULL(p.CustID,'')
AND ISNULL(i.CustName,'') <> ISNULL(p.CustName,'')
AND ISNULL(i.household,'') <> ISNULL(p.Household,''))
First of all - this is not a good SQL query. Every column is wrapped in a non-sargable function which means that the engine won't be able to take advantage of any indexes on any of those columns (assuming you have any).
Let's start by rewriting this as a semi-decent SQL query:
SELECT i.*
FROM Input i
LEFT JOIN Production p
ON (p.CustID = i.CustID OR (p.CustID IS NULL AND i.CustID IS NULL))
AND (p.CustName = i.CustName OR (p.CustName IS NULL AND i.CustName IS NULL))
AND (p.Household = i.Household OR
(p.Household IS NULL AND i.Household IS NULL))
WHERE p.CustID IS NULL
Now having said this, LEFT JOIN / IS NULL is not great for efficiency either, but we don't have much choice here because we're comparing on multiple columns. Based on your column names, I'm starting to wonder if the schema is properly normalized. A CustID should most likely be associated with one and only one CustName - the fact that you have to compare both of these seems a bit odd. And Household - I'm not sure what that is, but if it's a varchar(x)/nvarchar(x) column then I wonder if it might also have a 1:1 relationship with the customer.
If I'm speculating too much here then feel free to dismiss this paragraph; but just in case, I want to say that if this data isn't properly normalized, normalizing it would make it much easier and faster to query on:
SELECT *
FROM Input
WHERE CustID NOT IN (SELECT CustID FROM Production)
Anyway, going back to the first query, since that's what we have to work with for now. Unfortunately it's impossible to create a join on those specific conditions in Linq, so we need to rewrite the SQL query as something slightly worse (because we now have to read from Input twice):
SELECT *
FROM Input
WHERE <Primary Key> NOT IN
(
SELECT i.<Primary Key>
FROM Input i
INNER JOIN Production p
ON (p.CustID = i.CustID OR (p.CustID IS NULL AND i.CustID IS NULL))
AND (p.CustName = i.CustName OR (p.CustName IS NULL AND i.CustName IS NULL))
AND (p.Household = i.Household OR
(p.Household IS NULL AND i.Household IS NULL))
)
Now we have something we can finally translate to Linq syntax. We still can't do the join explicitly, which would be best, but we go old-school, start from the cartesian join and toss the join conditions into the WHERE segment, and the server will still be able to sort it out:
var excluded =
from i in input
from p in production
where
((p.CustID == i.CustID) || ((p.CustID == null) && (i.CustID == null))) &&
((p.CustName == i.CustName) ||
((p.CustName == null) && (i.CustName == null))) &&
((p.Household == i.Household) ||
((p.Household == null) && (i.Household == null)));
select i.PrimaryKey;
var results =
from i in input
where !excluded.Contains(i.PrimaryKey)
select i;
I'm assuming here that you have some sort of primary key on the table. If you don't, you've got other problems, but you can get around this particular problem using EXCEPT:
var excluded =
from i in input
from p in production
where
((p.CustID == i.CustID) || ((p.CustID == null) && (i.CustID == null))) &&
((p.CustName == i.CustName) ||
((p.CustName == null) && (i.CustName == null))) &&
((p.Household == i.Household) ||
((p.Household == null) && (i.Household == null)));
select i;
var results = input.Except(excluded);

Resources