Oracle SQL, Select and compare dates against null values - sql

I need to select and compare the last advertised date in advert, to any null values in lease to get when an un-leased property and when it was last advertised. This is the code I have so far;
SELECT YR_LEASE.PROPERTYNUM,
MAX(YR_ADVERT.DATETO),
count(YR_LEASE.RENTERNUM)
FROM YR_LEASE
JOIN YR_ADVERT
ON YR_LEASE.PROPERTYNUM=YR_ADVERT.PROPERTYNUM
GROUP BY YR_LEASE.PROPERTYNUM
This returns a count this is far too high and I'm not sure what i'm doing wrong, here's my ERD to try and give this question some context;
http://www.gliffy.com/pubdoc/4239520/L.png

I think you need to first identify unleased properties. From there you can find the latest advert date. Assuming some properties have never been advertised you'll need to go via YR_PROPERTY and do a left join to include unadvertised properties.
SELECT NVL(TO_CHAR(MAX(YR_ADVERT.DATETO),'DD/MM/YYYY'),'NO LAST ADVERT DATE') LAST_ADVERT_DATE
,YR_PROPERTY.PROPERTYNUM
FROM YR_PROPERTY LEFT JOIN YR_ADVERT ON YR_PROPERTY.PROPERTYNUM = YR_ADVERT.PROPERTYNUM
WHERE NOT EXISTS (SELECT 1
FROM YR_LEASE
WHERE YR_LEASE.PROPERTYNUM = YR_PROPERTY.PROPERTYNUM
AND YR_LEASE.RENT_FINISH > SYSDATE)
GROUP BY YR_LEASE.PROPERTYNUM;

Related

How to filter where a condition is true at least once

I need to filter down to only service orders that have a "service" work group value in at least one of their tasks. However, I don't want to get rid of the rows that aren't work group = "Service" if at least one of the task rows has that value. The end result would leave out all data from service orders that didn't have at least one BI_WRKFLW_TASK_KEY that was equal to "SERVICE". I know how to do normal filters but getting it to this specificity is beyond my current experience.
I've experimented with normal filters but they leave out rows that are a part of the same Service Order but just don't have that work group.
SELECT W.BI_WRKFLW_KEY,
T.BI_WORK_EVENT_CD,
T.BI_TASK_CD,
T.BI_WORKGRP,
**M.BI_SO_NBR**,
M.BI_SO_TYPE_CD,
M.BI_CLOSE_DT,
M.BI_OPEN_DT,
M.BI_SO_STAT_CD,
R.BI_WRKFLW_TMPLT_NM,
T.BI_WRKFLW_TASK_SEQ_NBR,
T.BI_WORKGRP,
A.BI_WORK_EVENT_CD,
A.BI_EVENT_DT_TM,
A.SY_JOB_QUEUE_ID,
**A.BI_WORKGRP**,
A.SY_USER_ID,
**A.BI_WRKFLW_TASK_KEY**
FROM BI_WRKFLW W
LEFT JOIN BI_WRKFLW_TASKS T ON W.BI_WRKFLW_KEY = T.BI_WRKFLW_KEY
LEFT JOIN BI_SO_DET D ON W.BI_WRKFLW_KEY = D.BI_WRKFLW_KEY
LEFT JOIN BI_SO_MASTER M ON D.BI_SO_NBR = M.BI_SO_NBR
LEFT JOIN BI_WRKFLW_TMPLT_REF R ON W.BI_WRKFLW_TMPLT_ID = R.BI_WRKFLW_TMPLT_ID
LEFT JOIN BI_TASK_ACT A ON T.BI_WRKFLW_TASKS_KEY = A.BI_WRKFLW_TASKS_KEY
WHERE M.BI_OPEN_DT >= ADD_MONTHS(CURRENT_DATE, -'12')
--AND M.BI_SO_TYPE_CD IN ('IVC-NEW1')
--AND M.BI_SO_STAT_CD LIKE 'O'
ORDER BY M.BI_SO_NBR, T.BI_EVENT_DT_TM
Any Service order row where the Service order has at least one BI_WRKFLOW_TASK_CD = "Service" would be kept and all other service orders filtered out.
I tried to map this out, i may not have got it quite right,
I think you are asking for BI_SO_MASTER records that have >=1 BI_WRKFLW_TASKS that belong to a certain group.
Try using a CTE to get the detail rows with a correct task, then you can find the SO population... then you can ???not sure what the ultimate result set goal is?
;with matchingTasks as ( D.BI_SO_NBR, D.<id> , W.BI_WRKFLW_KEY , T.<key> , A.Key
from BI_WRKFLW W
LEFT JOIN BI_WRKFLW_TASKS T ON W.BI_WRKFLW_KEY = T.BI_WRKFLW_KEY
LEFT JOIN BI_SO_DET D ON W.BI_WRKFLW_KEY = D.BI_WRKFLW_KEY
LEFT JOIN BI_TASK_ACT A ON T.BI_WRKFLW_TASKS_KEY = A.BI_WRKFLW_TASKS_KEYW
Where
<good dates>
and <A.field is what I am looking for>
)
/*Here you have the SO population
as well as the ids that helped this SO qualify.
*/
, My_SO_Population as (select Distinct BI_SO_NBR from matchingTasks )
/*now you can go get what you need.
the challenge of finding SOs w/ >=1 matching task has been solved...
*/
select <necessary fields> from
My_SO_Population
join <whatever you need....this is where i am cloudy>
if i am missing the goal, let me know where...
You can just add this to your WHERE clause:
AND T.BI_WRKFLW_KEY IN (
SELECT BI_WRKFLW_KEY
FROM BI_WRKFLW_TASKS
WHERE BI_WRKFLOW_TASK_CD = 'Service')

