Restructure Inline view - oracle

SELECT MAX(column1)
FROM table1 B , table2 A, table3 H
WHERE B.unit=A.unit
AND B.value=A.value
AND B.unit=H.unit
AND B.value=H.value
AND A.number=1234
Can someone help me to restructure this query in inline view?
SAMPLE
Table1
------
Value Unit
001 A1
002 B1
003 C2
002 A1
Table2
--------
Value Unit Number
001 B4 11
002 B1 1234
004 B1 22
TABLE3
-------
VALUE UNIT NUMBER COLUMN1
001 B4 11 555
002 B1 1234 557
002 B1 1234 559
OUTPUT
------
MAX(C0LUMN1)
-----------
559

In your query there is no need for inlineview :-
if that is rewritten in inlineview it will be like
Select Max(Column1)
From (Select Value,Unit From Table1)B,
(Select Value,Unit,Number From Table2)A,
Table3 as H
Where B.Unit=A.Unit
And B.Value=A.Value
AND B.unit=H.unit
And B.Value=H.Value
AND A.number=1234;
Below is the example when to use inline view hope this help!!!
An inline view is a SELECT statement in the FROM clause. As mentioned in the View section, a view is a virtual table that has the characteristics of a table yet does not hold any actual data. In an inline view construct, instead of specifying table name(s) after the FROM keyword, the source of the data actually comes from a view that is created within the SQL statement. The syntax for an inline view is,
SELECT "column_name" FROM (Inline View);
When should we use inline view? Below is an example:
Assume we have two tables: The first table is User_Address, which maps each user to a ZIP code; the second table is User_Score, which records all the scores of each user. The question is, how to write a SQL query to find the number of users who scored higher than 200 for each ZIP code?
Without using an inline view, we can accomplish this in two steps:
Query 1
CREATE TABLE User_Higher_Than_200
SELECT User_ID, SUM(Score) FROM User_Score
GROUP BY User_ID
HAVING SUM(Score) > 200;
Query 2
SELECT a2.ZIP_CODE, COUNT(a1.User_ID)
FROM User_Higher_Than_200 a1, User_Address a2
WHERE a1.User_ID = a2.ZIP_CODE
GROUP BY a2.ZIP_CODE;
In the above code, we introduced a temporary table, User_Higher_Than_200, to store the list of users who scored higher than 200. User_Higher_Than_200 is then used to join to the User_Address table to get the final result.
We can simplify the above SQL using the inline view construct as follows:
Query 3
SELECT a2.ZIP_CODE, COUNT(a1.User_ID)
FROM
(SELECT User_ID, SUM(Score) FROM
User_Score GROUP BY User_ID HAVING SUM(Score) > 200) a1,
User_Address a2
WHERE a1.User_ID = a2.ZIP_CODE
GROUP BY a2.ZIP_CODE;
There are two advantages on using inline view here:
We do not need to create the temporary table. This prevents the database from having too many objects, which is a good thing as each additional object in the database costs resources to manage.
We can use a single SQL query to accomplish what we want
Notice that we treat the inline view exactly the same as we treat a table. Comparing Query 2 and Query 3, we see that the only difference is we replace the temporary table name in Query 2 with the inline view statement in Query 3. Everything else stays the same.
Inline view is sometimes referred to as derived table. These two terms are used interchangeably.

I need to show column from other table that have the max column value
SELECT MAX( H.column1 ) AS max_column1,
MAX( A.number ) KEEP ( DENSE_RANK LAST ORDER BY H.column1 ) AS max_number
FROM table1 B
INNER JOIN table2 A
ON ( B.unit = A.unit AND B.value = A.value )
INNER JOIN table3 H
ON ( B.unit = H.unit AND B.value = H.value )
WHERE A.number=1234

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!

Worse query plan with a JOIN after ANALYZE

