Subquery Order by Oracle - sql

My Database has 3 Tables, Device,Postal_Address and Company.
Every Device has an Company_ID, and for every company there is an address. The problem is, the old address is still there if i change it, its everytime making a new entry for this. So if i select all devices with address i get some entrys doubled.
select d.device,
(
select postalcode_address from
(
select
pa.postalcode_address,
row_number() over (order by pa.last_update desc) r,
pa.company_id
from
postal_address pa
)
where company_id=c.company_id
AND r=1
) as Postcode
from device d,company c,
where d.company_id = c.company_id(+)
I have tried it with order the address withlast_update, but its getting me nothing back, where is my error? so i need the newest entry in the postal_address for my company_id.

Your row_number() analytic call is getting the row number across all companies, so you'll only get a match for the most-recently-changed company; no other company will get r = 1. You need to partition by the company ID:
row_number() over (partition by pa.company_id order by pa.last_update desc) r,
Or move the where company_id=c.company_id inside that nested subquery; but that doesn't work in some versions of Oracle (the c alias might not be visible).
I wouldn't use a subquery to get the column value within the select list at all here though; you could use the same row number check as an inline view and join to it:
select d.device, pa.postalcode_address
from device d
left join company c on c.company_id = d.company_id
left join (
select company_id, postalcode_address,
row_number() over (partition by company_id order by last_update desc) rn
from postal_address
) pa on pa.company_id = c.company_id and pa.rn = 1;

Related

How to return only the most recent date when joining two tables

