Oracle - Can this query be optimized? - sql

I want to get the last Date of a set of rows. What is more performant: Query1 or Query2:
Query1
select *
from(
select column_date
from table1 a join table2 b on a.column1=b.column1
where id= '1234'
order by column_date desc) c
where rownum=1
Query2
select column_date
from table1 a join table2 b on a.column1=b.column1
where id= '1234'
order by column_date desc
and take the first row in backend.
Or maybe is there another way to take the first row in Oracle? I know that normally subselects are bad performant. That's why I am trying to remove the subselect.
I tried that but I am not getting the result expected:
select column_date
from table1 a join table2 b on a.column1=b.column1
where id= '1234' and rownum=1
order by column_date desc

First, you can't really optimize a query. Queries are always rewritten by the optimizer and may give very different results depending on how much data there is, indexes, etc. So if you have a query that is slow, you must look at the execution plan to see what's happening. And if you have a query that is not slow, you shouldn't be optimizing it.
There's nothing wrong with subselects, per se. As Wernfriend Domscheit suggests, this will give you the minimum column_date, which I assume resides in table2.
SELECT MIN( b.column_date )
FROM table1 a
INNER JOIN table2 b on a.column1 = b.column1
WHERE a.id= '1234'
That is guaranteed to give you a single row. If you needed more than just the date field, this will select the rows with the minimum date:
SELECT a.*, b.column_date
FROM table1 a
INNER JOIN table2 b on a.column1 = b.column1
WHERE a.id= '1234'
AND b.column_date = ( SELECT MIN( b2.column_date ) FROM table2 b2 )
But if your column_date is not unique, this may return multiple rows. If that's possible, you'll need something in the data to differentiate the rows to select. This is guaranteed to give you a single row:
SELECT * FROM (
SELECT a.*, b.column_date
FROM table1 a
INNER JOIN table2 b on a.column1 = b.column1
WHERE a.id= '1234'
AND b.column_date = ( SELECT MIN( b2.column_date ) FROM table2 b2 )
ORDER BY a.some_other_column
)
WHERE ROWNUM = 1
In a recent enough version of Oracle, you can use FETCH FIRST 1 ROW ONLY instead of the ROWNUM query. I don't think it makes a difference.

Related

How to combine multiple SELECT statements into a single query & get a single result output

I have multiple SELECT queries which is ran against different tables.
The output of all the queries have the same number of rows (every query when ran individually will have the same number of rows). Is there a way I can combine the output of all these queries into a single result? (Keep out from first query and add the output of next query as a column to the output of the next query). I dont want to save these tables into database as I am just doing some validation testing.
Example:
SELECT AAA,BBB,CCC FROM Table1
SELECT Table2.DDD, Table1.AAA
FROM Table2
INNER JOIN Table1
ON Table1.AAA = Table2.AAA
I tried writing combining the query as
SELECT Table1.AAA,Table1.BBB,Table1.CCC,T1.DDD
FROM Table1,
(SELECT Table2.DDD, Table1.AAA
FROM Table2
INNER JOIN Table1
ON Table1.AAA = Table2.AAA)T1
I tried doing the above combined query, but instead of getting 11 rows as output (both queries above had result of 11 rows), I am getting 35 rows as output.
Hope the question made sense!
You'll need to specify a criteria to match each row the first query with which row of the second query.
If, for example, the column AAA is unique in both queries and you want to match rows with the same values you could do:
select a.*, b.*
from (
SELECT AAA,BBB,CCC FROM Table1
) a
full join join (
SELECT Table2.DDD, Table1.AAA
FROM Table2
INNER JOIN Table1
ON Table1.AAA = Table2.AAA
) b on b.aaa = a.aaa
If there aren't any clear matching rules, you can produce an artificial row number on each result set and use it to match rows. For example:
select
a.aaa, a.bbb, a.ccc,
b.ddd, b.aaa
from (
SELECT AAA, BBB, CCC,
row_number() over(order by aaa) as rn
FROM Table1
) a
full join join (
SELECT Table2.DDD, Table1.AAA,
row_number() over(order by table1.aaa, table2.ddd) as rn
FROM Table2
INNER JOIN Table1
ON Table1.AAA = Table2.AAA
) b on b.rn = a.rn
If you have several results and want to have all of them as additional columns you can simply use ",":
create table temp1 as select '1' as c1 from DUAL;
create table temp2 as select '2' as c2 from DUAL;
create table temp3 as select '3' as c3 from DUAL;
select a.c1, b.c2, c.c3 from temp1 a, (select c2 from temp2) b, (select c3 from temp3) c;
An alternative could also be that you want to have all the results as additional rows then you would use UNION ALL between the individual results.

Join between two tables, number of row

