is it possible to have a union and a outer join together? - sql

So my issue here is the 'ON' clause will not meet the delete condition if people get delete out of the source tables. Is there a way to do a outer join on the union to include if anything gets deleted for the delete part to work? This something that would run ever 5 minutes. Sorry if this is a bad example of what im trying to do.
merge into table_one ab
using ( select people,job from table_jobs
where job = 'police'
union
select b.people,b.job from second_table_jobs b
where b.job = 'firefighter') zba
on (ab.people = zba.people)
update
set
ab.job ='firefighter'
where b.people = ab.people
and zba.job <> ab.job
delete where zba.people <> ab.people
when not matched then
insert
(
people,
job
)
values
(
zba.people,
zba.job
)

You can run a separate DELETE statement using NOT EXISTS to keep it clean.
DELETE FROM table_one ab
WHERE NOT EXISTS (SELECT 1
FROM (SELECT people,
job
FROM table_jobs
WHERE job = 'police'
UNION
SELECT b.people,
b.job
FROM second_table_jobs b
WHERE b.job = 'firefighter') zba
WHERE ab.people = zba.people);

Related

SQL Oracle Update containing left join

I want to perform an update for the results of a select query.
SELECT
a.reason,
n.note
FROM applications a
LEFT JOIN notes n on n.app_id = a.app_id
AND n.note LIKE '%old%'
WHERE a.code = 'run' AND a.reason IS NULL
I thought I could perform these updates separately wrapping the select in an update however I get the error ORA-01733: virtual column not allowed here. How can I go about performing these updates?
UPDATE (
SELECT
a.reason AS Reason
FROM applications a
LEFT JOIN notes n on n.app_id = a.app_id
AND n.note LIKE '%old%'
WHERE a.code = 'run' AND a.reason IS NULL
) SET Reason = null
UPDATE (
SELECT
n.note AS Note
FROM applications a
LEFT JOIN notes n on n.app_id = a.app_id
AND n.note LIKE '%old%'
WHERE a.code = 'run' AND a.reason IS NULL
) SET Note = null
You can't update the two tables at the same time. You need two different update statements as follows:
Updating the APPLICATIONS table is quite easy as all the records of the APPLICATIONS table having a.code = 'run' AND a.reason IS NULL will be there in your SELECT query.
UPDATE APPLICATIONS A
SET
REASON = NULL
WHERE A.CODE = 'run'
AND A.REASON IS NULL;
To update the NOTES table, you can use the EXISTS clause as follows:
UPDATE NOTES N
SET
NOTE = NULL
WHERE EXISTS (
SELECT 1
FROM APPLICATIONS A
WHERE N.APP_ID = A.APP_ID
AND A.CODE = 'run'
AND A.REASON IS NULL
)
AND N.NOTE LIKE '%old%'
You must update the NOTES table first and then APPLICATIONS table as while updating the NOTES table you are using the condition A. REASON IS NULL but while updating the APPLICATIONS table, you are updating the REASON column.

Rewrite query without using temp table

I have a query that is using a temp table to insert some data then another select from to extract distinct results. That query by it self was fine but now with entity-framework it is causing all kinds of unexpected errors at the wrong time.
Is there any way I can rewrite the query not to use a temp table? When this is converted into a stored procedure and in entity framework the result set is of type int which throws an error:
Could not find an implementation of the query pattern Select not found.
Here is the query
Drop Table IF EXISTS #Temp
SELECT
a.ReceiverID,
a.AntennaID,
a.AntennaName into #Temp
FROM RFIDReceiverAntenna a
full join Station b ON (a.ReceiverID = b.ReceiverID) and (a.AntennaID = b.AntennaID)
where (a.ReceiverID is NULL or b.ReceiverID is NULL)
and (a.AntennaID IS NULL or b.antennaID is NULL)
select distinct r.ReceiverID, r.ReceiverName, r.receiverdescription
from RFIDReceiver r
inner join #Temp t on r.ReceiverID = t.ReceiverID;
No need for anything fancy, you can just replace the reference to #temp with an inner sub-query containing the query that generates #temp e.g.
select distinct r.ReceiverID, r.ReceiverName, r.receiverdescription
from RFIDReceiver r
inner join (
select
a.ReceiverID,
a.AntennaID,
a.AntennaName
from RFIDReceiverAntenna a
full join Station b ON (a.ReceiverID = b.ReceiverID) and (a.AntennaID = b.AntennaID)
where (a.ReceiverID is NULL or b.ReceiverID is NULL)
and (a.AntennaID IS NULL or b.antennaID is NULL)
) t on r.ReceiverID = t.ReceiverID;
PS: I haven't made any effort to improve the query overall like Gordon has but do consider his suggestions.
First, a full join makes no sense in the first query. You are selecting only columns from the first table, so you need that.
Second, you can use a CTE.
Third, you should be able to get rid of the SELECT DISTINCT by using an EXISTS condition.
I would suggest:
WITH ra AS (
SELECT ra.*
FROM RFIDReceiverAntenna ra
Station s
ON s.ReceiverID = ra.ReceiverID AND
s.AntennaID = ra.AntennaID)
WHERE s.ReceiverID is NULL
)
SELECT r.ReceiverID, r.ReceiverName, r.receiverdescription
FROM RFIDReceiver r
WHERE EXISTS (SELECT 1
FROM ra
WHERE r.ReceiverID = ra.ReceiverID
);
You can use CTE instead of the temp table:
WITH
CTE
AS
(
SELECT
a.ReceiverID,
a.AntennaID,
a.AntennaName
FROM
RFIDReceiverAntenna a
full join Station b
ON (a.ReceiverID = b.ReceiverID)
and (a.AntennaID = b.AntennaID)
where
(a.ReceiverID is NULL or b.ReceiverID is NULL)
and (a.AntennaID IS NULL or b.antennaID is NULL)
)
select distinct
r.ReceiverID, r.ReceiverName, r.receiverdescription
from
RFIDReceiver r
inner join CTE t on r.ReceiverID = t.ReceiverID
;
This query will return the same results as your original query with the temp table, but its performance may be quite different; not necessarily slower, it can be faster. Just something that you should be aware about.