I have a list of physician NPI #'s that I need to convert into an internal customer ID #, and then use these customer ID's to check the last time they were contacted by a call center rep. To do this, I'm working with two tables, NPI_Conversion (cid, npi) and Call_Center (call_center_cid, customer_name, call_date). I only want to return the most recent date that a customer was contacted from the Call Center. This query gets me the data that I need, but it returns every call made to an NPI:
SELECT call_center_cid, cid, customer_name, caller_name, npi, call_date
FROM CALL_CENTER
LEFT JOIN NPI_CONVERSION
ON call_center_cid = cid
WHERE NPI IN ( # LIST OF RELEVANT NPI #'s)
I saw some other posts that used max(date), so I tried using it like this:
SELECT call_center_cid, cid, customer_name, caller_name, npi, max(call_date) AS recent_call
FROM CALL_CENTER
LEFT JOIN NPI_CONVERSION
ON call_center_cid = cid
WHERE NPI IN ( # LIST OF RELEVANT NPI #'s)
GROUP BY 1, 2, 3, 4, 5
But this only returns the max date per caller_name
A portable option uses a subquery for filtering:
select cc.*, nc.npi
from call_center cc
inner join npi_conversion nc on cc.call_center_cid = np.cid
where
nc.npi in (...)
and cc.call_date = (
select max(cc1.call_date)
from call_center cc1
where cc1.customer_name = cc.customer_name
)
This would take advantage of an index on call_center (customer_name, call_date).
Note that I changed the left join to an inner join (the filtering in the where clause makes it clear that's what youw ant), and prefixed all columns with the table they belong to (which makes the query much clearer about the underlying data structures).
If your database supports window functions, you can also do:
select cc.*, np.npi
from (
select cc.*,
rank() over(partition by customer_name order by call_center_cid desc) as rn
from call_center cc
) cc
inner join npi_conversion nc on cc.call_center_cid = np.cid
where nc.npi in (...)
Similar to GMB's answer:
You can apply a ROW_NUMBER after the join to get the latest row:
SELECT call_center_cid, cid, customer_name, caller_name, npi, call_date
FROM CALL_CENTER
LEFT JOIN NPI_CONVERSION -- RIGHT JOIN?
ON call_center_cid = cid
WHERE NPI IN ( # LIST OF RELEVANT NPI #'s)
QUALIFY -- filter latest date per cid
ROW_NUMBER()
OVER (PARTITION BY cid
ORDER BY call_date DESC) = 1
Your current WHERE turns the LEFT join into an INNER join, maybe you want a RIGHT join instead.

simple SQL subquery

It seems im a complete idiot when it comes to SQL....
All i need is get one value from other table, but there is multiple rows with same customerId on second table.. and i would need to get one with highest timestamp
CREATE OR REPLACE VIEW CUS_SETTINGS as
SELECT
c.id as id,
c.LANG as Language,
c.ALLOWEMAIL as AllowEmail,
l.CONFIRMED as confirmed
FROM cus.CUSTOMER c
????? something with l
/
LEFT JOIN will bring every row so i have multiple duplicate id's
What i need is propably subquery, but i cant get it to work...
(SELECT CONFIRMED FROM settings WHERE ?? c.id == l.id ?? AND MAX(TIMESTAMP) )
i've tried many many variations of joins and subqueries.. but for some reason.. SQL is just
too confusing....
You can use a ROW_NUMBER() in the subquery:
SELECT c.id as id, c.LANG as Language, c.ALLOWEMAIL as AllowEmail,
l.CONFIRMED as confirmed
FROM cus.CUSTOMER c JOIN
(SELECT s.*,
ROW_NUMBER() OVER (PARTITION BY s.id ORDER BY s.timestamp DESC) as seqnum
FROM settings s
) s
ON s.id = c.id AND s.seqnum = 1;
Note: You might want a LEFT JOIN if you want to keep all customers, even those with no settings.
You can use analytic functions. MAX() OVER(PARTITION BY) clause can give you max timestamped id.
Analytic Functions Docs 11gR2
SELECT SECONDD.CONFIRMED
FROM CUSTOMER CU
INNER JOIN
(SELECT
*
FROM (SELECT SECONDD.*,
MAX (S.TIMESTAMP) OVER (PARTITION BY S.ID)
AS MAXTIMESTAMP
FROM SETTINGS SECONDD)
WHERE TIMESTAMP = MAXTIMESTAMP) SECONDD
ON SECONDD.ID = CU.ID
Don't worry; while this sounds very basic, it isn't :-)
The easiest way to get the CONFIRMED for the latest TIMESTAMP in Oracle is KEEP LAST. E.g.:
SELECT customer_id, MAX(confirmed) KEEP (DENSE_RANK LAST ORDER BY timestamp)
FROM settings
GROUP BY customer_id;
The related CREATE VIEW statement:
CREATE OR REPLACE VIEW cus_settings as
SELECT
c.id AS id,
c.lang AS language,
c.allowemail AS allowemail,
l.last_confirmed AS confirmed
FROM cus.customer c
LEFT JOIN
(
SELECT
customer_id,
MAX(confirmed) KEEP (DENSE_RANK LAST ORDER BY timestamp) AS last_confirmed
FROM settings
GROUP BY customer_id;
) l ON l.customer_id = c.id;
Or:
CREATE OR REPLACE VIEW cus_settings as
SELECT
c.id AS id,
c.lang AS language,
c.allowemail AS allowemail,
(
SELECT MAX(confirmed) KEEP (DENSE_RANK LAST ORDER BY timestamp)
FROM settings s
WHERE s.customer_id = c.id
) AS confirmed
FROM cus.customer c;

Oracle SQL STATS_MODE with two tables

I have the tables listed in the image. I need to display the institution and loan type of the most used financing plan in the sale_financing table. I have tried using the stats_mode function but I have not been able to get it to work. I am supposed to only display the most used financing plan and I keep getting 3 showing up. Here is an image of the tables.
My image may not work so here are the tables:
financing_plan
id
institution
loan_type
sale_financings
id
plan_id ------> foreign key linking to - financing_plan.id
I have tried several different ways in the Query Builder and I cannot get it to work.
Here is one :
SELECT
financing_plans.institution,
financing_plans.loan_type,
STATS_MODE(sale_financings.plan_id) AS stats_mode_plan_id
FROM
financing_plans
INNER JOIN sale_financings ON financing_plans.id =
sale_financings.plan_id
GROUP BY
financing_plans.institution,
financing_plans.loan_type
Another:
SELECT
financing_plans.institution,
financing_plans.loan_type,
STATS_MODE(sale_financings.plan_id) AS stats_mode_plan_id
FROM
financing_plans
INNER JOIN sale_financings ON financing_plans.id =
sale_financings.plan_id
GROUP BY
financing_plans.institution,
financing_plans.loan_type
HAVING
STATS_MODE(sale_financings.plan_id) = sale_financings.plan_id
Count the use of each plan_id, then rank these (using dense_rank()) by the count (descending order) allows "top" and "equal top" to be shown.
select
fp.institution, fp.loan_type, s.plan_count
from financing_plan fp
inner join (
select plan_id, plan_count, dense_rank() over(order by plan_count DESC) as rnk
from (
select plan_id, count(id) plan_count
from sale_financings
Group by plan_id
)
) s on fp.id = s.plan_id and s.rnk = 1
order by
fp.institution, fp.loan_type
;

Sub query in a inner join

Well, in order to get the last entry by date i've done that query :
select cle
from ( select cle, clepersonnel, datedebut, row_number()
over(partition by clepersonnel order by datedebut desc) as rn
from periodeoccupation) as T
where rn = 1
This one is working and give me the last entry by date, so far i'm ok.
But its the first time i work with subquery and i have to make my query way more complex with multiple joins. But i cannot figure out how to make a subquery in a inner join.
This is what i try :
select personnel.prenom, personnel.nom
from personnel
inner join
( select cle, clepersonnel, datedebut, row_number()
over(partition by clepersonnel order by datedebut desc) as rn
from periodeoccupation) as T
ON personnel.cle = periodeoccupation.clepersonnel
where rn = 1
but its not working !
If you have any idea or tips...Thank you !
Simply change
ON personnel.cle = periodeoccupation.clepersonnel
to
ON personnel.cle = T.clepersonnel
The query join has been aliased with T and you have reference the alias as the table in the aliased query is out of scope in your outer statement.

Find MAX with JOIN where Field also shows up in another Table

I have 3 tables: Master, Paper and iCodes. For a certain set of Master.Ref's, I need to find Max(Paper.Date), where the Paper.Code is also in the iCodes table (i.e., Paper.Code is a type of iCode). Master is joined to Paper by the File field.
EDIT:
I only need the Max(Paper.Date) its corresponding Code; I do not need all of the Codes.
I wrote the following but it is very slow. I have a few hundred ref #'s to look for. What is a better way to do this?
SELECT Master.Ref,
Paper.Code,
mp.MaxDate
FROM ( SELECT p.File ,
MAX(p.Date) AS MaxDate ,
FROM Paper AS p
LEFT JOIN Master AS m ON p.File = m.File
WHERE m.Ref IN ('ref1', 'ref2', 'ref3', 'ref4', 'ref5', 'ref6'... )
AND p.Code IN ( SELECT DISTINCT i.iCode
FROM iCodes AS i
)
GROUP BY p.File
) AS mp
LEFT JOIN Master ON mp.File = Master.File
LEFT JOIN Paper ON Master.File = Paper.File
AND mp.MaxDate = Paper.Date
WHERE Paper.Code IN ( SELECT DISTINCT iCodes.iCode
FROM iCodes
)
Does this do what you want?
SELECT m.Ref, p.Code, max(p.date)
FROM Master m LEFT JOIN
Paper
ON m.File = p.File
WHERE p.Code IN (SELECT DISTINCT iCodes.iCode FROM iCodes) and
m.Ref IN ('ref1','ref2','ref3','ref4','ref5','ref6'...)
GROUP BY m.Ref, p.Code;
EDIT:
To get the code on the max date, then use window functions:
select ref, code, date
from (SELECT m.Ref, p.Code, p.date
row_number() over (partition by m.Ref order by p.date desc) as seqnum
FROM Master m LEFT JOIN
Paper
ON m.File = p.File
WHERE p.Code IN (SELECT DISTINCT iCodes.iCode FROM iCodes) and
m.Ref IN ('ref1','ref2','ref3','ref4','ref5','ref6'...)
) mp
where seqnum = 1;
The function row_number() assigns a sequential number starting at 1 to a group of rows. The groups are defined by the partition by clause, so in this case everything with the same m.Ref value would be in a single group. Within the group, rows are assigned the number based on the order by clause. So, the one with the biggest date gets the value of 1. That is the row you want.