I see that running ANALYZE results in significantly poor performance on a particular JOIN I'm making between two tables.
Suppose the following schema:
CREATE TABLE a ( id INTEGER PRIMARY KEY, name TEXT );
CREATE TABLE b ( a NOT NULL REFERENCES a, value INTEGER, PRIMARY KEY(a, b) );
CREATE VIEW ab AS SELECT a.name, b.text, MAX(b.value)
FROM a
JOIN b ON b.a = a.id;
GROUP BY a.id
ORDER BY a.name
Table a is approximately 10K rows, table b is approximately 48K rows (~5 rows per row in table a).
Before ANALYZE
Now when I run the following query:
SELECT * FROM ab;
The query plan looks as follows:
1|0|0|SCAN TABLE b
1|1|1|SEARCH TABLE a USING INTEGER PRIMARY KEY (rowid=?)
This is a good plan, b is larger and I want it to be in the outer loop, making use of the index in table a. It finishes well within a second.
After ANALYZE
When I execute the same query again, the query plan results in two table scans:
1|0|1|SCAN TABLE a
1|1|0|SCAN TABLE b
This is far for optimal. For some reason the query planner thinks that an outer loop of 10K rows and an inner loop of 48K rows is a better fit. This takes about 1.5 minute to complete.
Should I adapt the index in table b to make it work after ANALYZE? Anything else to change to the indexing/schema?
I just try to understand the problem here. I worked around it using a CROSS JOIN, but that feels dirty and I don't really understand why the planner would go with a plan that is orders of magnitude slower than the un-analyzed plan. It seems to be related to GROUP BY, since the query planner puts table b in the outer loop without it (but that renders the query useless for what I want).
Accidentally found the answer by adjusting the GROUP BY clause in the view definition. Instead of joining on a.id, I group on b.a instead, although they have the same values.
CREATE VIEW ab AS SELECT a.name, b.text, MAX(b.value)
FROM a
JOIN b ON b.a = a.id;
GROUP BY b.a -- <== changed this from a.id to b.a
ORDER BY a.name
I'm still not entirely sure what the difference is, since it groups the same data.

In Elasticsearch, how can I establish join query with conditions and later perform percentile and count functions?

I have set of tables in my data base like table A which has set of set of categories , table B set of repositeries. A and B are related by categoryid. And then table C which has set of properties for a repoId. Table C and A are associated with repoId.
Table C can have multiple values for a repoId.
The data in C table is like a property say a number string like 12345XXXX (max data of 10 characters) and I have to find the top 6 matching characters of a particular value in table C and the count of repoIds associated with those top 6 value for a particular data in table A (categoryid).
Table A(set of categories ) ---------> Table B (set of repositories, associated with A with categoryid)---------> Table V (set of FMProperties against a repoId)
Now currently, this has been achieved by using joins and substring queries on these tables and it is very slow.
I have to achieve this functionality using Elastic search. I dont have clear view how to start?
Do I create separate documents / indexes for table A , B and C or fetch the info using sql query and create a single document.
And how we can apply this analytics part explained above.
I am very new and amateur in this technology but I am following the tutorials provided at elasticsearch site.
PFB the query in mysql for this logic:-
select 'fms' as fmstype, C.fmscode as fmsCode,
count(C.repoId) as countOffms from tableC C, tableB B
where B.repoId = C.repoId and B.categoryid = 175
group by C.fmscode
order by countOffms desc
limit 1)
UNION ALL
(select 'fms6' as fmstype, t1.fmscode, t2.countOffms from tableC t1
inner join
(
select substring(C.fmscode,1,6) as first6,
count(C.repoId) as countOffms from tableC C, tableB B
where B.repoId = C.repoId and B.categoryid = 175 and length(C.fmscode) = 6
group by substring(C.fmscode,1,6) order by countOffms desc
limit 1 ) t2
ON
substring(t1.fmscode,1,6) = t2.first6 and length(t1.fmscode) = 6
group by t1.fmscode
order by count(t1.fmscode) desc
limit 1)

ORA-000932 When joining an xmltype to a varchar field