Oracle SQL XOR condition with > 14 tables

I have a question on sql desgin.
Context:
I have a table called t_master and 13 other tables (lets call them a,b,c... for simplicity) where it needs to compared.
Logic:
t_master will be compared to table 'a' where t_master.gen_val =
a.value.
If record exist in t_master, retrieve t_master record, else retrieve 'a' record.
I do not need to retrieve the records if it exists in both tables (t_master and a) - XOR condition
Repeat this comparison with the remaining 12 tables.
I have some idea on doing this, using WITH to subquery the non-master tables (a,b,c...) first with their respective WHERE clause.
Then use XOR statement to retrieve the records.
Something like
WITH a AS (SELECT ...),
b AS (SELECT ...)
SELECT field1,field2...
FROM t_master FULL OUTER JOIN a FULL OUTER JOIN b FULL OUTER JOIN c...
ON t_master.gen_value = a.value
WHERE ((field1 = x OR field2 = y ) AND NOT (field1 = x AND field2 = y))
AND ....
.
.
.
.
Seeing that I have 13 tables that I need to full outer join, is there a better way/design to handle this?
Otherwise I would have at least 2*13 lines of WHERE clause which I'm not sure if that will have impact on the performance as t_master is sort of a log table.
**Assume I cant change any schema.
Currently I'm not sure if this SQL will working correctly yet, so I'm hoping someone can guide me in the right direction regarding this.
update from used_by_already's suggestion:
This is what I'm trying to do (comparison between 2 tables first, before I add more, but I am unable to get values from ATP_R.TBL_HI_HDR HI_HDR as it is in the NOT EXISTS subquery.
How do i overcome this?
SELECT LOG_REPO.UNIQ_ID,
LOG_REPO.REQUEST_PAYLOAD,
LOG_REPO.GEN_VAL,
LOG_REPO.CREATED_BY,
TO_CHAR(LOG_REPO.CREATED_DT,'DD/MM/YYYY') AS CREATED_DT,
HI_HDR.HI_NO R_VALUE,
HI_HDR.CREATED_BY R_CREATED_BY,
TO_CHAR(HI_HDR.CREATED_DT,'DD/MM/YYYY') AS R_CREATED_DT
FROM ATP_COMMON.VW_CMN_LOG_GEN_REPO LOG_REPO JOIN ATP_R.TBL_HI_HDR HI_HDR ON LOG_REPO.GEN_VAL = HI_HDR.HI_NO
WHERE NOT EXISTS
(SELECT NULL
FROM ATP_R.TBL_HI_HDR HI_HDR
WHERE LOG_REPO.GEN_VAL = HI_HDR.HI_NO
)
UNION ALL
SELECT LOG_REPO.UNIQ_ID,
LOG_REPO.REQUEST_PAYLOAD,
LOG_REPO.GEN_VAL,
LOG_REPO.CREATED_BY,
TO_CHAR(LOG_REPO.CREATED_DT,'DD/MM/YYYY') AS CREATED_DT,
HI_HDR.HI_NO R_VALUE,
HI_HDR.CREATED_BY R_CREATED_BY,
TO_CHAR(HI_HDR.CREATED_DT,'DD/MM/YYYY') AS R_CREATED_DT
FROM ATP_R.TBL_HI_HDR HI_HDR JOIN ATP_COMMON.VW_CMN_LOG_GEN_REPO LOG_REPO ON HI_HDR.HI_NO = LOG_REPO.GEN_VAL
WHERE NOT EXISTS
(SELECT NULL
FROM ATP_COMMON.VW_CMN_LOG_GEN_REPO LOG_REPO
WHERE HI_HDR.HI_NO = LOG_REPO.GEN_VAL
)
Full outer joins used to exclude all matching rows can be an expensive query. You don't supply much detail, but perhaps using NOT EXISTS would be simpler and maybe it will produce a better explain plan. Something along these lines.
select
cola,colb,colc
from t_master m
where not exists (
select null from a where m.keycol = a.fk_to_m
)
and not exists (
select null from b where m.keycol = b.fk_to_m
)
and not exists (
select null from c where m.keycol = c.fk_to_m
)
union all
select
cola,colb,colc from a
where not exists (
select null from t_master m where a.fk_to_m = m.keycol
)
union all
select
cola,colb,colc from b
where not exists (
select null from t_master m where b.fk_to_m = m.keycol
)
union all
select
cola,colb,colc from c
where not exists (
select null from t_master m where c.fk_to_m = m.keycol
)
You could union the 13 a,b,c ... tables to simplify the coding, but that may not perform so well.

