Limit number of rows fetching in a left outer join in Oracle - sql

I'm going to create a data model in oracle fusion applications. I need to create column End_date based on two tables in the query. So I used two methods.
Using a subquery:
SELECT *
FROM (SELECT projects_A.end_date
FROM projects_A, projects_B
WHERE projects_A.p_id = projects_B.p_id
AND rownum = 1)
Using a LEFT OUTER JOIN:
SELECT projects_A.end_date
FROM projects_A
LEFT JOIN projects_B
ON projects_A.p_id = projects_B.p_id
WHERE rownum = 1
Here when I used a subquery, the query returns the results as expected. But when I use left outer join with WHERE rownum = 1 the result is zero. Without WHERE rownum = 1 it retrieves all the results. But I want only the first result. So how can I do that using left outer join? Thank you.

Looks like you want to bring a non-null end_date value(So, add NULLS LAST), but the sorting order is not determined yet(you might add a DESC to the end of the ORDER BY clause depending on this fact ), and use FETCH clause(the DB version is 12c+ as understood from the comment) with ONLY option to exclude ties as yo want to bring only single row.
So, you can use the following query :
SELECT A.end_date
FROM projects_A A
LEFT JOIN projects_B B
ON A.p_id = B.p_id
ORDER BY end_date NULLS LAST
FETCH FIRST 1 ROW ONLY

Related

Can we select first row of data from column in sql?

I have a table with multiple data for same ID. I want to get the first row data for the ID.
I have added the below SQL that I have tried.
SELECT
"client"."id",
"client"."company_name",
"client_details"."address"
from Client
LEFT OUTER JOIN "client_details" ON ("client"."id" = "client_details"."client_id")
Since I have multiple address for the same ID, can we get only the first id?
Currently the output I get is 2 rows with different addresses.
You can add to your SQL LIMIT 1 and in case you want to be sure the order you can also add to your SQL ORDER BY...
You can use distinct on:
select distinct on (c.id) c.id, c.company_name, cd.address
from Client c left join
client_details cd
on c.id = cd.client_id
order by c.id, ?;
The ? is for the column that specifies the ordering (the definition of "first"). I am guessing that cd.id is what you want.
Note that this query removes the double quotes and introduces table aliases. This is easier on both the eyes (to read) and the fingers (to type).
use row_number()
select * from
(
SELECT
"client"."id",
"client"."company_name",
"client_details"."address",row_number() over(partition by "client"."id" order by "client_details"."address") as rn
from Client
LEFT OUTER JOIN "client_details" ON "client"."id" = "client_details"."client_id"
)A where rn=1
If there is a field you can order the results by you could use a lateral join e.g.
SELECT
"client"."id",
"client"."company_name",
"client_details"."address"
from Client
left join lateral (
select *
from client_details cd
where cd.client_id = client.id
order by [some_ordering_field]
limit 1
) "client_details" on true

SQL query on a random row via Oracle

I am using ORACLE database and I am trying to access one random row from my SQL query however the query is not retrieving back a random row and I am not sure what I did wrong?
My query is:
SELECT a.car_id, b.product_id
FROM listing a, carProduct b
WHERE a.car_id = b.car_id
AND a.certified = 'TRUE'
AND b.product_id like '%CERT'
AND rownum = 1
ORDER BY DBMS_RANDOM.RANDOM
The rownum has to come after the order by. You need a subquery:
SELECT lcp.*
FROM (SELECT l.car_id, cp.product_id
FROM listing l join
carProduct cp
on l.car_id = cp.car_id
WHERE l.certified = 'TRUE' AND cp.product_id like '%CERT'
ORDER BY DBMS_RANDOM.RANDOM
) lcp
WHERE rownum = 1;
Notes:
Learn to use proper, explicit JOIN syntax. Simple rule: Never use commas in the FROM clause.
Use table aliases that make sense, such as abbreviations for the table names.
The rownum = 1 needs to go in the outer query.
In Oracle 12c+, you don't need a subquery. You can just use fetch first 1 row only.

Postgres left outer join appears to not be using table indices

