Oracle SQL - Select not using index as expected - sql

So I haven't used Oracle in more than 5 years and I'm out of practice. I've been on SQL Server all that time.
I'm looking at some of the existing queries and trying to improve them, but they're reacting really weirdly. According to the explain plan instead of going faster they're instead doing full table scans and not using the indexes.
In the original query, there is an equijoin done between two tables done in the where statement. We'll call them table A and B. I used an explain plan followed by SELECT * FROM table(DBMS_XPLAN.DISPLAY (FORMAT=>'ALL +OUTLINE')); and it tells me that Table A is queried by Local Index.
TABLE ACCESS BY LOCAL INDEX ROWID
SELECT A.*
FROM TableA A, TableB B
WHERE A.SecondaryID = B.ID;
I tried to change the query and join TableA with a new table (Table C). Table C is a subset of Table B with 700 records instead of 100K. However the explain plan tells me that Table A is now queried with a full lookup.
CREATE TableC
AS<br>
SELECT * FROM TableB WHERE Active='Y';
SELECT A.*
FROM TableA A, TableC C
WHERE A.SecondaryID = C.ID;
Next step, I kept the join between tables A & C, but used a hint to tell it to use the index on Table A. However it still does a full lookup.
SELECT /*+ INDEX (A_NDX01) */ A.*
FROM TableA A, TableC C
WHERE A.SecondaryID = C.ID;
So I tried to change from a join to a simple Select of table A and use an IN statement to compare to table C. Still a full table scan.
SELECT A.*
FROM TableA A
WHERE A.SecondaryID in (SELECT ID FROM TableC);
Lastly, I took the previous statement and changed the subselect to pull the top 1000 records, and it used the index. The odd thing is that there are only 700 records in Table C.
SELECT A.*
FROM TableA A
WHERE A.SecondaryID in (SELECT ID FROM TableC WHERE rownum <1000
)
I was wondering if someone could help me figure out what's happening?
My best guess is that since TableC is a new table, maybe the optimizer doesn't know how many records are in it and that's why it's it will only use the index if it knows that there are fewer than 1000 records?
I tried to run dbms_stats.gather_schema_stats on my schema though and it did not help.
Thank you for your help.

As a general rule Using an index will not necessarily make your query go faster ALWAYS.
Hints are directives to the optimizer to make use of the path, it doenst mean optimizer would choose to obey the hint directive. In this case, the optimizer would have considered that an index lookup on TableA is more expensive in the
SELECT A.*
FROM TableA A, TableB B
WHERE A.SecondaryID = B.ID;
SELECT /*+ INDEX (A_NDX01) */ A.*
FROM TableA A, TableC C
WHERE A.SecondaryID = C.ID;
SELECT A.*
FROM TableA A
WHERE A.SecondaryID in (SELECT ID FROM TableC);
Internally it might have converted all of these statements(IN) into a join which when considering the data in the tableA and tableC decided to make use of full table scan.
When you did the rownum condition, this plan conversion was not done. This is because view-merging will not work when it has the rownum in the query block.
I believe this is what is happening when you did
SELECT A.*
FROM TableA A
WHERE A.SecondaryID in (SELECT ID FROM TableC WHERE rownum <1000)
Have a look at the following link
Oracle. Preventing merge subquery and main query conditions

Related

In SQL is there a way to use select * on a join?

