In Oracle database, I have this data in a table:
person
category
month
profit
John
A
Jun-1-2022
100
Mary
A
May-1-2022
200
John
B
Jun-1-2021
230
John
A
Jun-1-2021
430
I need to add a new column into this table, called 'Same_month_last_year', which contains the data of same month last year. For example, John's data would be 430 for row 1.
I know a function in Oracle called ADD_MONTHS. but I'm new to programming (a finance student) and cannot seem to figure out how to use ADD_MONTHS to create this new column. Could you please advise?
Use the SUM analytic function with a range window:
SELECT t.*,
SUM(profit) OVER (
PARTITION BY person
ORDER BY month
RANGE BETWEEN INTERVAL '12' MONTH PRECEDING
AND INTERVAL '12' MONTH PRECEDING
) AS last_year_profit
FROM table_name t
Which, for the sample data:
CREATE TABLE table_name (person, month, profit) AS
SELECT 'John', DATE '2022-06-01', 100 FROM DUAL UNION ALL
SELECT 'Mary', DATE '2022-05-01', 200 FROM DUAL UNION ALL
SELECT 'John', DATE '2021-06-01', 430 FROM DUAL;
Outputs:
PERSON
MONTH
PROFIT
LAST_YEAR_PROFIT
John
01-JUN-21
430
null
John
01-JUN-22
100
430
Mary
01-MAY-22
200
null
db<>fiddle here
You can not have dynamically calculated columns in a table. You need to create a view and assign IDs to each person. Then in the view's script, the calculation of the new column will work like this:
(select profit
from table t
where
month = add_months(t.month, -6)
and id = t.id) as same_month_last_year
Related
This question already has answers here:
Split rows in a table based on date fields SQL
(2 answers)
Big query- get last date of every month in a year
(2 answers)
Closed last month.
I want to create a new table from the existing data:
date
store
cost
2022-01-10
a
3000
2022-01-10
b
2500
And finally the targettable should look like:
date
store
cost
2022-01-10
a
96,77
2022-02-10
a
96,77
2022-03-10
a
96,77
2022-04-10
a
96,77
.... to last day of the month
a
96,77
2022-01-10
b
80,65
2022-02-10
b
80,65
2022-03-10
b
80,65
...
b
80,65
The query should insert into another bigquery table with new rows for each day of the month (like the 2nd table). The cost should devided by the day of the month to split the cost.
I tried a few querys, but cant find a solution! :(
Thank you!
Using this answer here I have calculated the number of days for each month, and then I have divided the monthly costs by it to get daily_avg_monthly_cost . Then I Joined with an array generated table containing all the dates in each month of the year and got the final output.
Please note: a limitation of this solution is that the value "2022" is hardcoded.
WITH source_data as (
SELECT date('2022-01-10') as _month, 'a' as store, 3000 as cost
UNION ALL
SELECT date('2022-01-10') as _month, 'b' as store, 2500 as cost
UNION ALL
SELECT date('2022-02-10') as _month, 'a' as store, 4000 as cost
UNION ALL
SELECT date('2022-02-10') as _month, 'b' as store, 3500 as cost
), cost_table AS (
SELECT
FORMAT_DATE('%Y-%m', _month) AS month,
store,
cost/EXTRACT(DAY FROM (DATE_SUB(DATE_TRUNC(DATE_ADD(_month, INTERVAL 1 MONTH), MONTH),INTERVAL 1 DAY))) daily_avg_monthly_cost
FROM source_data
),days AS (
SELECT
d,
FORMAT_DATE('%Y-%m',d) AS month
FROM (
SELECT
*
FROM
UNNEST(GENERATE_DATE_ARRAY('2022-01-01', '2022-12-31', INTERVAL 1 DAY)) AS d
)
)
SELECT
days.d as date,
cost_table.store,
cost_table.daily_avg_monthly_cost
FROM cost_table
JOIN days ON CAST(cost_table.month AS STRING) = days.month
I have table called ACCOUNTS which has every day data. I want to compare if there is change in email address between yesterday and today employee wise.
select EMAIL,EMPLOYEE from ACCOUNTS where day='30-DEC-20'; --today's data
select EMAIL,EMPLOYEE from ACCOUNTS where day='29-DEC-20' -- yesterday'data
I have to deal with bulk data sets here and have no clue whatsoever.
Assuming you have one row per day per employee, one method is aggregation:
select employee,
max(case when date = date '2020-12-29' then email end) as email_yesterday,
max(case when date = date '2020-12-30' then email end) as email_today
from accounts
where date in (date '2020-12-29', date '2020-12-30')
group by employee
having min(email) <> max(email);
If you wanted to generalize this to any day:
select employee,
max(case when date = trunc(sysdate) - interval '1' day then email end) as email_yesterday,
max(case when date = trunc(sysdate) then email end) as email_today
from accounts
where date >= trunc(sysdate) - interval '1' day
group by employee
having min(email) <> max(email);
This option compares e-mail addresses between "this" and "previous" days and returns a row if they differ.
Sample data is in a CTE (lines #1 - 9) - you don't type that as you have it in your table.
Query you might be interested in begins at line #11.
SQL> with accounts (email, employee, day) as
2 -- sample data; you already have that in your table
3 (select 'scott#x.com', 'Scott', date '2020-12-01' from dual union all
4 select 'scott#x.com', 'Scott', date '2020-12-02' from dual union all
5 select 'scott#y.com', 'Scott', date '2020-12-04' from dual union all
6 --
7 select 'adams#x.com', 'Adams', date '2020-12-11' from dual union all
8 select 'adams#y.com', 'Adams', date '2020-12-12' from dual
9 ),
10 -- query begins here
11 data as
12 -- fetch "today's" and "previous day's" e-mail addresses
13 (select email todays_email,
14 lag(email) over (partition by employee order by day desc) previous_email,
15 employee,
16 day
17 from accounts
18 )
19 --
20 -- display data where today's and previous day's e-mail adresses differ
21 select employee, day, todays_email, previous_email
22 from data
23 where todays_email <> previous_email
24 order by employee, day;
EMPLO DAY TODAYS_EMAI PREVIOUS_EM
----- ---------- ----------- -----------
Adams 11.12.2020 adams#x.com adams#y.com
Scott 02.12.2020 scott#x.com scott#y.com
SQL>
I have a SQL query that pulls in three columns as below
employee_id start_date end_date hours
123 09-01-2019 09-02-2019 8
123 09-28-2019 10-01-2019 32
I want to rewrite the query so instead of going granular, i just want to know the sum(hrs) an employee has on a year month level like below:
employee_id Year_Month hours
123 201909 32
123 201910 8
The employee has 4 days in September so 4*8=32 and one day in october so 8 hours for the month of October. My issue is when there are start and end dates that cross between adjacent months. I'm not sure how to write a query to get my desired output and I'd really appreciate any help on this
It might be simpler to use a recursive query to generate series of days in each month, then aggregate by month and count:
with
data as (< your existing query here >),
cte (employee_id, dt, max_dt) as (
select employee_id, start_date, end_date from data
union all
select employee_id, dt + 1, max_dt from cte where dt + 1 < max_dt
)
select employee_id, to_char(dt, 'yyyymm') year_months, count(*) * 8 hours
from mytable
group by employee_id, to_char(dt, 'yyyymm')
This assumes 8 hours per day, as explained in your question.
I know basic SQL but am trying to come up with a query that is beyond me.
AMOUNT_HISTORY table is something like this
I can add an input parameter for effective date, say effectiveDate.
The highest Amount1 for RefNo 1 in last 3 years is 12,000 which is the <= the Amount2 at the effectiveDate - thats fine.
The highest Amount1 for RefNo 2 in last 3 years is 22,000, which is > than the Amount2 at the effective Date - I need to select the RefNo in that case.
Note the dates go back further, so will need the last 3 dates criteria. There will only be dates on the anniversary of the effectiveDate. Normally I would add the query I have developed thus far, but I didn't get further than a simple Select From Where so not much progress made really. Any help appreciated.
First, you need to select only the rows where the date is between "effective date minus two years" and "effective date". This is done best in the WHERE clause, with a between condition, using add_months() to subtract two years from the effective date. Note - I used dt as column name (date is reserved in Oracle and shouldn't be used as an identifier); and I pass the effective date as a bind variable.
Then, from the remaining rows, group by refno, and compare max(amount1) to the amount2 on the effective date. This is a comparison at the group level, not at the individual row level, so it goes in the HAVING clause, not in the WHERE clause.
Lastly, amount2 on the effective date is at the row level, not at the group level; so we need a little trick. I use a "conditional maximum" - the max() function applied to an expression that is amount2 when the date is the effective date, but null on the other dates. A case expression is perfect for that.
with
test_data ( refno, dt, amount1, amount2 ) as (
select 1, date '2017-01-01', 12000, 12000 from dual union all
select 1, date '2016-01-01', 11000, null from dual union all
select 1, date '2015-01-01', 10500, null from dual union all
select 2, date '2017-01-01', 20000, 10000 from dual union all
select 2, date '2016-01-01', 21000, null from dual union all
select 2, date '2015-01-01', 22000, null from dual
)
-- End of test data (not part of the solution). SQL query begins below this line.
select refno, max(case when dt = :effective_date then amount2 end) as amount2
from test_data
where dt between add_months(:effective_date, -24) and :efffective_date
group by refno
having max(amount1) > max(case when dt = :effective_date then amount2 end)
;
REFNO AMOUNT2
----- -------
2 10000
I can't provide you the direct answer but definitely will help you getting the correct answer.
So, basically you need to learn -
GROUP BY along with aggregate function - http://www.w3schools.com/sql/sql_groupby.asp.
Group by clause group the results by ref no and then use aggregate function MAX
DATE_ADD - http://www.w3schools.com/sql/func_date_add.asp
sql query for getting data for last 3 months
Use DATE_ADD to add the criteria for interval of last 3 years
I am not very good with Queries and Database.
I have the the following data table
Date ID Value
20160601 1 300
20160607 1 301
20160601 2 600
20160607 2 601
20160501 1 250
20160507 1 240
20160501 2 800
20160507 2 801
my requirement is to select the last date of a given month for each ID and show the value.
for example, If I choose month 5 the result would be:
Date ID Value
20160507 1 240
20160507 2 801
and so on based on the month the user will enter.
I know it may look simple but I am really stuck and I would appreciate some help. Thanks.
Assuming date is an actual date column (as it should be), you can use extract to compare the month value, and then the row_number() over ... analytic function to get the latest row per id value:
select date, id, value
from (select date, id, value,
row_number() over (partition by id order by date desc) as rn
from tbl
where extract(month from date) = 5)
where rn = 1
Of course, I assume that your actual date column is called something else, as date is a reserved word.
Find the maximum date then select all rows with that date.
select *
from table
where date = (select max(date) from table where date like '201605%')