Let me know if this should be posted on DBA.stackexchange.com instead...
I have the following query:
SELECT DISTINCT "court_cases".*
FROM "court_cases"
LEFT OUTER JOIN service_of_processes
ON service_of_processes.court_case_id = court_cases.id
LEFT OUTER JOIN jobs
ON jobs.service_of_process_id = service_of_processes.id
WHERE
(jobs.account_id = 250093
OR court_cases.account_id = 250093)
ORDER BY
court_cases.court_date DESC NULLS LAST,
court_cases.id DESC
LIMIT 30
OFFSET 0;
But it takes a good 2-4 seconds to run, and in a web application this is unacceptable for a single query.
I ran EXPLAIN (ANALYZE, BUFFERS) on the query as suggested on the PostgreSQL wiki, and have put the results here: http://explain.depesz.com/s/Yn6
The table definitions for those tables involved in the query is here (including the indexes on foreign key relationships):
http://sqlfiddle.com/#!15/114c6
Is it having issues using the indexes because the WHERE clause is querying from two different tables? What kind of index or change to the query can I make to make this run faster?
These are the current sizes of the tables in question:
PSQL=# select count(*) from service_of_processes;
count
--------
103787
(1 row)
PSQL=# select count(*) from jobs;
count
--------
108995
(1 row)
PSQL=# select count(*) from court_cases;
count
-------
84410
(1 row)
EDIT: I'm on Postgresql 9.3.1, if that matters.
or clauses can make optimizing a query difficult. One idea is to split the two parts of the query into two separate subqueries. This actually simplifies one of them a lot (the one on court_cases.account_id).
Try this version:
(SELECT cc.*
FROM "court_cases" cc
WHERE cc.account_id = 250093
ORDER BY cc.court_date DESC NULLS LAST,
cc.id DESC
LIMIT 30
) UNION ALL
(SELECT cc.*
FROM "court_cases" cc LEFT OUTER JOIN
service_of_processes sop
ON sop.court_case_id = cc.id LEFT OUTER JOIN
jobs j
ON j.service_of_process_id = sop.id
WHERE (j.account_id = 250093 AND cc.account_id <> 250093)
ORDER BY cc.court_date DESC NULLS LAST, id DESC
LIMIT 30
)
ORDER BY court_date DESC NULLS LAST,
id DESC
LIMIT 30 OFFSET 0;
And add the following indexes:
create index court_cases_accountid_courtdate_id on court_cases(account_id, court_date, id);
create index jobs_accountid_sop on jobs(account_id, service_of_process_id);
Note that the second query uses and cc.count_id <> 250093, which prevents duplicate records. This eliminates the need for distinct or for union (without union all).
I'll try modifying the query as the following:
SELECT DISTINCT "court_cases".*
FROM "court_cases"
LEFT OUTER JOIN service_of_processes
ON service_of_processes.court_case_id = court_cases.id
LEFT OUTER JOIN jobs
ON jobs.service_of_process_id = service_of_processes.id and jobs.account_id = 250093
WHERE
(court_cases.account_id = 250093)
ORDER BY
court_cases.court_date DESC NULLS LAST,
court_cases.id DESC
LIMIT 30
OFFSET 0;
I think that the issue is in the fact that the where filter is not properly decomposed by query planner optimizer, a really strange performance bug

How can you add 2 joins in a subquery?

I am trying to get information from 3 tables in my database. I am trying to get 4 fields. 'kioskid', 'kioskhours', 'videotime', 'sessiontime'. In order to do this, i am trying a join in a subquery. This is what I have so far:
SELECT k.kioskid, k.hours, v.time, s.time
FROM `nsixty_kiosks` as k
LEFT JOIN (SELECT time
FROM `nsixty_videos`
ORDER BY videoid) as v
ON kioskid = k.kioskid LEFT JOIN
(SELECT kioskid, time
FROM `sessions`
ORDER BY pingid desc LIMIT 1) as s ON s.kioskid = k.kioskid
WHERE hours is NOT NULL
When I run this query, it works but it shows every row instead of just showing the last row of each kiosk id. Which is meant to show based on the line 'ORDER BY pingid desc LIMIT 1'.
Any body have some ideas?
Instead of joining to s, you can use a correlated subquery:
SELECT k.kioskid,
k.hours,
v.time,
( SELECT time
FROM sessions
WHERE sessions.kioskid = k.kioskid
ORDER
BY pingid DESC
LIMIT 1
)
FROM nsixty_kiosks AS k
LEFT
JOIN ( SELECT time
FROM `nsixty_videos`
ORDER BY videoid
) AS v
ON kioskid = k.kioskid
WHERE hours IS NOT NULL
;
N.B. I didn't fix your LEFT JOIN (...) AS v, because I don't understand what it's trying to do, but it too is broken; the ON clause doesn't refer to any of its columns, and there's no point in having an ORDER BY in a subquery unless you also have a LIMIT or whatnot in there.
Well, your join on the 'v' subquery doesn't actually reference the 'v' subquery, nor does the 'v' subquery even contain a kioskid field to JOIN on, so that's undoubtedly part of the problem.
To go much further we'd need to see schema and sample data.

MYSQL join - reference external field from nested select?

Is it allowed to reference external field from nested select?
E.g.
SELECT
FROM ext1
LEFT JOIN (SELECT * FROM int2 WHERE int2.id = ext1.some_id ) as x ON 1=1
in this case, this is referencing ext1.some_id in nested select.
I am getting errors in this case that field ext1.some_id is unknow.
Is it possible? Is there some other way?
UPDATE:
Unfortunately, I have to use nested select, since I am going to add more conditions to it, such as LIMIT 0,1
and then I need to use a second join on the same table with LIMIT 1,1 (to join another row)
The ultimate goal is to join 2 rows from the same table as if these were two tables
So I am kind of going to "spread" a few related rows into one long row.
The answer to your initial question is: No, remove your sub-query and put the condition into the ON-clause:
SELECT *
FROM ext1
LEFT JOIN int2 ON ( int2.id = ext1.some_id )
One solution could be to use variables to find the first (or second) row, but this solution would not work efficiently with indexes, so you might end up with performance problems.
SELECT ext1.some_id, int2x.order_col, int2x.something_else
FROM ext1
LEFT JOIN (SELECT `int2`.*, #i:=IF(#id=(#id:=id), #i+1, 0) As rank
FROM `int2`,
( SELECT #i:=0, #id:=-1 ) v
ORDER BY id, order_col ) AS int2x ON ( int2x.id = ext1.some_id
AND int2x.rank = 0 )
;
This assumes that you have a column that you want to order by (order_col) and Left Joins the first row per some_id.
Do you mean this?
SELECT ...
FROM ext1
LEFT JOIN int2 ON int2.id=ext1.some_id
That's what the ON clause is for:
SELECT
FROM ext1
LEFT JOIN int2 AS x ON x.id = ext1.some_id