I am working in oracle and new to coding and new to this site so I apologize in advance for the newbie question:
I have a script I am trying to run that will return the sum of next months' sales orders and compare that figure against our budgeted sales forecast. It was working great last month (November) when I set it up but now that it's December, I believe it's having problems figuring out that next month is a new year.
Essentially I just want to sum of our sales order records from the next month and compare that number against our forecast number.
Here is what I have so far (I'm sure I am making lots of grammatical mistakes so please be patient!)
select
"Backlog", "Forecast Amount" , round("Backlog"/"Forecast Amount",4) as "Backlog Percent"
from
(select round(sum(NVL(unit_price,0) *NVL( ship_quan,0)),2) as "Backlog"
from v_backlog_releases
where
(TO_CHAR(V_BACKLOG_RELEASES.PROMISE_DATE,'MM\YYYY') = TO_CHAR(sysdate,'MM\YYYY')+1)),
(select budamount as "Forecast Amount"
from
glbudget,
glperiods
where
glbudget.glperiods_id=glperiods.id and
TO_CHAR(GLPERIODS.START_DATE,'MM') = TO_CHAR(sysdate,'MM')+1)
The system won't let me post images of the output since I am too new. Essentially I should get something that looks like this:
Backlog | Forecast Amount | Backlog Percent
100,000 | 200,000 | .50
The backlog column is just a sum of ship quantities * price for all orders due to ship the following month.
Your issue is that for December TO_CHAR(sysdate, 'MM') + 1 is returning 13 instead of 1 of the next year. Obviously there is no month 13...
Try using ADD_MONTHS(sysdate, 1) instead and handle that result as appropriate. Best advice is to handle dates as dates instead of chars whenever possible.
Update based on comments:
Try using:
EXTRACT(MONTH FROM GLPERIODS.START_DATE) = EXTRACT(MONTH FROM ADD_MONTHS(sysdate, 1))
Documentation: https://docs.oracle.com/cd/B14117_01/server.101/b10759/functions045.htm
Related
Please let me know if the following is off topic, or not clear, or too specific, or too complex to understand. I think the following is a challenge to describe, understand and solve.
CIF=cost, insurance, frieght (basically it is the import value)
The simiplified version of input table (Import) looks like this:
enter image description here
So from January to June the value 1 is assigned to SixMonthPeriod column, and the rest of the months are given the value 2.
I then want to calculate unit price for a six period, thus I use
select SixMonthPeriod, ProductDescrip, Sum(weight), Sum (CIF), (Sum (CIF))/(Sum(weight)) as UnitPrice
from Import
group by SixMonthPeriod, ProductDescrip;
This is fine, but I then want to calculate inflation for each product (over a six month period )where I need to use lag (an oracle analytical function). The six month period has to be fixed. Thus, if the previous period for a particular product is missing, then the unit price should be zero. I want to re-begin/begin the calculation of inflation for each product. The unit price and inflation equations looks like the following, respectively:
unit price = (Sum(weight) over a six month period)/(Sum (CIF) over a six month period)
inflation = (Current Unit price - previous unit price)/(previous unit price)
I use the following SQL to calculate inflation for a six month period for each product, where the calculation begins again for each product:
select Yr, SixMthPeriod, Product, UnitPrice, LagUnitPrice, ((UnitPrice -LagUnitPrice)/LagUnitPrice) as inflation
from (select Year as Yr, SixMonthPeriod as SixMthPeriod,
ProductDescrip as product, (Sum (CIF))/(Sum(weight)) as UnitPrice,
lag((Sum (CIF))/(Sum(weight)))
over (partition by ProductDescrip order by YEAR, SixMonthPeriod) as LagUnitPrice
From Import
group by Year, SixMonthPeriod, ProductDescrip)
The problem is the inflation period is not fixed.
For example, for the result, I get the following:
enter image description here
The first two rows are fine and there should be null values because they are my first line, thus there is no LagUnitPrice and inflation.
The third line has a problem where it has taken 0.34 as the LagUnitPrice but actually it is zero (for the period 2016 where SixMthPeriod=1 for the product barley). the oracle analytical functions does not take into account missing rows (e.g. for the period 2016 where SixMthPeriod=1 for the product barley).
how do I fix this problem (if you understand me)?
I have 96 rows, thus I can export the file to excel, and use excel's formulas to fix these exceptions.
You can autogenerate missing periods with nullable price, attach them to your data and do the rest as you did:
select product, year, smp, price, prev_price, (price - prev_price) / prev_price inflation
from (
select product, year, smp, price,
lag(price) over (partition by product order by year, smp) prev_price
from (
select year, ProductDescrip product, SixMonthPeriod smp, sum(CIF)/sum(weight) price
from Import
group by year, SixMonthPeriod, ProductDescrip) a
full join (
select distinct year, productdescrip product, column_value smp
from import cross join table(sys.odcinumberlist(1, 2))) b
using (product, year, smp))
order by product, year, smp
SQLFiddle demo
Subquery b is responsible for generating all periods, you can run it separately to see what it produces.
It's an exercise that is not solved in the book in which I am studying.
The goal is to find the seller who has had the highest number of sales per month,
during all the months for which there is registered information. The problem is that I do not know how to divide tuples into periods of one month.
First table is:
Table Sellers
Id_seller
Name_Product
And the other one is:
Table Product
Name_Product
View_datetime
Budget
What did i do?
I made this query:
SELECT id_seller FROM(SELECT id_seller, COUNT(id_seller)
FROM SELLERS INNER JOIN PRODUCT
ON SELLERS.name_product = PRODUCT.name_product
GROUP BY id_seller HAVING COUNT(id_seller)>= 1
ORDER BY 2 DESC)
WHERE ROWNUM = 1;
The query returns me the seller that most sales has done, but not "per month since there are records" as the statement asks. Any ideas? I'm so lost...
The idea is to compare the total sales of each salesman in this month (sysdate), with those of a month ago, two months ago ... so long as there are older records. And get the maximum from each seller. And then you print the seller with more sales from the previous list. If a seller sells 400 products this month(April, the sysdate), but another seller sold in October last year 500, the result would be the second seller . That's what I do not know how to do.
Thanks ^^
You could try this query
select MonthName, id_seller, max(TotalSales) from (
select to_char(sysdate, 'Month') MonthName, sellers.id_seller, count(sellers.id_seller) TotalSales
from sellers inner join product
on sellers.name_product = product.name_product
group by to_char(view_datetime, 'Month'), sellers.id_seller
) tab
group by MonthName, id_seller
There are a few points to make...
The tables are weird. I assume your table sellers would better be called sales, right?
In this example, having count... >= 1 is a no-op. Count could only be 0 if there were no rows at all, in which case there would be no row in the group- by output. You can just leave this count away, here.
To get the sales per month, just add the month to the group by. I.e. group by id_seller, To_date(view_datetime,'YYYYMM').
i'm trying to show some averages over the past 12 months but there is no data for June/July so i want the titles for the months to display but just 0's in the 3 columns
currently it's only showing August - May which is 10 rows so it's throwing off formulas and charts etc.
select to_char(Months.Period,'YYYY/MM') As Period, coalesce(avg(ec.hours_reset),0) as AvgOfHOURSReset, coalesce(AVG(ec.cycles_reset),0) as AvgofCycles_Reset, Coalesce(AVG(ec.days_reset),0) as AvgofDAYS_Reset
from (select distinct reset_date as Period from engineering_compliance
where reset_date between '01/JUN/15' and '31/MAY/16') Months
left outer join engineering_compliance ec on ec.reset_date = months.Period
WHERE EC.EO = 'AT CHECK'
group by to_char(Months.Period,'YYYY/MM')
order by to_char(Months.Period,'YYYY/MM')
;
(select distinct to_char(reset_date,'YYYY/MM') as Period from engineering_compliance
where reset_date between '01/JUN/15' and '31/MAY/16') Months;
That query is pretty good, it's not far from working.
You would need to replace the Months table part. You want exactly one row per month, regardless of whether there's any data in the ec table.
You could maybe synthesize some data without going to any actual table in your own schema.
For example:
SELECT
extract(month from add_months(sysdate,level-1)) Row_Month,
extract(year from add_months(sysdate,level-1)) Row_Year,
to_char(add_months(sysdate,level-1),'YYYY/MM') Formatted_Date,
trunc(add_months(sysdate,level-1),'mon') Join_Date
FROM dual
CONNECT BY level <= 12;
gives:
ROW_MONTH,ROW_YEAR,FORMATTED_DATE,JOIN_DATE
6,2016,'2016/06',1/06/2016
7,2016,'2016/07',1/07/2016
8,2016,'2016/08',1/08/2016
9,2016,'2016/09',1/09/2016
10,2016,'2016/10',1/10/2016
11,2016,'2016/11',1/11/2016
12,2016,'2016/12',1/12/2016
1,2017,'2017/01',1/01/2017
2,2017,'2017/02',1/02/2017
3,2017,'2017/03',1/03/2017
4,2017,'2017/04',1/04/2017
5,2017,'2017/05',1/05/2017
Option 1: Write that subselect inline into your query, replacing sysdate with the start month and the figure 12 on the last line can be altered for the number of months you want in the series.
Option 2 (can be reused more conveniently in a variety of situations and queries): Write a view with a long series of months (for example, Jan 1970 to Dec 2199) using my SQL above. You can then join to that view on join_date with whatever start and end months you want. It will give you one row per month and you can pick up the formatted date from its column.
I was trying to generate a monthly report but I have no idea how to break it down in weeks. e.g when i generate January my output report should be divided in 4 week - First week/Second week/Third week/Fourth week OF JANUARY - is this even possible? Should it be done before saving to database or SQL will do? I have a datetime field called RecordDate
I am using SQL Server 2005,VS 2010 and CR for VS2010.
Given your datetime field RecordDate, the following SQL will give the week of the month (starting at 1)
select (((datepart(d, RecordDate)-1) / 7)+1)
if you group by that, you should be able to produce a breakdown by the weeks in a month.
Of course, doing it this way some of the 'weeks' will not be 7 days long. It may be that you really want to group by the week of the year, ie.
select datepart(wk, RecordDate)
In each case you will need to produce labels. If you're going to do this in SQL then in the first case to get a label like 'week 1 of month 7' it'll be something like
select 'week ' + cast( (((datepart(d, RecordDate)-1) / 7)+1) as char(1))
+ ' of month ' + cast(datepart(month, RecordDate) as varchar(2))
from Table
group by (((datepart(d, RecordDate)-1) / 7)+1), datepart(month, RecordDate)
You'd have to go round the houses a bit to get a label like 'week 1 starting in month 7' for the second case (I will leave this as an exercise for the reader)
I have a table A which contains a Date type attribute. I want to write a query to select the date in another table B with value one month after the value in A.Any one know how to do it in oracle?
uhm... This was the first hit on google:
http://psoug.org/reference/date_func.html
It seems you're looking for the "add_months" function.
You need to use the ADD_MONTHS function in Oracle.
http://www.techonthenet.com/oracle/functions/add_months.php
Additional info: If you want to use this function with today's date you can use ADD_MONTHS(SYSDATE, 1) to get one month from now.
The question is to select a date_field from table b where date_field of table b is one month ahead of a date_field in table a.
An additional requirement must be taken into consideration which is currently unspecified in the question. Are we interested in whole months (days of month not taken into consideration) or do we want to include the days which might disqualify dates that are one month ahead but only by a couple of days (example: a=2011-04-30 and b=2011-05-01, b is 1 month ahead but only by 1 day).
In the first case, we must truncate both dates to their year and month values:
SELECT TRUNC( TO_DATE('2011-04-22','yyyy-mm-dd'), 'mm') as trunc_date
FROM dual;
gives:
trunc_date
----------
2011-04-01
In the second case we don't have to modify the dates.
At least two approaches can be used to solve the initial problem:
First one revolves around adding one month to the date_field in table a and finding a row in table b with a matching date.
SELECT b.date_field
FROM tab_a as a
,tab_b as b
WHERE ADD_MONTHS( TRUNC( a.date_field, 'mm' ), 1) = TRUNC( b.date_field, 'mm' )
;
Note the truncated dates. Leaving this out will require a perfect day to day match between dates.
The second approaches is based on calculating the difference in months between two dates and picking a calculation that gives a 1 month difference.
SELECT b.date_field
FROM tab_a as a
,tab_b as b
WHERE months_between( TRUNC( b.date_field, 'mm') , TRUNC(a.date_field, 'mm') ) = 1
The order of the fields in months_between is important here. In the provided example:
for b.date_field one month ahead of a.date_field the value is 1
for b.date_field one month before a.date_field the value is -1 (negative one)
Reversing the order will also reverse the results.
Hope this answers your question.