a little backstory, i have a comma delimited column in a database, that i have tried to convert into a many to many junction table, so instead of having
ID REPORT
1 5,6,7
i would see
ID REPORT
1 5
1 6
1 7
the query below does just that, and seems to work correctly,
with t as (select id,hqcat from report)
SELECT id, EXTRACT(column_value,'/e/text()') hqcat from t x,
TABLE(XMLSEQUENCE(EXTRACT(XMLTYPE('<ROW><e>'||REPLACE(hqcat,',','</e><e>')||'</e></ROW>'),'//e')))
however when i try to join it to my lookup table by hqcat, i get a ORA-00932 error about inconstant data types, please help me, how do i change my original query to make it work with other tables and joins?
here is the join causing the error:
select *
from BIN03 a11
join ( with t as (select id,hqcat from report)
SELECT id, EXTRACT(column_value,'/e/text()') hqcat from t x,
TABLE(XMLSEQUENCE(EXTRACT(XMLTYPE('<ROW><e>'||REPLACE(hqcat,',','</e><e>')||'</e></ROW>'),'//e')))) a12
on (a11.CODE = a12.HQCAT);
Thank you!
i've also tried to create my new "many to many" view as so:
WITH TAB AS
( (select id ID, hqcat STR from report))
SELECT ID as ID,
REGEXP_SUBSTR (STR, '[^,]+', 1, LEVEL) HQCAT FROM TAB
CONNECT BY LEVEL <= (regexp_count(str,',') + 1);
but this just reduces query speeds by 1000's.....
The (deprecated) extract function returns an XMLType, not a varchar2. You can cast it to the type you want, either in the inline view:
cast(EXTRACT(column_value,'/e/text()') as varchar2(3))
or when comparing the value:
a11.CODE = cast(a12.HQCAT as varchar2(3))
I've guessed the variable size you need, obviously, you might need it to be different. You could also use the XMLType getStringVal() function as shown below.
It seems little odd to mix an old-style cross join with a comma in the inline view's from clause, with ANSI joins in the rest, and you can get odd (or hard to debug, anyway) results doing that. You could also use another CTE for clarity:
with t as (select id, hqcat from report),
a12 as (
select id, extract(column_value,'/e/text()').getstringval() hqcat
from t
cross join table(xmlsequence(extract(xmltype('<ROW><e>'||
replace(hqcat,',','</e><e>')||'</e></ROW>'),'//e')))
)
select *
from a12
join bin03 a11 on a11.code = a12.hqcat;

Oracle Query : Single update statement

I need to code an Oracle Query for the below logic and any help is appreciated.
I have a table with 8 columns and out of that need to consider 3 column for the a specific business logic.
Table data (with the 3 columns)
A B C
071699 01 I
071699 01W
071699 02W
071699 01W I
071699 02W
more rows.
Amount of data varies depending upon case, meaning it could be one or more rows per column A-B combination and usually out of these
column C is populated for at least 1 combination.
This table has over 100K of distinct A values.
Logic I need to implement:
Check - for a specific A value, how many combinations we have (A-B).
For a specific A: Check if any combination (A-B) is populated with column C data.
Take the value from the populated C column and update the same table (for the other combination of same A)
Data before (only showing specific rows)
A B C
071699 01 I
071699 01W
071699 02W
Data After Query
A B C
071699 01 I
071699 01W I
071699 02W I
I have a SQL server query doing this logic in a single query but not working in Oracle and I am getting error,"Single row query returning more than one row"
SQL Server Query
update c
set c.colC = u.colC
from data_table u
join data_table c on u.colA = c.colA and u.colB <> c.colB
and u.colB = (select MIN(colB) from data_table
where colA = u.colA and colC is not null)
and u.colC is not null and c.colC is null
Any help is appreciated to write similar oracle version.
Oracle doesn't allow joins in update queries unless you have a unique column which guarantees a 1-1 mapping which definitely doesn't apply in your case. So about the best you can do here is a nested subquery. It ain't pretty but it will work. I wasn't able to completely match up the logic you said you needed to implement with the logic that was in the SQL Server update statement, so I went with the statement.
UPDATE data_table u
SET u.COLC =(
SELECT c.ColC
FROM data_table c
WHERE c.ColA = u.ColA
and c.ColB =(
SELECT MIN( ColB )
FROM data_table
WHERE ColA = c.ColA
AND ColC IS NOT NULL
)
)
where u.ColC is null;
I tried various ways to do this task in a single query but not able to achieve that due to Oracle limitations, below is the solution worked for me:
I split the query into two parts - one doing insert statement and another with update.
First query : Here I am inserting the rows into another temp table, logic - get distinct rows per colA - ColC where colC is polulated.
Use the tmp table created in the step 1, to update the main data table by join on colA.
If anyone has better solution, then please send your response and I'll surely try it.
I resolved this issue with the set based coding after trying various methods like spliting into two tmp tables, cursor, etc, below is the sample code :
This query is 3-5X faster than any other solution.
UPDATE data_table a SET C = (
WITH comp AS (
SELECT DISTINCT A, C FROM data_table a
WHERE B = (SELECT MIN(B) FROM data_table
WHERE A = a.A
AND C IS NOT NULL)
)
SELECT
CASE
when a.C IS NULL THEN c.C
when a.C IS NOT NULL THEN a.C
END
FROM comp c
WHERE a.A = c.A AND c.C IS NOT NULL
);

Resources