Select client informations about his reservation in SQL*Plus - oracle

I'd like to isplay the name, address and phone number for the clients who has not made any reservations for the past two months.
The tables are :
CLIENT (
ClientNo,
Name,
Sex,
DOB,
Address,
Phone,
Email,
Occupation,
MaritalStatus,
Spouse,
Anniversary
)
RESERVATION
ResNo,
ResDate,
NoOfGuests,
StartDate,
EndDate,
ClientNo,
Status
)
I tried:
SELECT Name, Address, Phone, ResNo
FROM Client C, Reservation R
WHERE Date_Column >= ResDate R(MONTH, -3, GETDATE())
ORDER BY Name DESC

You need an outer join, with all conditions in the join, and then filter out all successful joins:
SELECT C.*
FROM Client C
LEFT JOIN Reservation R ON C.ID = R.ID -- outer join
AND R.ResDate >= add_months(TRUNC(SYSDATE) + 1, 2) -- condition in join
WHERE R.ID IS NULL -- only return missed joins
ORDER BY Name ASC
You want the missed joins - ones where there are no reservations at all given the conditions. Such missed joins have all-nulls in the joined table's columns, so filtering on (one of) those columns being null will return clients who don't have any such reservations.

Try to make your where condition like
ResDate >= add_months(TRUNC(SYSDATE) + 1, 2)
Also you are missing the JOIN for your table. So you need to put the JOIN like
SELECT C.Name, C.Address, C.Phone, R.ResNo, R.ResDate
FROM Client C INNER JOIN Reservation R ON C.ID = R.ID --Change the column for Joing as per your table structure
WHERE R.ResDate >= add_months(TRUNC(SYSDATE) + 1, 2))
ORDER BY Name ASC;

Related

Assign a Name to the Sub queries in Oracle

I have 2 sub queries which are repeated and are very long so I want to give it a name and then refer that name in the query as mentioned in https://stackoverflow.com/a/3058938/6700081.
I have a Customers table with ID, Name, OrderID
I have the Orders table with ID (this is the order ID so it is also the Foreign key), Price, Order_Date
This is my original query which is working fine:
SELECT CUSTOMERS.name, ORDERS.price FROM
CUSTOMERS INNER JOIN ORDERS
ON (CUSTOMERS.ORDER_ID = ORDERS.ID)
WHERE ORDERS.PRICE = (SELECT MAX(ORDERS.PRICE) from ORDERS where ORDERS.ORDER_DATE <= (SELECT ADD_MONTHS((SELECT MIN(ORDER_DATE) FROM ORDERS), 12*10) from ORDERS WHERE ROWNUM = 1))
AND ORDERS.ORDER_DATE <= (SELECT ADD_MONTHS((SELECT MIN(ORDER_DATE) FROM ORDERS), 12*10) from ORDERS WHERE ROWNUM = 1);
I tried to change it to a named query as below:
WITH MAX_ORDER_DATE as (SELECT ADD_MONTHS((SELECT MIN(ORDER_DATE) FROM ORDERS), 12*10) from ORDERS WHERE ROWNUM = 1),
WITH MAX_ORDER_PRICE as (SELECT MAX(ORDERS.PRICE) from ORDERS where ORDERS.ORDER_DATE <= (MAX_ORDER_DATE))
SELECT CUSTOMERS.name, ORDERS.price FROM
CUSTOMERS INNER JOIN ORDERS
ON (CUSTOMERS.ORDER_ID = ORDERS.ID)
WHERE ORDERS.PRICE = (MAX_ORDER_PRICE)
AND ORDERS.ORDER_DATE <= (MAX_ORDER_DATE);
But I get an error related to invalid table name. What is wrong with this query?
Your corrected query:
WITH MAX_ORDER_DATE AS (
SELECT ADD_MONTHS((SELECT MIN(ORDER_DATE) FROM ORDERS), 12*10) AS max_date
FROM ORDERS
WHERE ROWNUM = 1
),
MAX_ORDER_PRICE AS (
SELECT MAX(ORDERS.PRICE) AS max_price
FROM ORDERS
WHERE ORDERS.ORDER_DATE <= (SELECT max_date FROM MAX_ORDER_DATE)
)
SELECT c.name, o.price
FROM CUSTOMERS c
INNER JOIN ORDERS o
ON c.ORDER_ID = o.ID
WHERE
o.PRICE = (SELECT max_price FROM MAX_ORDER_PRICE) AND
o.ORDER_DATE <= (SELECT max_date FROM MAX_ORDER_DATE);
The main issues I noticed with your syntax were that you repeated WITH for each common table expression, when you only need to state it once at the beginning of the definitions. Also, if you want to use the single values you define in the two CTEs, you should use a subquery against those CTEs. Finally, I added aliases to the columns you select in the CTEs.

