Oracle join select result - oracle

I 've got this problem:
I have a select statement, which is rather time consuming.
I have to join the result with itself.
I want to do something like this:
Select table1.*, table2.Consumption
from (heavy select statement) table1 left outer join
(same heavy statement) table2
on table1."id" = table2."id" and table1."Year" -1 = table2."Year"
I don't want to catch the same data 2 times. I would rather like to do something like table1 table2. Is this possible?
I need this for an application, which executes querys but isn't able to use create or something like this, otherwise i would store the data in a table.

You can use a common table expression (CTE) and materialize the results of the heavy select statement:
WITH heavy AS ( SELECT /*+ MATERIALIZE */ ... (heavy select statemenet) )
Select table1.*, table2.Consumption
from heavy table1 left outer join
heavy table2
on table1."id" = table2."id" and table1."Year" -1 = table2."Year"

Related

Oracle Performance issues on using subquery in an "In" orperator

I have two query that looks close to the same but Oracle have very different performance.
Query A
Create Table T1 as Select * from FinalView1 where CustomerID in ('A0000001','A000002')
Query B
Create Table T1 as Select * from FinalView1 where CustomerID in (select distinct CustomerID from CriteriaTable)
The CriteriaTable have 800 rows but all belongs to Customer ID 'A0000001' and 'A000002'.
This means the subquery: "select distinct CustomerID from CriteriaTable" also only returns the same two elements('A0000001','A000002') as manually entered in query A
Following is the query under the FinalView1
create or replace view FinalView1_20200716 as
select
Customer_ID,
<Some columns>
from
Table1_20200716 T1
INNER join Table2_20200716 T2 on
T1.Invoice_number = T2.Invoice_number
and
T1.line_id = T2.line_id
left join Table3_20200716 T3 on
T3.id = T1.Customer_ID
left join Table4_20200716 T4 on
T4.Shipping_ID = T1.Shipping_ID
left join Table5_20200716 Table5 on
Table5.Invoice_ID = T1.Invoice_ID
left join Table6_20200716 T6 on
T6.Shipping_ID = T4.Shipping_ID
left join First_Order first on
first.Shipping_ID = T1.Shipping_ID
;
Table1_20200716,Table2_20200716,Table3_20200716,Table4_20200716,Table5_20200716,Table6_20200716 are views to the corresponding table with temporal validity feature. For example
The query under Table1_20200716
Create or replace view Table1_20200716 as
select
*
from Table1 as for period of to_date('20200716,'yyyymmdd')
However table "First_Order" is just a normal table as
Following is the performance for both queries (According to explain plan):
Query A:
Cardinality: 102
Cost : 204
Total Runtime: 5 secs max
Query B:
Cardinality:27921981
Cost: 14846
Total Runtime:20 mins until user cancelled
All tables are indexed using those columns that used to join against other tables in the FinalView1. According to the explain plan, they have all been used except for the FirstOrder table.
Query A used uniquue index on the FirstOrder Table while Query B performed a full scan.
For query B, I was expecting the Oracle will firstly query the sub-query get the result into the in operator, before executing the main query and therefore should only have minor impact to the performance.
Thanks in advance!
As mentioned from my comment 2 days ago. Someone have actually posted the solution and then have it removed while the answer actually work. After waiting for 2 days the So I designed to post that solution.
That solution suggested that the performance was slow down by the "in" operator. and suggested me to replace it with an inner join
Create Table T1 as
Select
FV.*
from
FinalView1 FV
inner join (
select distinct
CustomerID
from
CriteriaTable
) CT on CT.customerid = FV.customerID;
Result from explain plan was worse then before:
Cardinality:28364465 (from 27921981)
Cost: 15060 (from 14846)
However, it only takes 17 secs. Which is very good!

can i set up an SSRS report where users input parameters to a table

I have an oracle query that uses a created table as part of the code. Every time I need to run a report I delete current data and import the new data I receive. This is one column of id's. I need to create a report on SSRS in which the user can input this data into said table as a parameter. I have designed a simple report that they can enter some of the id's into a parameter, but there may be times when they need to enter in a few thousand id's, and the report already runs long. Here is what the SSRS code currently says:
select distinct n.id, n.notes
from notes n
join (
select max(seq_num) as seqnum, id from notes group by id) maxresults
on n.id = maxresults.ID
where n.seq_num = maxresults.seqnum
and n.id in (#MyParam)
Is there a way to have MyParam insert data into a table I would join called My_ID, joining as Join My_Id id on n.id = id.id
I do not have permissions to create functions or procedures in the database.
Thank you
You may try the trick with MATERIALIZE hint which normally forces Oracle to create a temporary table :
WITH cte1 AS
( SELECT /*+ MATERIALIZE */ 1 as id FROM DUAL
UNION ALL
SELECT 2 DUAL
)
SELECT a.*
FROM table1 a
INNER JOIN cte1 b ON b.id = a.id

Comparing two tables, if rows are different, run query in Oracle

Think of my two tables have the same columns. One column is the ID, and the other one is the text. Is it possible to implement the following pseudo code in PLSQL?
Compare each row (They will have the same ID)
If anything is different about them
Run a couple of queries: an Update, and an Insert
ElseIf they are the same
Do nothing
Else the row does not exist
So add the row to the table compared on
Is it easy to do this using PLSQL or should I create a standalone application to do do this logic.
As your table have the same columns, by using NATURAL JOIN you can easily check if two corresponding rows are identical -- without need to update your code if a column is added to your table.
In addition, using OUTER JOIN allow you to find the rows present in one table but not in the other.
So, you can use something like that to achieve your purpose:
for rec in (
SELECT T.ID ID1,
U.ID ID2,
V.EQ
FROM T
FULL OUTER JOIN U ON T.ID = U.ID
FULL OUTER JOIN (SELECT ID, 1 EQ FROM T NATURAL JOIN U) V ON U.ID = V.ID)
loop
if rec.id1 is null
then
-- row in U but not in T
elsif rec.id2 is null
then
-- row in T but not in U
elsif rec.eq is null
-- row present in both tables
-- but content mismatch
end if
end loop
Else the row does not exist
So add the row to the table compared on
Is this condition means that rows can be missed in both tables? If only in one, then:
insert into t1 (id, text)
select id, text
from t2
minus
select id, text
from t1;
If missed records can be in both tables, you need the same query that inserts into table t2 rows from t1.
If anything is different about them
If you need one action for any amount of different rows, then use something like this:
select count(*)
into a
from t1, t2
where t1.id = t2.id and t1.text <> t2.text;
if a > 0 then
...
otherwise:
for i in (
select *
from t1, t2
where t1.id = t2.id and t1.text <> t2.text) loop
<do something>
end loop;
A 'merge' statement is what u needed.
Here is the syntax:
MERGE INTO TARGET_TABLE
USING SOURCE_TABLE
ON (CONDITION)
WHEN MATCHED THEN
UPDATE SET (DO YOUR UPDATES)
WHEN NOT MATCHED THEN
(INSERT YOUR NEW ROWS)
Google MERGE syntax for more about the statement.
Just use MINUS.
query_1
MINUS
query_2
In your case, if you really want to use PL/SQL, then select count into a local variable. Write a logic, if count > 0 then do other stuff.

Order of columns returned in SQL query (With table join)

I am wondering what determines the order of columns returned in a SQL query.
For example, SELECT * FROM SOMETABLE;
SQ_ID |BUS_TYPE |VOIP |LOCAL_PHONE
--------|-----------|---------|-------------
SQ000001|Business |Y |N
I am guessing the attribute COLUMN_ID determines this. In the case of a table join, for example, SELECT * FROM SOMETABLE LEFT JOIN OTHERTABLE USING (SOME_COL); how is the order now determine.
The order of columns in SELECT * FROM some_table is determined by the column_id of each column, as seen in USER_TAB_COLUMNS.
The order of columns in SELECT * FROM some_table JOIN other_table is all the columns for each table starting with the leftmost table after the FROM clause. In other words, this ...
SELECT * FROM some_table JOIN other_table
... is equivalent to this ...
SELECT some_table.*, other_table.* FROM some_table JOIN other_table
Changing that inner join to LEFT JOIN or RIGHT JOIN won't change the projection.
This is, of course, theoretical. We should never use select * in production code. Explicit column declarations, with table aliases when joining, are always safer. Apart from better expression of intent, explicit projections protect our code from future changes to the tables such as adding a LOB column or a column name which creates ambiguity with a joined table's column.
You can list the order of the colums in the Select statement:
SELECT SOME_COL, SOME_OTHER_COL
FROM SOMETABLE LEFT JOIN OTHERTABLE USING (SOME_COL)
But you also speak of the ID influencing the order and of ordering in general. So I think you could also be looking for ORDER BY to order the rows:
SELECT *
FROM SOMETABLE LEFT JOIN OTHERTABLE USING (SOME_COL)
ORDER BY SOME_COL
What also comes quite handy in this case is the use of aliases. Especally when both tables have coloums with the same name:
SELECT s.some_col, o.some_col
FROM SOMETABLE s LEFT JOIN OTHERTABLE o ON(o.id = s.id)
ORDER BY o.SOME_COL
I use the ON JOIN syntax in this case, because i find this more intuive when using aliases but it should also work with USING.

Efficient Alternative to Outer Join

The RIGHT JOIN on this query causes a TABLE ACCESS FULL on lims.operator. A regular join runs quickly, but of course, the samples 'WHERE authorised_by IS NULL' do not show up.
Is there a more efficient alternative to a RIGHT JOIN in this case?
SELECT full_name
FROM (SELECT operator_id AS authorised_by, full_name
FROM lims.operator)
RIGHT JOIN (SELECT sample_id, authorised_by
FROM lims.sample
WHERE sample_template_id = 200)
USING (authorised_by)
NOTE: All columns shown (except full_name) are indexed and the primary key of some table.
Since you're doing an outer join, it could easily be that it actually is more efficient to do a full table scan rather than use the index.
If you are convinced the index should be used, force it with a hint:
SELECT /*+ INDEX (lims.operator operator_index_name)*/ ...
then see what happens...
No need to nest queries. Try this:
select s.full_name
from lims.operator o, lims.sample s
where o.operator_id = s.authorised_by(+)
and s.sample_template_id = 200
I didn't write sql for oracle since a while, but i would write the query like this:
SELECT lims.operator.full_name
FROM lims.operator
RIGHT JOIN lims.sample
on lims.operator.operator_id = lims.sample.authorized_by
and sample_template_id = 200
Does this still perform that bad?

Resources