Hi I am using the following calculation to annualize a price field.
(price * 365 / close _date - begin_date ) * 9.35/100
This calculation is working when leap year is involved
for example when price = 6000
begin date = 1-jan- 2016
close date = 31-dec-2017
so the annualized value is 280.50
But when I change begin dates & end date to 1-jan-2017 and 31-dec-2018, the value changes to 280.88
How do I get the values to 280.50 in both the cases?can any averaging be done so that the value comes perfectly when any date is involved ?
2016 is a Leap Year.. 366 days. While 2017 has 365 days
Your Denom slips from 730 days to 729 days
You may want to use months_between():
select price * 12 / months_between(close _date, begin_date ) * 9.35/100
You may have to adjust the boundaries by one day for it to work for your dates:
select price * 12 / months_between(close _date + 1, begin_date) * 9.35/100
Note: months_between() is a little tricky because it returns fractional months. However, if the difference is always to the first of the month, then you are safe.
Related
I have two columns; both have integer values. One Representing years, and the other representing months.
My goal is to perform calculations in days (integer), so I have to convert both to calendar days, to achieve that, taking in consideration that we have years with both 365 and 366 days.
Example in pseudo code:
Select Convert(years_int) to days, Convert(months int) to days
from table.
Real Example:
if --> Years = 1 and Months = 12
1) Convert both to days to compare them: Years = 365 days; Months = 365 days
After conversion : (Years = Months) Returns TRUE.
The problem is when we have years = 10 (for example), we must take in account the fact that at least two of them have 366 days. The same with Months - we have 30 and 31 days. So I need to compensate that fact to get the most accurate possible value in days.
Thanks in advance
From integers to timestamp can be done in PostgreSQL. I do not have impala, but hopefully below script will help you getting this done using impala:
with
year as (select 2022 as y union select 2023),
month as (select generate_series(1,12) as m),
day as(select generate_series(1,31) as d )
select y,m,d,dt from (
select
y,m,d,
to_date(ds,'YYYYMMDD')+(((d-1)::char(2))||' day')::interval dt
from ( select
*,
y::char(4)|| right('0'||m::char(2),2) || right('0'||0::char(2),2) as ds
from year,month,day
) x
) y
where extract(year from dt)=y and extract(month from dt)=m
order by dt
;
see: DBFIDDLE
Used functions in this query and, a way, to convert them to imapala (remember I do not use that tool/language/dialect)
function
impala alternative
to_date(a,b)
This will convert the string a to a date using the format b. Using impala you can use CAST(expression AS type FORMAT pattern)
y::char(4)
Cast y to a char(4), Using imala you can use: CAST(expression AS type)
right(a,b)
Use: right()
\\
Use: concat()
generate_series(a,b)
This generates a serie of numbers from a to (an inclusing) b. A SQL altervative is to write SELECT 1 as x union SELECT 2 union SELECT 3, which generates the same series as generate_series(1,3) in PostgreSQL
extract(year from a)
Get the year from the datetime field a, see YEAR()
One special case is this one to_date(ds,'YYYYMMDD')+(((d-1)::char(2))||' day')::interval
This will convert ds (with datatype CHAR(8)) to a date, and then add (using +) a number of days (like: '4 day')
Because I included all days until 31, this will fail in Februari, April, June, September, November because those months do not have 31 days. This is corrected by the WHERE clause in the end (where extract(year from dt)=y and extract(month from dt)=m)
I need to take the purchasedate from a table and calculate the total years plus the decimal equivalent of months left over from today's date and be able to present this calculated data in yyy.mm.
i.e 4.5 would be deciphered as 4 years and 6 months from purchasedate.
If you work out the difference in months, then divide by 12 to get years, then use the remainder to get months which you can convert into a fraction e.g.
declare #PurchaseDate date = '01 Jan 2015', #Now date = current_timestamp
select convert(varchar(3),datediff(month,#PurchaseDate,#Now)/12) + '.' + substring(convert(varchar(5),convert(decimal(9,2),(datediff(month,#PurchaseDate,#Now)%12)/12.0)),3,2)
Gives
5.33
It might need some tweaking for edge cases.
Note the divide by 12.0 to force a fractional result - otherwise you just get an integer.
Just multiply by 12 and add months:
select dateadd(month, floor(#years * 12), purchasedate)
I will try to be simple as possible to make my question crystal-clear. I have a table that's called 'fb_ads' (it's about different facebook compaigns for different stores in USA) on BigQuery, it contains the following columns:
STORE : name of store
CLICKS: number of clicks.
IMPRESSIONS: number of impressions of the ad
COST: the ad cost
DATE: AAAA-MM-DD
Frequency: number of visitors of a store
So, I'm trying to calculate the variance between two years 2017 and 2018.
Here is the variance I'm trying to calculate:
Variance_Of_Frequency = ((Frequency in 2018 at date X) - ((Frequency in 2017 at date X))/((Frequency in 2017 at date X)
The problem is, that I'll have to compare the same day of the week close to Date X;
For example, if I have a compaign run on a Monday 2017-08-13, I'll need to compare to another monday in 2018 close to 2018-08-13 (it might be a monday on 2018-08-15 for example).
This is a daily variance!
I tried to make a weekly variance calculating and I don't know if it's correct, here is how I did it:
I first started with aggregating my daily table to a weekly tables using the following query:
creating my weekly_table
SELECT
year_week,
STORE,
min(DATE ) as DATE ,
SUM(IMPRESSIONS ) AS FB_IMPRESSIONS ,
SUM(CLICKS ) AS FB_CLICKS ,
SUM(COST) AS FB_COST ,
SUM(Frequency) AS FREQUENCY,
FROM (
SELECT
*,
CONCAT(cast(ANNEE as string), LPAD(cast((extract(WEEK from date)) as string), 2, '0') ) AS year_week
FROM `fb_ads`)
GROUP BY
year_week,
STORE,
ORDER BY year_week
Then I tried to calculate the variance using this:
SELECT
base.*, (base.frequency-lw.frequency) / lw.frequency as VAR_FF
FROM
`weekly_table` base
JOIN (
SELECT
* EXCEPT (date),
DATE_ADD(DATE(TIMESTAMP(date)) , INTERVAL 1 Week)AS date
FROM
`weekly_table` ) lw
ON
base.date = lw.date
AND base.store= lw.store
Anyone has any idea how to do the daily thing or if my weekly queries are correct ?
Thanks!
For a given date, you want to know the date of the nearest Monday to the same date in the following year...
SET #dt = '2017-08-17';
SELECT CASE WHEN WEEKDAY(#dt + INTERVAL 1 YEAR) > 3
THEN ADDDATE(ADDDATE(#dt + INTERVAL 1 YEAR,INTERVAL 1 WEEK),INTERVAL - WEEKDAY(#dt + INTERVAL 1 YEAR) DAY)
ELSE ADDDATE(#dt + INTERVAL 1 YEAR,INTERVAL - WEEKDAY(#dt + INTERVAL 1 YEAR) DAY)
END x;
Obviously, I could remove all those + INTERVAL 1 YEAR bits by defining #dt that way to begin with.
I'm trying to put together a query in SQLLite for my app and wanted to ask for a little help please.
The query I want to write:
"I'm a user with UserID x. For my personal work week, which can start on any day D that I want (Sunday to Saturday, Monday to Sunday, etc), I'd like to see which work week was my worst, which was my best, in terms of sales.
To keep it simple "worst" and "best" simply means the highest and lowest sales summed for that week.
If everyone's work week started on Sunday, this is easy, but this is not the case. I have to overcome a SQL challenge of grouping all of the rows in the database table not just by week, but a custom week (users define which day starts and ends the week).
As an example, if my work week starts on Sunday, then this past week, the week of May 28th is a Sunday and is the beginning of my work week (and it ends this Saturday June 3rd). I would follow this pattern for all records in the table.
However, a different user could have their work week start on Monday, May 29th, and end on Friday, June 2nd.
So this means for user 1, I'd want to group his rows from the starting day of Sunday, and the ending day of Saturday (and then aggregate them all, and take the first and last records for sales).
For user 2, however, I'd want to group his records in the date ranges of Monday to Sunday
Here is where I'm at so far. I'm close I think.
(Note that I store the date as a unix timestamp in milliseconds, hence the division by 1000 and the unixepoch part). The +d part is actually an integer based on the start of the day, but I haven't figured out what that number should really be. Just when I think I get it, it fails for someone else's day.
SELECT 'Date', SUM(Amount) 'Amount'
FROM Sales WHERE UserID = x
GROUP BY CAST(( julianday((datetime(CreationDate / 1000, 'unixepoch', 'localtime')) + d) / 7 ) AS INT)
Does anyone think they could give me a hand? :)
Thanks so much!
EDIT
Thank you so much for your help!
For the +d question (what should the value 'd' be to offset)?
Here is what I found after testing, and this works as far as I can tell. I understand Sqllite uses 0 as Sunday, 1 as Monday, etc, and I understand we were grouping and dividing by 7 (for 7 days in the week), but any idea why these would be the right values for 'd' as the offset? It seems to be working now. I see the pattern goes 2,1,0,6,5,4,3 but kinda strange order to go in eh?
if (day == Sunday) //if your work week starts on Sunday, d=2
return 2
else if (day == Monday)
return 1
else if (day == Tuesday)
return 0
else if (day == Wednesday)
return 6
else if (day == Thursday)
return 5
else if (day == Friday)
return 4
else if (day == Saturday)
return 3
You are very close.
You are adding d to the datetime. I don't know whether this actually adds days. I could not find out what happens if you add an integer to a datetime in SQLite. To play it save, add the day to the julian day instead. You don't have to first get a datetime and from this the julian day by the way, you can do that in one step:
julianday(CreationDate / 1000, 'unixepoch', 'localtime') + d
This is the only real flaw I see in your query.
the Julian day is a fractional number such as 2457907.5. When you invoke a division with / on it, you get a fractional result. I see that you convert this result to INT, but I would suggest to convert to INT first and only then divide which would make this an integer division implicitly.
cast(julianday(CreationDate / 1000, 'unixepoch', 'localtime') + d as int) / 7
This is just for readability; I get a day number (2457907 rather than some decimal 2457907.5) and integer-divide by 7 (e.g. 2457907 / 7 = 351129).
The whole query:
SELECT
MIN(DATE(CreationDate / 1000, 'unixepoch', 'localtime')) AS from_date,
MAX(DATE(CreationDate / 1000, 'unixepoch', 'localtime')) AS till_date,
SUM(Amount) AS total
FROM Sales
WHERE UserID = x
GROUP BY CAST(JULIANDAY(CreationDate / 1000, 'unixepoch', 'localtime') + d as INT) / 7
ORDER BY SUM(Amount);
from_date and till_date don't always represent the full seven days though, but only the worked days (e.g. in a week from Sunday to Saturday, but worked only Monday, Wednesday and Friday, it would show the dates for Monday and Friday). It would take slightly more work to show the real week. (I better don't try this now, for it's so easy to be one day off, when not being able to try the query.)
EDIT: Here is my try on the start and end days of the weeks. When we invoke DATE on a floating point value, this value is considered a Julian day. (Maybe it would work with the integer, too, I can not be sure from the documentation.)
SELECT
DATE(CAST(CAST(JULIANDAY(CreationDate / 1000, 'unixepoch', 'localtime') + d as INT) / 7 as REAL)) AS from_date,
DATE(CAST(CAST(JULIANDAY(CreationDate / 1000, 'unixepoch', 'localtime') + d as INT) / 7 as REAL), '+6 day') AS till_date,
MIN(DATE(CreationDate / 1000, 'unixepoch', 'localtime')) AS first_working_day,
MAX(DATE(CreationDate / 1000, 'unixepoch', 'localtime')) AS last_working_day,
SUM(Amount) AS total
FROM Sales
WHERE UserID = x
GROUP BY CAST(JULIANDAY(CreationDate / 1000, 'unixepoch', 'localtime') + d as INT) / 7
ORDER BY SUM(Amount);
Please try to execute below query:
select
min(to_char(to_date(order_date,'mm/dd/yyyy'),'Day'))
keep(dense_rank first order by sum(sales) desc) best_day,
min(to_char(to_date(order_date,'mm/dd/yyyy'),'Day'))
keep(dense_rank last order by sum(sales) desc)worst_day
from orders
where userid=x
group by to_char(to_date(order_date,'mm/dd/yyyy'),'Day');
I need a query which can generate the total no of records generated in the previous month.
I started with this query:
select state, taxing_entity, count(taxing_entity) Total_TRXN_Count
from taxes where effect_date between '2016/07/01' and '2016/07/31'
group by state, taxing_entity, effect_date
But I need a query which can dynamically calculate the no of records for a previous month without hard coding the dates. I tried many queries like:
SELECT * FROM taxes
WHERE effect_date >= DATEADD(day,-30, getdate())
and effect_date <= getdate()
and
SELECT *
FROM taxes
WHERE effect_date >= DATEADD(effect_date, -1, GETDATE())
but I have had no success until now. Can someone please suggest a way to get the total records from a previous month using the Informix dialect of SQL?
Background
You can do it with Informix functions TODAY, MONTH(), YEAR() and MDY() — and a little jiggery-pokery.
The expression for the first day of this month is:
MDY(MONTH(TODAY), 1, YEAR(TODAY))
The expression for the first day of the prior month, therefore, is:
MDY(MONTH(TODAY), 1, YEAR(TODAY)) - 1 UNITS MONTH
The advantage of these expressions is that they are unambiguous and reliable; they don't depend on anything special (in the SQL you write) to find the last day of the month. You can find the last day of the month by subtracting 1 UNITS DAY from the first day of the following month. With modern versions of Informix, you can do date arithmetic that rounds to the end of month:
SELECT MDY(1,31,2016) + 1 UNITS MONTH FROM sysmaster:'informix'.sysdual;
2016-02-29
but older versions of Informix would generate an error instead. Even now, MDY(2,31,2016) generates an 'invalid day in month' error.
Answer
So for the problem in the question, the date range you want is:
SELECT *
FROM taxes
WHERE effect_date >= (MDY(MONTH(TODAY), 1, YEAR(TODAY)) - 1 UNITS MONTH)
AND effect_date < MDY(MONTH(TODAY), 1, YEAR(TODAY))
It is easier to use 'before the first day of this month' than to use 'on or before the last day of the previous month'. However you could write:
SELECT *
FROM taxes
WHERE effect_date >= (MDY(MONTH(TODAY), 1, YEAR(TODAY)) - 1 UNITS MONTH)
AND effect_date <= (MDY(MONTH(TODAY), 1, YEAR(TODAY)) - 1 UNITS DAY)
or, equivalently:
SELECT *
FROM taxes
WHERE effect_date BETWEEN (MDY(MONTH(TODAY), 1, YEAR(TODAY)) - 1 UNITS MONTH)
AND (MDY(MONTH(TODAY), 1, YEAR(TODAY)) - 1 UNITS DAY)
Using stored procedures
You could generalize the code by using any chosen date in place of TODAY, and you could write one or two simple stored procedures to do those calculations if you think they're too obscure as shown. Possible names would include first_day_this_month and first_day_prev_month, passing the reference date as an argument, defaulting to TODAY:
CREATE PROCEDURE first_day_this_month(refdate DATE DEFAULT TODAY)
RETURNING DATE AS first_day;
RETURN MDY(MONTH(refdate), 1, YEAR(refdate));
END PROCEDURE;
CREATE PROCEDURE first_day_prev_month(refdate DATE DEFAULT TODAY)
RETURNING DATE AS first_day;
RETURN MDY(MONTH(refdate), 1, YEAR(refdate)) - 1 UNITS MONTH;
END PROCEDURE;
Example use (assuming you have DBDATE='Y4MD-' or equivalent in your environment, so DATE strings look like ISO 8601 or DATETIME strings):
SELECT first_day_this_month() AS aug_1,
first_day_prev_month() AS jul_1,
first_day_this_month(DATE('2015-12-25')) as dec_1,
first_day_prev_month(DATE('2015-10-31')) AS sep_1
FROM sysmaster:'informix'.sysdual;
Output:
aug_1 jul_1 dec_1 sep_1
2016-08-01 2016-07-01 2015-12-01 2015-09-01
And hence:
SELECT *
FROM taxes
WHERE effect_date >= first_day_prev_month()
AND effect_date < first_day_this_month()
A few years old but I assume this is still relevant info:
effect_date >=
MDY(month(current - 1 units month), 1, year(current - 1 units month))
and effect_date <
MDY(month(current), 1, year(current))
http://www.dbforums.com/showthread.php?1627297-Informix-query-to-find-first-date-of-previous-month
Of course if you were given a random date and you need to cover it's month from the first to the end, the pattern is similar:
effect_date >=
MDY(month(<given_date>), 1, year(<given_date>))
and effect_date <
MDY(month(<given_date> + 1 units month), 1, year(<given_date> + 1 units month))
well I had this prepared because sql-server was tagged you can translate the it to informix maybe I will look it up too.
SELECT *
FROM
taxes
WHERE
effect_date >= DATEFROMPARTS(YEAR(DATEADD(MONTH,-1,GETDATE())),MONTH(DATEADD(MONTH,-1,GETDATE())),1)
AND effect_date < DATEFROMPARTS(YEAR(GETDATE()),MONTH(GETDATE()),1)
I think infomix might just be:
SELECT *
FROM
taxes
WHERE
effect_date >= MDY(MONTH(ADD_MONTHS(CURRENT,-1)),1,YEAR(ADD_MONTHS(CURRENT,-1)))
AND effect_date < MDY(MONTH(CURRENT),1,YEAR(CURRENT))
If you change from <= to < then you really don't have to search by end of last month but rather than beginning of this month. Which if you effect_date has time included would actually be what you want because of the midnight issue.
This should work in MSSQL:
SELECT COUNT(*)
FROM taxes
WHERE effect_date >= DATEADD(MONTH, -1, effect_date)
I'm not too familiar with Informix, but the syntax shouldn't be too far off.