Selecting one random data from a column from multiple rows in oracle

I am creating a view that needs to select only one random row for each customer. Something like:
select c.name, p.number
from customers c, phone_numbers p
where p.customer_id = c.id
If this query returns:
NAME NUMBER
--------- ------
Customer1 1
Customer1 2
Customer1 3
Customer2 4
Customer2 5
Customer3 6
I need it to be something like:
NAME NUMBER
--------- ------
Customer1 1
Customer2 4
Customer3 6
Rownum wont work because it will select only the first from all 6 records, and i need the first from each customer. I need solution that won't affect performance much, because the query that selects the data is pretty complex, this is just an example to explain what I need. Thanks in advance.
Use the ROW_NUMBER() analytic function:
SELECT name,
number
FROM (
SELECT c.name,
p.number,
ROW_NUMBER() OVER ( PARTITION BY c.id ORDER BY DBMS_RANDOM.VALUE ) AS rn
FROM customers c
INNER JOIN phone_numbers p
ON ( p.customer_id = c.id )
)
WHERE rn = 1
You can use group by clause to return only one phone number:
select c.name, MAX(p.number) as phone
from customers c, phone_numbers p
where p.customer_id = c.id
group by c.name
You can also use the min or max aggregate function with the keep dense_rank syntax:
select c.name,
min(p.number) keep (dense_rank last order by dbms_random.value) as number
from customers c
join phone_numbers p on p.customer_id = c.id
group by c.id, c.name
order by c.name;
(number isn't a valid column or alias name as it's a reserved word, so use your own real name of course).
If the phone number needs to be arbitrary rather than actually random, you can order by something else:
select c.name,
min(p.number) keep (dense_rank last order by null) as number
from customers c
join phone_numbers p on p.customer_id = c.id
group by c.id, c.name
order by c.name;
You'll probably get the same number back for each customer each time, but not always, and data/stats/plan changes will affect which you see. It seems like you don't care though. But then, using a plain aggregate might work just as well for you, as in #under's answer.
If you're getting lots of columns from that random row, rather than just the phone number, MTO's subquery might be simpler; for one or two values I find this a bit clearer though.

Oracle select distinct with join and multiple columns

Unfortunately this database has a ton of duplicate email addresses in it. I need to do a query and return only unique emails, doesn't really matter which one.
The query I have looks like this, can't really figure out what to add to not get duplicate emails returned. Can anyone think of anything?
select c.cid, c.email, c.uuid, e.code
from c
inner join e on e.cid = c.cid
where regexp_like(c.email, '\.net$', 'i');
-- Adding some additional info on request
The above query returns the following results, where you can see there are duplicates. I'm interested in only returning one row per unique email address.
3478|cust1#cust1.net|ouskns;dhf|1
3488|cust2#cust2.net|jlsudo;uff|0
3598|cust3#cust3.net|dl;udjffff|1
3798|cust1#cust1.net|osuosdujff|1
3888|cust1#cust1.net|odsos7jfff|1
-- Solution, thanks Mathguy
select cid, email, uuid, code
from
(select c.cid, c.email, c.uuid, e.code, row_number() over (partition by
c.email order by null) as rn
from c
inner join e on e.cid = c.cid
where regexp_like(c.email, '\.net$', 'i')
)
where rn = 1;
If it works as is and the only problem is the duplicates, you can change c.email to MAX(c.email) as email in the select clause, and add a group by clause to group by the other columns included in select.
EDIT: (actually I should delete the original answer since the OP clarified his question was quite different from what he seemed to ask originally - but that would also delete the comments... so editing instead)
If your query produces the desired results, but now you must pick just one random row per email address, you can try this:
select cid, email, uuid, code
from
( -- .... copy your select query here
-- ADD one column to the select line like so:
-- select c.cid, c.uuid, c.email, e.code,
-- row_number() over (partition by c.email order by null) as rn
-- ....
)
where rn = 1;
Using DISTINCT :
select DISTINCT c.email
from c
inner join e on e.cid = c.cid
where regexp_like(c.email, '\.net$', 'i');
Or using GROUP BY (and you get the number of dups in the cnt column)
select c.email, count(*) as cnt
from c
inner join e on e.cid = c.cid
where regexp_like(c.email, '\.net$', 'i')
GROUP BY c.email;