Using Snowflake,have 2 tables, one with many columns and the other with a few, trying to select * on their join, get the following error:
SQL compilation error:duplicate column name
which makes sense because my joining columns are in both tables, could probably use select with columns names instead of *, but is there a way I could avoid that? or at least have the query infer the columns names dynamically from any table it gets?
I am quite sure snowflake will let you choose all from both halves of two+ tables via
SELECT a.*, b.*
FROM table_a AS a
JOIN table_b AS b
ON a.x = b.x
what you will not be able to do is refer to the named of the columns in GROUP BY indirectly, thus this will not work
SELECT a.*, b.*
FROM table_a AS a
JOIN table_b AS b
ON a.x = b.x
ORDER BY x
even though some databases know because you have JOIN ON a.x = b.x there is only one x, snowflake will not allow it (well it didn't last time I tried this)
but you can with the above use the alias name or the output column position thus both the following will work.
SELECT a.*, b.*
FROM table_a AS a
JOIN table_b AS b
ON a.x = b.x
ORDER BY a.x
SELECT a.*, b.*
FROM table_a AS a
JOIN table_b AS b
ON a.x = b.x
ORDER BY 1 -- assuming x is the first column
in general the * and a.* forms are super convenient, but are actually bad for performance.
when selecting you are now are risk of getting the columns back in a different order if the table has been recreated, thus making reading code unstable. Which also impacts VIEWs.
It also means all meta data for the table need to be loaded to know what the complete form of the data will be in. Where if you want x,y,z only and later a w was added to the table, the whole query plan can be compiled faster.
Lastly if you are selecting SELECT * FROM table in a sub-select and only a sub-set of those columns are needed the execution compiler doesn't need to prune these. And if all variables are attached to a correctly aliased table, if later a second table adds the same named column, naked columns are not later ambiguous. Which will only occur when that SQL is run, which might be an "annual report" which doesn't happen that often. wow, what a long use alias rant.
You can prefix the name of the column with the name of the table:
select table_a.id, table_b.name from table_a join table_b using (id)
The same works in combination with *:
select table_a.id, table_b.* from table_a join table_b using (id)
It works in "join" and "where" parts of the statement as well
select table_a.id, table_b.* from table_a join table_b
on table_a.id = table_b.id where table_b.name LIKE 'b%'
You can use table aliases to make the statement sorter:
select a.id, b.* from table_a a join table_b b
on a.id = b.id
Aliases could be applies on fields to use in subqueries, client software and (depending on the SQL server) in the other parts of the statements, for example 'order by':
select a.id as a_id, b.* from table_a a join table_b b
on a.id = b.id order by a_id
If you're after a result that includes all the distinct non-join columns from each table in the join with the join columns included in the output only once (given they will be identical for an inner-join) you can use NATURAL JOIN.
e.g.
select * from d1 natural inner join d2 order by id;
See examples: https://docs.snowflake.com/en/sql-reference/constructs/join.html#examples

Full outer join tables on one or other column using OR in ON clause

I want to do a full outer join on 2 tables - Table_A and Table_B on 2 columns Unique_ID1 OR Unique_ID2 -as some rows may match on one and others on another and I have no way to determine this.
I tried this -
Select *
from Table_A
full outer join Table_B on Table_A.Unique_ID1 = Table_A.Unique_ID1
OR Table_A.Unique_ID2 = Table_A.Unique_ID2
While this gives me no error, the query runs forever. What is the best way to re-structure this to get the desired output?
I think this is the query you actually were intending to run:
SELECT *
FROM Table_A a
FULL OUTER JOIN Table_B b
ON a.Unique_ID1 = b.Unique_ID1 OR a.Unique_ID2 = b.Unique_ID2;
That being said, if you still have performance problems with the above, then adding indices might be in order here. The ON clause of your query is actually a bit tricky to index. You could rewrite the query as a union:
SELECT *
FROM Table_A a
FULL OUTER JOIN Table_B b ON a.Unique_ID1 = b.Unique_ID1
UNION
SELECT *
FROM Table_A a
FULL OUTER JOIN Table_B b ON a.Unique_ID2 = b.Unique_ID2;
Then add the following indices:
CREATE INDEX idx1 ON Table_A (Unique_ID1);
CREATE INDEX idx2 ON Table_A (Unique_ID2);
CREATE INDEX idx1 ON Table_B (Unique_ID1);
CREATE INDEX idx2 ON Table_B (Unique_ID2);
While my union version might not logically be identical to your current query, you can probably tweak it to make it so. And, it is likely to be sargable, with both halves of the union using an index.
Your condition always evaluated to true since you have the column from Table_A in both sides of the equality check, producing a full Cartesian product. Replace one side of the equality with Table_B, and you should be OK:
SELECT *
FROM Table_A
FULL OUTER JOIN Table_B ON Table_A.Unique_ID1 = Table_B.Unique_ID1 OR
-- Here ----------------------------------------------^
Table_A.Unique_ID2 = Table_B.Unique_ID2
-- And here ------------------------------------------^

Slow performance query

I'm having a slow query performance and sometimes it came to an error of "Can't allocate space for object 'temp work table'"
I have 2 tables and 1 view. The first two tables have an left join and the last view will do a sub query. Below is the sample query.
SELECT a.*
FROM Table1 a LEFT JOIN Table2 b ON a.ID = b.ID
WHERE a.ID (SELECT ID
FROM View1).
The above query is very slow. BUT when I used a #temp table it becomes faster.
SELECT ID
INTO #Temp
FROM View1
SELECT a.*
FROM Table1 a LEFT JOIN Table2 b ON a.ID = b.ID
WHERE a.ID IN (SELECT ID
FROM #Temp)
Could someone explain why the first sql statement is very slow? and kindly give me an advise like adding new index?
Note: The first query statement cannot be altered or modified. I used only the second query statement to show to my team that if we put the 3rd table into temporary table and used it, makes faster.
Basically in the first query you are accessing the view for each and every row, and in turn the view is executing it's query.
In the second one you are executing the view's query just once and using the returned results through the temp table.
Try:
SELECT a.*
FROM Table1 a LEFT JOIN Table2 b ON a.ID = b.ID,
(SELECT ID
FROM View1) c
WHERE a.ID = c.ID;

Weird SQL request(Join)

Assuming there are two tables A={a,b} and B={0,1,2}, which can be joined
tableA tableB
a 0
b 1
a 2
3
How to get the following result
ExpectingResult:
tableA tableB
a-------0
b-------1
null----2
null----3
OR
tableA tableB
a-------2
b-------3
null----0
null----1
Just make sure the element in each table just appear once, I tried all kinds of join(inner, full, cross), none of them can achieve so. Could anybody give me a tip?
Thank you very much
Please check this link out to the question itself: http://www.sqlfiddle.com/#!3/9fc21/2
That is a crappy request. There is almost negligible reasons for producing such an output that comes to mind, although I'm not ruling out a sane reason completely.
For SQL Server 2000, you will need to go through temp tables to get a sequential key to zip up with.
SELECT IDENTITY(int,1,1) ID, Value
INTO #tblA
FROM tableA
ORDER BY Value;
SELECT IDENTITY(int,1,1) ID, Value
INTO #tblB
FROM tableB
ORDER BY Value;
SELECT A.Value, B.Value
FROM #tblA A FULL OUTER JOIN #tblB B ON A.ID = B.ID
ORDER BY Coalesce(A.ID, B.ID);
Using SQL Server 2005, you can use a combination of ROW_NUMBER and a FULL OUTER JOIN to combine both results.
See SQL Fiddle
Unfortunately, SQL Server 2000 doesn't have a ROW_NUMBER function so you are kind of stuck with using temporary tables with identity fields to simulate a rownumber.
The gist of this would be to
Select your required data from tableA into #tempTableA, adding an identity field.
Repeat for tableB
Use the temptables to FULL OUTER JOIN the results

SQL Select queries

Which is better and what is the difference?
SELECT * FROM TABLE_A A WHERE A.ID IN (SELECT B.ID FROM TABLE_B B)
or
SELECT * FROM TABLE_A A, TABLE_B B WHERE A.ID = B.ID
The "best" way is to use the standard ANSI JOIN syntax:
SELECT (columns)
FROM TABLE_A a
INNER JOIN TABLE_B b
ON b.ID = a.ID
The first WHERE IN version will often result in the same execution plan, but on certain platforms it can be slower - it's not always consistent. The IN query (which is equivalent to EXISTS) is also going to become progressively more cumbersome to write and maintain as you start to add more tables or create more complex join conditions - it's not as flexible as an actual JOIN.
The second, comma-separated syntax is not as consistently supported as JOIN. It does work on most SQL DBMSes, but it's not the "preferred" version because if you leave out the WHERE clause then you end up with a cross-product. Whereas if you forget to write in the JOIN condition, you'll just end up with a syntax error. JOIN tends to be preferred because of this safety net.
I upvoted #Aaronaught's answer, but I have some comments:
Both the comma-style join syntax and the JOIN syntax are ANSI. The first is SQL-89, and the second is SQL-92. The SQL-89 syntax is still part of the standard, to support backward compatibility.
Can you give an example of an RDBMS that supports the SQL-92 syntax but not the SQL-89? I don't think there are any, so "not as consistently supported" may not be accurate.
You can also omit the join condition using JOIN syntax, and create a Cartesian product. Example: SELECT ... FROM A JOIN B is valid (correction: this is true only in some brands that implement the standard syntax loosely, such as MySQL).
But in any case I agree this is easier to spot when you use SQL-92 syntax. If you use SQL-89 syntax you may end up with a long WHERE clause and it's too easy to miss one of your join conditions.
The difference is that the first does a subquery which can be slower in some databases. And the second does a join, combining both tables in the same query.
Generally, the second would be faster if the database won't optimize it since with a subquery the database would have to keep the results of the subquery in memory.
These two queries return different results. You select only columns from TABLE_A in the first.
There are at least three differences between query X:
SELECT * FROM TABLE_A A WHERE A.ID IN (SELECT B.ID FROM TABLE_B B)
and Y:
SELECT * FROM TABLE_A A, TABLE_B B WHERE A.ID = B.ID
1) As Michas said, the set of columns will be different, where query Y will return the columns from tables A & B, but query X only returns the columns from table A. If you explicitly name which columns you want back, query X can only include columns from table A, but query Y would include columns from table B.
2) The number of rows may be different. If table B has more than on ID matching an ID from table A, then more rows will be returned with Query Y than X.
create table TABLE_A (ID int, st VARCHAR(10))
create table TABLE_B (ID int, st VARCHAR(10))
insert into TABLE_A values (1, 'A-a')
insert into TABLE_B values (1, 'B-a')
insert into TABLE_B values (1, 'B-b')
SELECT * FROM TABLE_A A WHERE A.ID IN (SELECT B.ID FROM TABLE_B B)
ID st
----------- ----------
1 A-a
(1 row(s) affected)
SELECT * FROM TABLE_A A, TABLE_B B WHERE A.ID = B.ID
ID st ID st
----------- ---------- ----------- ----------
1 A-a 1 B-a
1 A-a 1 B-b
(2 row(s) affected)
3) The execution plans will probably be different, since the queries are asking the database for different results. Inner joins used to run faster than in or exists and may still run faster in some cases. But since the results can be different you need to make sure that the data supports the transformation from a in or exists to a join.