I hope you're well.
I try to write a sql query with a join between two tables like below :
table1 (id_master, id)
1,1
1,2
1,3
1,4
1,5
And the second table
table2 (id_master, id)
1,1
1,2
1,3
1,4
As you can see, each table contain ID_master & id.
The table2 contains the acknownledgement (ack) of table1. Each row in the table1 must have an "ack" in the table2.
In my example, I have no result because (table1 (1,5) hasn't got an ack in table2 and I want result when table1.row (1,5) has got a ack in table2.
I have tried with join but i have result when we have the first "ack". I want have result when I have all "ack".
I hope to be clear.
thanks for your help.
kind regards
EDIT :
Thanks stripathi & jpw,
Example1:
table1 (id_master, id)
1,A
1,B
1,C
2,D
2,E
the second table
table2 (id_master, id)
1,A
1,B
2,D
2,E
My query's result must be :
2,D
2,E
Because we can find the rows(2,D) & (2,E) in the two tables, but it isn't the case for (1,*) (it miss the (1,C) in the table2).
I think both of these two queries should do what you want, and they seem to work using Oracle 11g R2 (see this SQL Fiddle). Note that the result might be wrong if the second table contains items that are not present in the first table.
select *
from table1
where id_master in (
select a.id_master
from table1 a
group by a.id_master
having count(distinct a.id) = (
select count(distinct b.id)
from table2 b
where a.id_master = b.id_master
group by b.id_master
)
);
select *
from table1 a
where not exists (
select id from Table1 where id_master = a.id_master
minus
select id from Table2
);
If you using oracle you can use ROWNUM to get row number of first ack. You can try this :
SELECT ID,ID_MASTER FROM(
SELECT ID,ID_MASTER,ROWNUM RR
FROM TABLE2
ORDER BY ID_MASTER,ID ASC) T2
WHERE RR >= (
SELECT R FROM(
SELECT ID_MASTER,ID, ROWNUM R
FROM TABLE1
ORDER BY ID_MASTER,ID ASC
) T1
WHERE T1.ID_MASTER||T1.ID NOT IN(SELECT ID_MASTER||ID FROM TABLE2)
)

How to simplify this query with sql joins?

my_table has 4 columns: id integer, value integer, value2 integer, name character varying
I want all the records that:
have the same value2 as a record which name is 'a_name'
have a field value inferior to the one of a record which name is 'a_name'
And I have satisfying results with the following query:
select t.id
from my_table as t
where t.value < ( select value from my_table where name = 'a_name')
and s.value2 = (select value2 from my_table where name = 'a_name');
But is it possible to simplify this query with sql joins ?
Joining on the same table is still too much intricate in my mind. And I try to understand with this example.
What I happened so far trying, is a result full of dupplicates:
select t2.id
from my_table as t
inner join my_table as t2 on t2.value2 = t.value2
where t2.value < ( select value from my_table where name = 'a_name');
I think this will solve your problem.
select t1.id
from my_table as t1
join my_table as t2
on t1.value2 = t2.value2
and t2.name = 'a_name'
and t1.value < t2.value
You should use self join instead of inner join see this
http://msdn.microsoft.com/en-us/library/ms177490%28v=sql.105%29.aspx
You can always get distinct results by calling "SELECT distinct t2.id ..."
However, that will not enhance your understanding of inner joins. If you are willing, keep reading on. Let's start by getting all records with name = 'a_name'.
SELECT a.*
FROM my_table as a
WHERE a.name = 'a.name';
A simpler way to perform your inner joins is to understand that the result for the above query is yet another table, formally known as a relation. You can think of it as joining on the same table, but an easier way to think of it is as "joining on the result of this query". Lets put this to the test.
SELECT other.id
FROM my_table as a,
INNER JOIN my_table as other ON other.value2 = a.value2
WHERE a.name = 'a_name'
AND other.value < a.value;
If the first query (all rows with name = 'a_name') has many results, you stand a good chance of the second query having duplicates, because the inner join between aliases 'a' and 'other' is a subset of their cross product.
Edits: Grammar, Clarity
please try this
select t.id
from my_table as t
inner join
(select value from my_table where name = 'a_name')t1 on t.value<t1.value
inner join
(select value2 from my_table where name = 'a_name')t2 on t.value2=t2.value2

Query for a table with big size of columns

I've got a table in which there are some columns with big text data. The query for 10 rows (table has only 31 records) takes more than 20 seconds. If I remove fields with big size, the query is executed quickly. The query for 1 row (by id) always executed quickly.
How can I do the query for many rows work more faster?
The query looks like this
SELECT DISTINCT (a.id), a.field_1, a.field_2, a.field_3
, a.field_4, a.field_5, a.filed_6, ...
FROM table_a a, table_b b
WHERE a.field_8 = 'o'
ORDER BY a.field_2 DESC
LIMIT 10;
#a_horse already hinted at the likely syntax error. Try:
SELECT DISTINCT ON (a.id) a.id, a.field_1, a.field_2, a.field_3, ...
FROM table_a a
-- JOIN table_b b ON ???
WHERE a.field_8 = 'o'
ORDER BY a.id, a.field_2 DESC
LIMIT 10;
Note the bold emphasis and read up on the DISTINCT clause in the manual.
Also, an index on field_8 might help.
A multicolumn index on (field_8, id, field_2) might help even more, if you can narrow it down to that (and if that is the sort order you want, which I doubt).
If you want the result sorted by a.field_2 DESC first:
In PostgreSQL 9.1, if id is the primary key:
SELECT a.id, a.field_1, a.field_2, a.field_3, ...
FROM table_a a
-- JOIN table_b b ON ???
WHERE a.field_8 = 'o'
GROUP BY a.id -- primary key takes care of all columns in table a
ORDER BY a.field_2 DESC
LIMIT 10;
why you are selecting table_b? you dont join this tables!
make a real join like this
SELECT DISTINCT
(a.id), a.field_1, a.field_2, a.field_3, a.field_4, a.field_5, a.filed_6
FROM table_a a
INNER JOIN table_b b
ON b.field_on_table_b = a.field_on_table_a
WHERE a.field_8 = 'o'
ORDER BY a.field_2 DESC LIMIT 10
then be sure that field_8 (in the where statement) is defined with a key!

select first N distinct rows without inner select in oracle

I have something like the following structure: Table1 -> Table2 relationship is 1:m
I need to perform queries similar to the next one:
select Table1.id from Table1 left outer join Table2 on (Table1.id1 = Table2.id2) where Table2.name like '%a%' and rownum < 11
i.e. I want first 10 ids from Table 1 which fulfils conditions in Table2. The problem is that I've to use distinct, but the distinct clause applies after 'rownum < 11', so the result could be e.g. 5 records even if their number is more than 10.
The apparent solution is to use the following:
select id from ( select Table1.id from Table1 left outer join Table2 on (Table1.id1 = Table2.id2) where Table2.name like '%a%' ) where rownum < 11
But I'm afraid of performance of such a query. If Table1 contains about 300k records, and Table2 contains about 700k records, wouldn't such a query be really slow?
Is there another query, but without inner select? Unluckily, I want to avoid using inner selects.
Unluckily, I want to avoid using inner
selects
With having the WHERE clause on TABLE2, you are filtering the select to an INNER JOIN (ie. since Table2.name IS null <> Table2.name like '%a%' you will only get results where the join is INNER to one another. Also, the %a% without a function based index will result in a full table scan on each iteration.
but #lweller is completely correct, to do the query correctly you will need to use a subquery. keep in mind, without an ORDER BY you have no guarantee of the order of your top X records (it may always 'appear' that the values conform to the primary key or whatnot, but there is no guarantee.
WITH TABLE1 AS(SELECT 1 ID FROM DUAL
UNION ALL
SELECT 2 ID FROM DUAL
UNION ALL
SELECT 3 ID FROM DUAL
UNION ALL
SELECT 4 ID FROM DUAL
UNION ALL
SELECT 5 ID FROM DUAL) ,
TABLE2 AS(SELECT 1 ID, 'AAA' NAME FROM DUAL
UNION ALL
SELECT 2 ID, 'ABB' NAME FROM DUAL
UNION ALL
SELECT 3 ID, 'ACC' NAME FROM DUAL
UNION ALL
SELECT 4 ID, 'ADD' NAME FROM DUAL
UNION ALL
SELECT 1 ID, 'BBB' NAME FROM DUAL
) ,
sortable as( --here is the subquery
SELECT
Table1.ID ,
ROW_NUMBER( ) OVER (ORDER BY Table2.NAME NULLS LAST) ROWOverName , --this wil handle the sort
table2.name
from
Table1
LEFT OUTER JOIN --this left join it moot, pull the WHERE table2.name into the join to have it LEFT join as expected
Table2
on
(
Table1.id = Table2.id
)
WHERE
Table2.NAME LIKE '%A%')
SELECT *
FROM sortable
WHERE ROWOverName <= 2;
-- you can drop the ROW_NUMBER( ) analytic function and replace the final query as such (as you initially indicated)
SELECT *
FROM sortable
WHERE
ROWNUM <= 2
ORDER BY sortable.NAME --make sure to put in an order by!
;
You don't need DISTINCT here at all, and there is nothing bad in subqueries as such.
SELECT id
FROM Table1
WHERE id IN
(
SELECT id
FROM Table2
WHERE name LIKE '%a%'
)
AND rownum < 11
Note that the order is not guaranteed. To guarantee order, you have to use a nested query:
SELECT id
FROM (
SELECT id
FROM Table1
WHERE id IN
(
SELECT id
FROM Table2
WHERE name LIKE '%a%'
)
ORDER BY
id -- or whatever else
)
WHERE rownum < 11
There is no way to do it without nested queries (or the CTE).
For me there is no reason to be afraid of performance. I think the sub select ist the best way to solve your problem. And if you want don't trust me, take a look at explain plan of your query and you will see that it behave not so bad as you might think.