Joining the top result in Oracle

I'm using this query:
SELECT *
FROM HISTORY
LEFT JOIN CUSTOMER ON CUSTOMER.CUST_NUMBER = HISTORY.CUST_NUMBER
LEFT JOIN (
Select LOAN_DATE, CUST_NUMBER, ACCOUNT_NUMBER, STOCK_NUMBER, LOC_SALE
From LOAN
WHERE ACCOUNT_NUMBER != 'DD'
ORDER BY LOAN_DATE DESC
) LOAN ON LOAN.CUST_NUMBER = HISTORY.CUST_NUMBER
order by DATE desc
But I want only the top result from the loan table to be joined (Most recent by Loan_date). For some reason, it's getting three records (one for each loan on the customer I'm looking at). I'm sure I'm missing something simple?
If you're after joining the latest loan row per cust_number, then this ought to do the trick:
select *
from history
left join customer on customer.cust_number = history.cust_number
left join (select loan_date,
cust_number,
account_number,
stock_number,
loc_sale
from (select loan_date,
cust_number,
account_number,
stock_number,
loc_sale,
row_number() over (partition by cust_number
order by loan_date desc) rn
from loan
where account_number != 'DD')
where rn = 1) loan on loan.cust_number = history.cust_number
order by date desc;
If there are two rows with the same loan_date per cust_number and you want to retrieve both, then change the row_number() analytic function for rank().
If you only want to retreive one row, then you'd have to make sure you add additional columns into the order by, to make sure that the tied rows always display in the same order, otherwise you could find that sometimes you get different rows returned on subsequent runs of the query.

Missing right parenthesis error in query oracle

Tables needed -
Habits(conditionId, name)
Patient(patientId, name, gender, DoB, address, state,postcode, homePhone, businessPhone, maritalStatus, occupation, duration,unit, race, registrationDate , GPNo, NaturopathNo)
PatientMetabolic (functionNo, patientId, score)
The Question Is -
Question - Display the details of the patient (i.e. Name, Gender, Address, Postcode, DOB) who smokes and has the highest (most severe) total of metabolic functions.
(conditionid for smoke is H1 in Habit table)
(metabolic function are in patientbetabolic table functionNo)
(To find the highest most severe total of metabolic function we need to create a sum of score which tells who has the most metabolic functions)
My query -
SELECT *
FROM patient
where patientid IN (SELECT patientid,SUM(score) as totalscore
from PATIENTMETABOLIC
where patientid IN (SELECT patientid
from patienthabit
where conditionid = 'H1')
group by patientid
order by totalscore desc);
Error:
ORA-00907: missing right parenthesis
An alternative way to do this is by using joins.
select * from (select p.patientid,p.name,sum(pm.score) as total from patient p join patienthabit ph on p.patientid = ph.patientid
and ph.conditionid = 'H1' Left join patientmetabolic pm
on p.patientid = pm.patientid group by p.patientid,p.name order by 3 desc) where ROWNUM = 1;
Try this:
SELECT *
FROM PATIENT
WHERE PATIENTID = (SELECT PATIENTID
FROM (SELECT patientid, SUM(score)
from PATIENTMETABOLIC
where patientid IN (SELECT patientid
from patienthabit
where conditionid = 'H1')
group by patientid
order by SUM(score) desc)
WHERE ROWNUM = 1);
SQLFiddle here
Share and enjoy.
Since the first inner query returns patientid and totalscore, you can't use it as a list against IN operator.
The result of this query might be similar to that of your query:
SELECT p.*
FROM patient p JOIN
patienthabit ph ON p.patientid=ph.patientid
WHERE ph.conditionid='H1'

Resources