SQL select database library

How to print all readers, where time between last two borrows is more than 2 months?
select
name, surname, max(k1.borrow_date)
from
k_person
join
k_reader using(person_id)
join
k_rent_books k1 using(reader_id)
join
k_rent_books k2 using(reader_id)
where
months_between(add_months((k1.borrow_date),-2),k2.borrow_date) > 2
group by
name, surname, person_id
order by
surname;
But i dont know how to say that compare two last dates.
Thanks for help.
Due to some restrictions with the USING clause (e.g. ORA-25154), I had to switch the join syntax, but here's one option. Basically the way to find the last and second last borrow dates for a reader is as follows:
Join to one copy of the K_RENT_BOOKS (K_RB1) table and finds the row with the latest BORROW_DATE for the current reader (from K_READER).
Next, it joins to a second copy of K_RENT_BOOKS (K_RB2), again for
the current reader and finds the latest BORROW_DATE that is not the
one found in the first copy (K_FB1).
Keep the resulting joined record if the last borrow date is two
months after the 2nd last borrow date.
--
select k_p.name, k_rb1.borrow_date, k_rb2.borrow_date
from k_person k_p
inner join
k_reader k_r
on k_p.person_id = k_r.person_id
inner join
k_rent_books k_rb1
on k_rb1.reader_id = k_r.reader_id
inner join
k_rent_books k_rb2
on k_rb2.reader_id = k_r.reader_id
where k_rb1.borrow_date = (select max(borrow_date)
from k_rent_books k_rb3
where k_rb3.reader_id = k_r.reader_id
)
and k_rb2.borrow_date = (select max(borrow_date)
from k_rent_books k_rb4
where k_rb4.reader_id = k_r.reader_id
and k_rb4.borrow_date <> k_rb1.borrow_date
)
and months_between(k_rb1.borrow_date, k_rb2.borrow_date) > 2
There are other ways of doing this that may be faster (e.g. using a with clause that generates the last and second last borrow dates for all readers) but hopefully this provides a starting point.

Include missing years in Group By query