Oracle SQL - selective filtering causes cartesian

Oracle 12.2
I have a SQL statement that is causing me issues. I am retrieving data from a table called BURNDOWN. If the user is an admin, they get to see all the data. If the user is NOT an admin, they are restricted to what they can see, based on some join conditions.
The issue I am running into is when the user is an ADMIN, I don’t need the other tables… subsequently, the JOIN condition is not relevant, so Oracle is deciding to do a cartesian join across everything…
How do I get around this so that is the user is an Admin, I only look at one table, else I look at all tables and include the join condition?
The example SQL is a contrived example, but it shows the issue.
Select
BURNDOWN.NAME,
BURNDOWN.ADDRESS,
BURNDOWN.STATE
from BURNDOWN, FILTER_A, FILTER_B, FILTER_C
Where
(
:ISAdmin = 1
Or
(
BURNDOWN.x=FILTER_A.x and
FILTER_A.y=FILTER_B.y and
FILTER_B.z=FILTER_C.z and
FILTER_C.user = :ThisUser
)
)
Use an EXISTS to see if the data exists in the FILTER tables without joining them in to the results.
select bd.*
from burndown bd
where ( :isadmin = 1 or
exists ( select 1
from filter_a a
inner join filter_b b on b.y = a.y
inner join filter_c c on c.z = b.z
where a.x = bd.x
and c.user = :ThisUser )
)
Presumably, you want:
select bd.*
from burndown bd
where :ISAdmin = 1 or
(exists (select 1 from FILTER_A a where bd.x = a.x) or
exists (select 1 from FILTER_B b where bd.y = b.y) or
exists (select 1 from FILTER_C c where bd.z = c.z)
);

Errors usually but within EXISTS sub-query it runs

Is the following problematic?
DELETE a
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData a
WHERE EXISTS
(
SELECT *
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData b
WHERE
b.[Past28Days] = 1 AND
a.[Index] = b.[Index]
HAVING SUM(b.Amount) = 0
)
Reason I'm slightly uneasy about using the above script is that if I run the following it errors:
SELECT *
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData b
WHERE
b.[Past28Days] = 1
HAVING SUM(b.Amount) = 0
I understand why this script errors => the select is not grouped on anything therefore the processor does not like the aggregation in the HAVING clause.
But as a sub-query this error does not occur - why? Is this a problematic approach?
EDIT
Ended up using the following:
DELETE a
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData a
WHERE a.[Index] IN
(
SELECT [Index]
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData
WHERE [Past28Days] = 1
GROUP BY [Index]
HAVING SUM(Amount) = 0
)
But as suggested in answer the following is more readable by simply adding the GROUP BY into the sub-query:
DELETE a
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData a
WHERE EXISTS
(
SELECT *
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData b
WHERE
b.[Past28Days] = 1 AND
a.[Index] = b.[Index]
GROUP BY b.[Index]
HAVING SUM(b.Amount) = 0
)
It is legal to omit group by and still perform aggregations, therefore having is still a way of limiting results:
select sum(x)
from
(
select 1 x union all select 2
) a
having sum(x) = 3
Exists() work because everything in select list is ignored. Exists() looks for rows only, and terminates as soon as one is found. You might add group by b.Index to make intent clear to anyone reviewing the code later, or rewrite it using inner join and derived table.
DELETE a
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData a
INNER JOIN
(
SELECT b.[Index]
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData b
WHERE
b.[Past28Days] = 1
GROUP BY b.[Index]
HAVING SUM(b.Amount) = 0
) b1
ON a.[Index] = b1.[Index]