I am fairly new in Access and SQL programming. I am trying to do the following:
Sum(SO_SalesOrderPaymentHistoryLineT.Amount) AS [Sum Of PaymentPerYear]
and group by year even when there is no amount in some of the years. I would like to have these years listed as well for a report with charts. I'm not certain if this is possible, but every bit of help is appreciated.
My code so far is as follows:
SELECT
Base_CustomerT.SalesRep,
SO_SalesOrderT.CustomerId,
Base_CustomerT.Customer,
SO_SalesOrderPaymentHistoryLineT.DatePaid,
Sum(SO_SalesOrderPaymentHistoryLineT.Amount) AS [Sum Of PaymentPerYear]
FROM
Base_CustomerT
INNER JOIN (
SO_SalesOrderPaymentHistoryLineT
INNER JOIN SO_SalesOrderT
ON SO_SalesOrderPaymentHistoryLineT.SalesOrderId = SO_SalesOrderT.SalesOrderId
) ON Base_CustomerT.CustomerId = SO_SalesOrderT.CustomerId
GROUP BY
Base_CustomerT.SalesRep,
SO_SalesOrderT.CustomerId,
Base_CustomerT.Customer,
SO_SalesOrderPaymentHistoryLineT.DatePaid,
SO_SalesOrderPaymentHistoryLineT.PaymentType,
Base_CustomerT.IsActive
HAVING
(((SO_SalesOrderPaymentHistoryLineT.PaymentType)=1)
AND ((Base_CustomerT.IsActive)=Yes))
ORDER BY
Base_CustomerT.SalesRep,
Base_CustomerT.Customer;
You need another table with all years listed -- you can create this on the fly or have one in the db... join from that. So if you had a table called alltheyears with a column called y that just listed the years then you could use code like this:
WITH minmax as
(
select min(year(SO_SalesOrderPaymentHistoryLineT.DatePaid) as minyear,
max(year(SO_SalesOrderPaymentHistoryLineT.DatePaid) as maxyear)
from SalesOrderPaymentHistoryLineT
), yearsused as
(
select y
from alltheyears, minmax
where alltheyears.y >= minyear and alltheyears.y <= maxyear
)
select *
from yearsused
join ( -- your query above goes here! -- ) T
ON year(T.SO_SalesOrderPaymentHistoryLineT.DatePaid) = yearsused.y
You need a data source that will provide the year numbers. You cannot manufacture them out of thin air. Supposing you had a table Interesting_year with a single column year, populated, say, with every distinct integer between 2000 and 2050, you could do something like this:
SELECT
base.SalesRep,
base.CustomerId,
base.Customer,
base.year,
Sum(NZ(data.Amount)) AS [Sum Of PaymentPerYear]
FROM
(SELECT * FROM Base_CustomerT INNER JOIN Year) AS base
LEFT JOIN
(SELECT * FROM
SO_SalesOrderT
INNER JOIN SO_SalesOrderPaymentHistoryLineT
ON (SO_SalesOrderPaymentHistoryLineT.SalesOrderId = SO_SalesOrderT.SalesOrderId)
) AS data
ON ((base.CustomerId = data.CustomerId)
AND (base.year = Year(data.DatePaid))),
WHERE
(data.PaymentType = 1)
AND (base.IsActive = Yes)
AND (base.year BETWEEN
(SELECT Min(year(DatePaid) FROM SO_SalesOrderPaymentHistoryLineT)
AND (SELECT Max(year(DatePaid) FROM SO_SalesOrderPaymentHistoryLineT))
GROUP BY
base.SalesRep,
base.CustomerId,
base.Customer,
base.year,
ORDER BY
base.SalesRep,
base.Customer;
Note the following:
The revised query first forms the Cartesian product of BaseCustomerT with Interesting_year in order to have base customer data associated with each year (this is sometimes called a CROSS JOIN, but it's the same thing as an INNER JOIN with no join predicate, which is what Access requires)
In order to have result rows for years with no payments, you must perform an outer join (in this case a LEFT JOIN). Where a (base customer, year) combination has no associated orders, the rest of the columns of the join result will be NULL.
I'm selecting the CustomerId from Base_CustomerT because you would sometimes get a NULL if you selected from SO_SalesOrderT as in the starting query
I'm using the Access Nz() function to convert NULL payment amounts to 0 (from rows corresponding to years with no payments)
I converted your HAVING clause to a WHERE clause. That's semantically equivalent in this particular case, and it will be more efficient because the WHERE filter is applied before groups are formed, and because it allows some columns to be omitted from the GROUP BY clause.
Following Hogan's example, I filter out data for years outside the overall range covered by your data. Alternatively, you could achieve the same effect without that filter condition and its subqueries by ensuring that table Intersting_year contains only the year numbers for which you want results.
Update: modified the query to a different, but logically equivalent "something like this" that I hope Access will like better. Aside from adding a bunch of parentheses, the main difference is making both the left and the right operand of the LEFT JOIN into a subquery. That's consistent with the consensus recommendation for resolving Access "ambiguous outer join" errors.
Thank you John for your help. I found a solution which works for me. It looks quiet different but I learned a lot out of it. If you are interested here is how it looks now.
SELECT DISTINCTROW
Base_Customer_RevenueYearQ.SalesRep,
Base_Customer_RevenueYearQ.CustomerId,
Base_Customer_RevenueYearQ.Customer,
Base_Customer_RevenueYearQ.RevenueYear,
CustomerPaymentPerYearQ.[Sum Of PaymentPerYear]
FROM
Base_Customer_RevenueYearQ
LEFT JOIN CustomerPaymentPerYearQ
ON (Base_Customer_RevenueYearQ.RevenueYear = CustomerPaymentPerYearQ.[RevenueYear])
AND (Base_Customer_RevenueYearQ.CustomerId = CustomerPaymentPerYearQ.CustomerId)
GROUP BY
Base_Customer_RevenueYearQ.SalesRep,
Base_Customer_RevenueYearQ.CustomerId,
Base_Customer_RevenueYearQ.Customer,
Base_Customer_RevenueYearQ.RevenueYear,
CustomerPaymentPerYearQ.[Sum Of PaymentPerYear]
;

How to view the Suggsetions sorted in the Database for the last three months?

I am a New ASP.NET Developer and I am trying to develop a simple suggestion box system. I have the following part of my database desing:
User Table: Username, Name, DivisionCode... etc
Division Table: SapCode, Division
SuggestionLog Table: ID, Title, Description, submittedDate, Username
(The first attribute is the primary key in each table and the attribute (submittedDate) is of DateTime data type)
Now, I need to develop a table that shows suggestions for the last three months. I already developed a query that shows the Employee Name, Username, Division, Suggestion Title, Suggestion Description. All what I want now is to show the Month. For example, to show the suggestions for the last three months, the Month column should show: Jan-2012, Dec-2011, Nov-2011 So how to do that?
My current SQL query:
SELECT dbo.SafetySuggestionsLog.Title, dbo.SafetySuggestionsLog.Description, dbo.SafetySuggestionsType.Type, dbo.SafetySuggestionsLog.Username,
dbo.employee.Name, dbo.Divisions.DivisionShortcut
FROM dbo.Divisions INNER JOIN
dbo.employee ON dbo.Divisions.SapCode = dbo.employee.DivisionCode INNER JOIN
dbo.SafetySuggestionsLog ON dbo.employee.Username = dbo.SafetySuggestionsLog.Username INNER JOIN
dbo.SafetySuggestionsType ON dbo.SafetySuggestionsLog.TypeID = dbo.SafetySuggestionsType.ID
The desired output is to display:
Employee Name, Username, Division, SuggestionTitle, SuggstionDescription, SuggestionType Month(submissionDate)
I reformatted you query so it would fit on the page without scrolling.
Hopefully this provides what you need. It uses DATENAME to get the month and year parts from the current date and DATEPART to do the "three months ago" calculation.
Note that DATEPART doesn't behave as you might expect - it counts the number of period-end boundaries (in this case months) - hence the condition is
...WHERE DATEDIFF(month,SafetySuggestionsLog.submittedDate,getdate()) < 3
because the last three months have two month-end boundaries between them.
I also added an ORDER BY clause.
SELECT dbo.SafetySuggestionsLog.Title,
dbo.SafetySuggestionsLog.Description,
dbo.SafetySuggestionsType.Type,
dbo.SafetySuggestionsLog.Username,
dbo.employee.Name,
dbo.Divisions.DivisionShortcut,
left(datename(month,SafetySuggestionsLog.submittedDate),3)
+ '-'
+ datename(year,SafetySuggestionsLog.submittedDate) AS SubmittedMonth
FROM dbo.Divisions
INNER JOIN dbo.employee
ON dbo.Divisions.SapCode = dbo.employee.DivisionCode
INNER JOIN dbo.SafetySuggestionsLog
ON dbo.employee.Username = dbo.SafetySuggestionsLog.Username
INNER JOIN dbo.SafetySuggestionsType
ON dbo.SafetySuggestionsLog.TypeID = dbo.SafetySuggestionsType.ID
WHERE DATEDIFF(month,SafetySuggestionsLog.submittedDate,getdate()) < 3
ORDER BY SafetySuggestionsLog.submittedDate DESC
It might also be worth noting that you don't have to fully qualify the name of all the columns in the query - it's valid SQL to alias the input tables like so:
...INNER JOIN dbo.SafetySuggestionsLog AS log
You can then refer to column names by alias in the query - e.g.
log.Username
instead of
dbo.SafetySuggestionsLog.Username
which makes it a bit easier to read.

SQL counter difficulties

select maintitle,
firstprodyear,
COUNT(DISTINCT episode.episodeid) as TOTALEPISODES
from series
LEFT OUTER JOIN episode ON series.seriesid = episode.seriesid
LEFT OUTER JOIN filmitem ON filmitem.filmid = episode.episodeid
where firstprodyear =(select MIN(firstprodyear) from series)
group by maintitle, firstprodyear;
2/3s of the query works. I do get the title of the serie and earliest year. But it seems like the episode counter isn't working properly. For some episode I do get 15, 34 and somewhere 0.
I would preciate for some guidance to make the episodecounter work as it should. Where have I missed?
Try:
select maintitle,
min(firstprodyear) firstprodyear,
COUNT(DISTINCT episode.episodeid) as TOTALEPISODES
from series
LEFT OUTER JOIN episode ON series.seriesid = episode.seriesid
/*LEFT OUTER JOIN filmitem ON filmitem.filmid = episode.episodeid */
group by maintitle;
Note: the link to filmitem appears to be unnecessary with the data selected.
Your query is returning the series that have firstprodyear equal to the earliest firstprodyear in the your database. You can think of the sub-select statement as returning a fixed number that is then used in the query.
For example, if the earliest series in your database is Days of Our Lives (firstprodyear = 1965), then you will get back the number of episodes in those series that also started in 1965.
You may also want to be more explicit about which table actually contains the firstprodyear field, though I'm assuming it's series.