Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have a table as below.
DATE_WORKED COUNTRY
1-Nov-13 United Kingdom
4-Nov-13 United Kingdom
5-Nov-13 India
6-Nov-13 India
7-Nov-13 India
8-Nov-13 United Kingdom
11-Nov-13 United Kingdom
12-Nov-13 India
13-Nov-13 India
14-Nov-13 India
15-Nov-13 United Kingdom
18-Nov-13 United Kingdom
19-Nov-13 India
20-Nov-13 India
21-Nov-13 India
22-Nov-13 United Kingdom
25-Nov-13 United Kingdom
26-Nov-13 India
27-Nov-13 India
28-Nov-13 India
29-Nov-13 United Kingdom
I am looking to find the start_date and end date for each stay in a country.
COUNTRY START_DATE END_Date
United Kingdom 1-Nov-13 4-Nov-13
India 5-Nov-13 7-Nov-13
United Kingdom 8-Nov-13 11-Nov-13
India 12-Nov-13 14-Nov-13
United Kingdom 15-Nov-13 18-Nov-13
India 19-Nov-13 21-Nov-13
United Kingdom 22-Nov-13 25-Nov-13
India 26-Nov-13 28-Nov-13
United Kingdom 29-Nov-13
Please help me with an SQL query to achieve this.
Thanks in advance.
Using Tabibitosan:
SQL> create table mytable (date_worked,country)
2 as
3 select to_date('1-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
4 select to_date('4-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
5 select to_date('5-Nov-13','dd-Mon-yy'), 'India' from dual union all
6 select to_date('6-Nov-13','dd-Mon-yy'), 'India' from dual union all
7 select to_date('7-Nov-13','dd-Mon-yy'), 'India' from dual union all
8 select to_date('8-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
9 select to_date('11-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
10 select to_date('12-Nov-13','dd-Mon-yy'), 'India' from dual union all
11 select to_date('13-Nov-13','dd-Mon-yy'), 'India' from dual union all
12 select to_date('14-Nov-13','dd-Mon-yy'), 'India' from dual union all
13 select to_date('15-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
14 select to_date('18-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
15 select to_date('19-Nov-13','dd-Mon-yy'), 'India' from dual union all
16 select to_date('20-Nov-13','dd-Mon-yy'), 'India' from dual union all
17 select to_date('21-Nov-13','dd-Mon-yy'), 'India' from dual union all
18 select to_date('22-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
19 select to_date('25-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
20 select to_date('26-Nov-13','dd-Mon-yy'), 'India' from dual union all
21 select to_date('27-Nov-13','dd-Mon-yy'), 'India' from dual union all
22 select to_date('28-Nov-13','dd-Mon-yy'), 'India' from dual union all
23 select to_date('29-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual
24 /
Table created.
SQL> with tabibitosan as
2 ( select row_number() over (order by date_worked)
3 - row_number() over (partition by country order by date_worked) grp
4 , date_worked
5 , country
6 from mytable
7 )
8 select country
9 , min(date_worked) start_date
10 , max(date_worked) end_date
11 from tabibitosan
12 group by country
13 , grp
14 order by start_date
15 /
COUNTRY START_DATE END_DATE
-------------- ------------------- -------------------
United Kingdom 01-11-2013 00:00:00 04-11-2013 00:00:00
India 05-11-2013 00:00:00 07-11-2013 00:00:00
United Kingdom 08-11-2013 00:00:00 11-11-2013 00:00:00
India 12-11-2013 00:00:00 14-11-2013 00:00:00
United Kingdom 15-11-2013 00:00:00 18-11-2013 00:00:00
India 19-11-2013 00:00:00 21-11-2013 00:00:00
United Kingdom 22-11-2013 00:00:00 25-11-2013 00:00:00
India 26-11-2013 00:00:00 28-11-2013 00:00:00
United Kingdom 29-11-2013 00:00:00 29-11-2013 00:00:00
9 rows selected.
Somewhat more complicated than #RobVanWijk's answer:
with v_data as (
select to_date('2013-11-01', 'YYYY-MM-DD') as date_worked, 'UK' as country from dual union all
select to_date('2013-11-04', 'YYYY-MM-DD') as date_worked, 'UK' as country from dual union all
select to_date('2013-11-05', 'YYYY-MM-DD') as date_worked, 'India' as country from dual union all
select to_date('2013-11-06', 'YYYY-MM-DD') as date_worked, 'India' as country from dual union all
select to_date('2013-11-07', 'YYYY-MM-DD') as date_worked, 'India' as country from dual union all
select to_date('2013-11-08', 'YYYY-MM-DD') as date_worked, 'UK' as country from dual union all
select to_date('2013-11-11', 'YYYY-MM-DD') as date_worked, 'UK' as country from dual union all
select to_date('2013-11-12', 'YYYY-MM-DD') as date_worked, 'India' as country from dual
)
select country, start_day, end_day from (
select
v3.*,
row_number() over (partition by start_day, end_day order by date_worked) as rn
from (
select
v2.*,
max(case when is_first_day = 1 then date_worked else null end) over (Partition by null order by date_worked) as start_day,
min(case when is_last_day = 1 then date_worked else null end) over (Partition by null order by date_worked desc) as end_day
from (
select
v1.*,
(case when country <> nvl(country_next_day, 'n/a') then 1 else 0 end) is_last_day,
(case when country <> nvl(country_prev_day, 'n/a') then 1 else 0 end) is_first_day
from (
select
date_worked,
country,
lead(country) over (order by date_worked) as country_next_day,
lag(country) over (order by date_worked) as country_prev_day
from v_data
) v1
) v2
order by date_worked
) v3
) v4 where rn=1
Explanation:
for each workday, get the successor and the predecessor using the lag() and lead() analytic functions (v1)
for each workday, decide whether it is the start or end of a group by comparing its country to the previous and next countries (v2)
for each group, compute the start and end day (v3)
for each workday, compute its ordering inside its group (v4)
return only workdays with ordering 1
Try this query:
select country,min(date_worked) as start_date,max(date_worked) as end_date
from (select country,date_worked,
Row_Number() over(order by date_worked)
-Row_Number() over(partition by country order by date_worked) as disTance
from YourTable)
group by disTance,country order by min(date_worked);
Related
Consider I have a table like this
PASSENGER CITY DATE
43 NEW YORK 1-Jan-21
44 CHICAGO 4-Jan-21
43 NEW YORK 2-Jan-21
43 NEW YORK 3-Jan-21
44 ROME 5-Jan-21
43 LONDON 4-Jan-21
44 CHICAGO 6-Jan-21
44 CHICAGO 7-Jan-21
How would I group Passenger and City column in sequence to get a result like below?
PASSENGER CITY COUNT
43 NEW YORK 3
44 CHICAGO 1
44 ROME 1
43 LONDON 1
44 CHICAGO 2
One way to deal with such a gaps-and-islands problem is to calculate a ranking for the gaps.
Then group also on that ranking.
SELECT PASSENGER, CITY
, COUNT(*) AS "Count"
-- , MIN("DATE") AS StartDate
-- , MAX("DATE") AS EndDate
FROM (
SELECT q1.*
, SUM(gap) OVER (PARTITION BY PASSENGER ORDER BY "DATE") as Rnk
FROM (
SELECT PASSENGER, CITY, "DATE"
, CASE
WHEN 1 = TRUNC("DATE")
- TRUNC(LAG("DATE")
OVER (PARTITION BY PASSENGER, CITY ORDER BY "DATE"))
THEN 0 ELSE 1 END as gap
FROM table_name t
) q1
) q2
GROUP BY PASSENGER, CITY, Rnk
ORDER BY MIN("DATE"), PASSENGER
PASSENGER
CITY
Count
43
NEW YORK
3
43
LONDON
1
44
CHICAGO
1
44
ROME
1
44
CHICAGO
2
db<>fiddle here
From Oracle 12, you can use MATCH_RECOGNIZE:
SELECT *
FROM table_name
MATCH_RECOGNIZE (
PARTITION BY passenger
ORDER BY "DATE"
MEASURES
FIRST(city) AS city,
COUNT(*) AS count
PATTERN (same_city+)
DEFINE
same_city AS FIRST(city) = city
);
Which, for the sample data:
CREATE TABLE table_name (PASSENGER, CITY, "DATE") AS
SELECT 43, 'NEW YORK', DATE '2021-01-01' FROM DUAL UNION ALL
SELECT 44, 'CHICAGO', DATE '2021-01-04' FROM DUAL UNION ALL
SELECT 43, 'NEW YORK', DATE '2021-01-02' FROM DUAL UNION ALL
SELECT 43, 'NEW YORK', DATE '2021-01-03' FROM DUAL UNION ALL
SELECT 44, 'ROME', DATE '2021-01-05' FROM DUAL UNION ALL
SELECT 43, 'LONDON', DATE '2021-01-04' FROM DUAL UNION ALL
SELECT 44, 'CHICAGO', DATE '2021-01-06' FROM DUAL UNION ALL
SELECT 44, 'CHICAGO', DATE '2021-01-07' FROM DUAL
Outputs:
PASSENGER
CITY
COUNT
43
NEW YORK
3
43
LONDON
1
44
CHICAGO
1
44
ROME
1
44
CHICAGO
2
If you have ordered the input result set (note: tables should be considered to be unordered) and want to maintain the order then:
SELECT *
FROM (SELECT t.*, ROWNUM AS rn FROM table_name t)
MATCH_RECOGNIZE (
PARTITION BY passenger
ORDER BY RN
MEASURES
FIRST(rn) AS rn,
FIRST("DATE") AS "DATE",
FIRST(city) AS city,
COUNT(*) AS count
PATTERN (same_city+)
DEFINE
same_city AS FIRST(city) = city
)
ORDER BY rn
Outputs:
PASSENGER
RN
DATE
CITY
COUNT
43
1
01-JAN-21
NEW YORK
3
44
2
04-JAN-21
CHICAGO
1
44
5
05-JAN-21
ROME
1
43
6
04-JAN-21
LONDON
1
44
7
06-JAN-21
CHICAGO
2
db<>fiddle here
I would like to implement the partitioned outer join in BigQuery. To give a concrete example, I'd like to achieve the partitioned outer join as the accepted answer here: https://dba.stackexchange.com/questions/227069/what-is-a-partitioned-outer-join
I understand there are a lot of discussions about this topic, but I can't make it work under BigQuery. I added partition by date after the left table following the same syntax in the answer as follows:
select * from (
select '2019-01-17' as date, 'London' as location, 11 as qty
union all
select '2019-01-15' as date, 'London' as location, 10 as qty
union all
select '2019-01-16' as date, 'Paris' as location, 20 as qty
union all
select '2019-01-17' as date, 'Boston' as location, 31 as qty
union all
select '2019-01-16' as date, 'Boston' as location, 30 as qty
) as sales partition by (date)
right join
(
select 'London' as location
union all
select 'Paris' as location
union all
select 'Boston' as location
)
as loc
using (location)
The target result I'm looking for is:
date qty location
15-JAN-19 NULL Boston
15-JAN-19 10 London
15-JAN-19 NULL Paris
16-JAN-19 30 Boston
16-JAN-19 NULL London
16-JAN-19 20 Paris
17-JAN-19 31 Boston
17-JAN-19 11 London
17-JAN-19 NULL Paris
But I got the following error: Syntax error: Unexpected keyword PARTITION at [11:12]
How can I implement it in BigQuery?
Below is for BigQuery Standard SQL
#standardSQL
SELECT `date`, qty, location
FROM (SELECT DISTINCT `date` FROM sales)
CROSS JOIN loc
LEFT JOIN sales
USING (`date`, location)
You can test, play with above using sample data from your question as in below example
#standardSQL
WITH sales AS (
SELECT '2019-01-17' AS `date`, 'London' AS location, 11 AS qty UNION ALL
SELECT '2019-01-15', 'London', 10 UNION ALL
SELECT '2019-01-16', 'Paris', 20 UNION ALL
SELECT '2019-01-17', 'Boston', 31 UNION ALL
SELECT '2019-01-16', 'Boston', 30
), loc AS (
SELECT 'London' AS location UNION ALL
SELECT 'Paris' UNION ALL
SELECT 'Boston'
)
SELECT `date`, qty, location
FROM (SELECT DISTINCT `date` FROM sales)
CROSS JOIN loc
LEFT JOIN sales
USING (`date`, location)
-- ORDER BY `date`, location
with below result
Row date qty location
1 2019-01-15 null Boston
2 2019-01-15 10 London
3 2019-01-15 null Paris
4 2019-01-16 30 Boston
5 2019-01-16 null London
6 2019-01-16 20 Paris
7 2019-01-17 31 Boston
8 2019-01-17 11 London
9 2019-01-17 null Paris
In case if you need dates to be in 15-JAN-19 format - you below
#standardSQL
SELECT FORMAT_DATE('%d-%b-%y', CAST(`date` AS DATE)) AS `date`, qty, location
FROM (SELECT DISTINCT `date` FROM sales)
CROSS JOIN loc
LEFT JOIN sales
USING (`date`, location)
so result will be
Row date qty location
1 15-Jan-19 null Boston
2 15-Jan-19 10 London
3 15-Jan-19 null Paris
4 16-Jan-19 30 Boston
5 16-Jan-19 null London
6 16-Jan-19 20 Paris
7 17-Jan-19 31 Boston
8 17-Jan-19 11 London
9 17-Jan-19 null Paris
SELECT c.DESCRIPTION,
fobt.OBJECT_ID,
fobt.FA_OBJECT_TRANS_VALUE_TYPE,
fobt.OBJECT_TRANSACTION_ID,
fobt.OBJECT_TRANSACTION_DATE,
fobt.AMOUNT,
fobt.FA_YEAR,
fobt.FA_PERIOD,
SUM(fobt.AMOUNT) OVER(
PARTITION BY fobt.
ORDER BY fobt., fobt.OBJECT_TRANSACTION_DATE
) AS RUNTOTAL,
DENSE_RANK() OVER(
PARTITION BY fobt.OBJECT_ID
ORDER BY fobt.FA_YEAR DESC
) RK1,
DENSE_RANK()
OVER(
PARTITION BY fobt.OBJECT_ID, c.DESCRIPTION
ORDER BY
fobt.FA_YEAR,
fobt.FA_PERIOD,
c.DESCRIPTION DESC
)
rnk2
FROM FA_OBJECT_TRANSACTION fobt
INNER JOIN (SELECT DISTINCT COMPANY,
OBJECT_ID,
OBJECT_TRANSACTION_ID,
CODE_C
FROM FA_) FT
ON fobt. = FT.
AND fobt. = FT.OBJECT_TRANSACTION_ID
INNER JOIN CODE_C c
ON FT.CODE_C = c.CODE_C
AND FT.COMPANY = c.COMPANY
WHERE fobt.OBJECT_ID = 12345
AND fobt.FA_ IN ('Depreciation')
ORDER BY 2, 4
I'm trying to fix the DENSE_RANK for OBJECT_ID, DESCRIPTION, YEAR AND PERIOD. There is only one object id = 1234 but there are 2 locations (Rotterdam and South Windsor). Equipment A was logged in location South Windsor from 5/2015 to 3/2016 and then transferred to Rotterdam on 4/2016. I will like to have a DENSE_RANK by PARTITION by OBJECT_ID and LOCATION but the order by is not working properly. I would like to take the latest date(year and month by location of that piece of equipment. For example, Equipment A on 3/2016 at location South Windsor would be ranked 1 because it is the latest date at that location and then transferred to location Rotterdam on 4/2016 and that would also get tagged ranked 1 because it changed location. Below are the current results and second example is the desired result.
If I understood everything correctly You need to add only DESC after fa_year in last dense_rank:
... dense_rank() over(partition by object_id, description
order by fa_year desc, fa_period desc) as rk2
Alternatively use over (... order by transaction_id desc).
Test:
with t (DESCRIPTION, OBJECT_ID, transaction_id, AMOUNT, FA_YEAR, FA_PERIOD)
as (select 'South Windsor, CT', 22208, 278620, 257, 2015, 5 from dual union all
select 'South Windsor, CT', 22208, 287864, 257, 2015, 6 from dual union all
select 'South Windsor, CT', 22208, 305508, 257, 2015, 7 from dual union all
select 'South Windsor, CT', 22208, 322323, 257, 2015, 8 from dual union all
select 'South Windsor, CT', 22208, 330902, 257, 2015, 9 from dual union all
select 'South Windsor, CT', 22208, 340183, 257, 2015, 10 from dual union all
select 'South Windsor, CT', 22208, 350218, 257, 2015, 11 from dual union all
select 'South Windsor, CT', 22208, 359261, 257, 2015, 12 from dual union all
select 'South Windsor, CT', 22208, 374836, 257, 2016, 1 from dual union all
select 'South Windsor, CT', 22208, 382864, 257, 2016, 2 from dual union all
select 'South Windsor, CT', 22208, 388910, 257, 2016, 3 from dual union all
select 'Rotterdam, NY', 22208, 388915, 257, 2016, 4 from dual)
select description, transaction_id, fa_year, fa_period,
sum(amount) over (partition by object_id order by fa_year, fa_period) total,
dense_rank() over(partition by object_id order by fa_year desc) rk1,
dense_rank() over(partition by object_id, description
order by fa_year desc, fa_period desc) rk2
from t
order by object_id, transaction_id
Output:
DESCRIPTION TRANSACTION_ID FA_YEAR FA_PERIOD TOTAL RK1 RK2
----------------- -------------- ---------- ---------- ---------- ------ ------
South Windsor, CT 278620 2015 5 257 2 11
South Windsor, CT 287864 2015 6 514 2 10
South Windsor, CT 305508 2015 7 771 2 9
South Windsor, CT 322323 2015 8 1028 2 8
South Windsor, CT 330902 2015 9 1285 2 7
South Windsor, CT 340183 2015 10 1542 2 6
South Windsor, CT 350218 2015 11 1799 2 5
South Windsor, CT 359261 2015 12 2056 2 4
South Windsor, CT 374836 2016 1 2313 1 3
South Windsor, CT 382864 2016 2 2570 1 2
South Windsor, CT 388910 2016 3 2827 1 1
Rotterdam, NY 388915 2016 4 3084 1 1
I need to calculate the price change of an item (both in cost and % change) over the last three years.
The table has four fields:
SKU_no, Date_updated, Price, Active_flag
When the Active_flag field is A, the item is active, when I it is inactive. Some items haven't changed prices in years so they won't have three years of entries with an inactive flag.
Sample table
SKU_NO Update_date Price Active_flag
30 1/1/1999 40.8 I
33 1/1/2014 70.59 A
33 1/1/2013 67.23 I
33 1/1/2012 60.03 I
33 1/1/2011 55.08 I
33 1/1/2010 55.08 I
34 1/1/2009 51 A
36 1/1/2014 70.59 A
36 1/1/2013 67.23 I
36 1/1/2012 60.03 I
38 1/1/2002 43.32 A
38 1/1/2001 43.32 I
38 4/8/2000 43.32 I
38 1/1/1999 43.32 I
39 1/1/2014 73.08 A
39 1/1/2013 69.6 I
39 1/1/2012 62.13 I
39 1/1/2011 57 I
39 1/1/2010 57 I
39 1/1/2009 52.8 I
This is the first query I wrote. I'm not too familiar with complex calculations
select
s.VENDOR,
s.FISCAL_YEAR,
s.FISCAL_MONTH_NO,
s.FISCAL_YEAR||'_'||FISCAL_MONTH_NO as PERIOD,
CASE WHEN S.COST_USED_FLAG IN ('CONTRACT') THEN 'CONTRACT' ELSE 'NON-CONTRACT' END AS CONTRACT_TYPE,
CASE WHEN ((s.FISCAL_YEAR = 2014 AND FISCAL_MONTH_NO <=9) OR (FISCAL_YEAR = 2013 AND FISCAL_MONTH_NO >=10)) THEN 'CP_1'
WHEN ((s.FISCAL_YEAR = 2013 AND FISCAL_MONTH_NO <= 9) OR (FISCAL_YEAR = 2012 AND FISCAL_MONTH_NO >=10)) THEN 'CP_2'
WHEN ((s.FISCAL_YEAR = 2012 AND FISCAL_MONTH_NO <= 9) OR (FISCAL_YEAR = 2011 AND FISCAL_MONTH_NO >=10)) THEN 'CP_3'
ELSE 'NULL' END CAGR_PERIODS,
CASE WHEN s.MARKET IN ('PO', 'SC', 'OC') THEN 'PC' ELSE 'EC' END AS MARKET_TYPE,
s.MARKET,
s.COST_PLUS_FLAG,
s.COST_USED_FLAG,
LPAD(S.PC_ITEM_NO,6,'0') AS NEW_ITEM_NO,
s.PC_ITEM_NO,
i.ITEM_NO,
i.VEND_CAT_NUM,
i.DESCRIPTION,
s.PC_PROD_CAT,
s.PC_PROD_SUBCAT,
i.SELL_UOM,
i.QTY_PER_SELL_UOM,
i.PRIMARY_UOM,
i.HEAD_CONV_FACT,
SUM(s.QTY_EACH) AS QUANTITY_SOLD,
SUM(s.EXT_GROSS_COGS) AS TOTAL_COGS,
SUM(s.EXT_GROSS_COGS)/ SUM(s.QTY_EACH) as NET_SALES,
SUM(s.EXT_SALES)/ SUM(s.QTY_EACH) as ASP,
SUM(s.EXT_SALES) AS TOTAL_SALES,
SUM(S.EXT_SALES) - SUM(S.EXT_GROSS_COGS) as GROSS_PROFIT
from SIXSIGMA.CIA_ALL_SALES_TREND_DATA s
INNER JOIN MGMSH.ITEM i
ON S.PC_ITEM_NO = I.ITEM_NO
WHERE S.VENDOR = 'BD' AND
(S.EXT_SALES IS NOT NULL AND S.FISCAL_YEAR IN ('2013','2012','2011'))
GROUP BY
s.VENDOR,
s.FISCAL_YEAR,
s.FISCAL_MONTH_NO,
s.FISCAL_YEAR||'_'||FISCAL_MONTH_NO,
CASE WHEN s.MARKET IN ('PO', 'SC', 'OC') THEN 'PC' ELSE 'EC' END,
CASE WHEN S.COST_USED_FLAG IN ('CONTRACT') THEN 'CONTRACT' ELSE 'NON-CONTRACT' END,
CASE WHEN ((s.FISCAL_YEAR = 2014 AND FISCAL_MONTH_NO <=9) OR (FISCAL_YEAR = 2013 AND FISCAL_MONTH_NO >=10)) THEN 'CP_1'
WHEN ((s.FISCAL_YEAR = 2013 AND FISCAL_MONTH_NO <= 9) OR (FISCAL_YEAR = 2012 AND FISCAL_MONTH_NO >=10)) THEN 'CP_2'
WHEN ((s.FISCAL_YEAR = 2012 AND FISCAL_MONTH_NO <= 9) OR (FISCAL_YEAR = 2011 AND FISCAL_MONTH_NO >=10)) THEN 'CP_3'
ELSE 'NULL' END,
s.MARKET,
s.COST_USED_FLAG,
s.COST_PLUS_FLAG,
s.PC_ITEM_NO,
s.PC_PROD_CAT,
i.SELL_UOM,
i.QTY_PER_SELL_UOM,
i.PRIMARY_UOM,
i.HEAD_CONV_FACT,
i.DESCRIPTION,
i.VEND_CAT_NUM,
s.PC_PROD_SUBCAT,
i.ITEM_NO
ORDER BY s.PC_ITEM_NO,s.FISCAL_YEAR, s.FISCAL_MONTH_NO
There are several ways to approach this, but I would recommend a windowing function such as LAG or LEAD. With these functions, you can reference neighboring rows. For example:
lead(column, offset, default) over (partition by some_column order by column)
And in the example below:
lead(price, 1, price) over (partition by sku_no order by update_date desc)
Here is a working example with sample data:
with sample_data as (
select '30' sku_no, to_date('1/1/1999','DD/MM/YYYY') update_date, 40.8 price, 'I' active_flag from dual union all
select '33', to_date('1/1/2014','DD/MM/YYYY'), 70.59, 'A' from dual union all
select '33', to_date('1/1/2013','DD/MM/YYYY'), 67.23, 'I' from dual union all
select '33', to_date('1/1/2012','DD/MM/YYYY'), 60.03, 'I' from dual union all
select '33', to_date('1/1/2011','DD/MM/YYYY'), 55.08, 'I' from dual union all
select '33', to_date('1/1/2010','DD/MM/YYYY'), 55.08, 'I' from dual union all
select '34', to_date('1/1/2009','DD/MM/YYYY'), 51 , 'A' from dual union all
select '36', to_date('1/1/2014','DD/MM/YYYY'), 70.59, 'A' from dual union all
select '36', to_date('1/1/2013','DD/MM/YYYY'), 67.23, 'I' from dual union all
select '36', to_date('1/1/2012','DD/MM/YYYY'), 60.03, 'I' from dual union all
select '38', to_date('1/1/2002','DD/MM/YYYY'), 43.32, 'A' from dual union all
select '38', to_date('1/1/2001','DD/MM/YYYY'), 43.32, 'I' from dual union all
select '38', to_date('4/8/2000','DD/MM/YYYY'), 43.32, 'I' from dual union all
select '38', to_date('1/1/1999','DD/MM/YYYY'), 43.32, 'I' from dual union all
select '39', to_date('1/1/2014','DD/MM/YYYY'), 73.08, 'A' from dual union all
select '39', to_date('1/1/2013','DD/MM/YYYY'), 69.6 , 'I' from dual union all
select '39', to_date('1/1/2012','DD/MM/YYYY'), 62.13, 'I' from dual union all
select '39', to_date('1/1/2011','DD/MM/YYYY'), 57 , 'I' from dual union all
select '39', to_date('1/1/2010','DD/MM/YYYY'), 57 , 'I' from dual union all
select '39', to_date('1/1/2009','DD/MM/YYYY'), 52.8 , 'I' from dual)
select
sku_no,
update_date,
price,
lead(price,1, price) over (partition by sku_no order by update_date desc) prior_price, -- Showing the offset
price - lead(price,1, price) over (partition by sku_no order by update_date desc) price_difference, -- Calculate the difference
round((price - lead(price,1, price) over (partition by sku_no order by update_date desc)) * 100 /price, 2) percent_change -- Calculate the percentage
from sample_data
where update_date >= add_months(trunc(sysdate,'YYYY'),-36); -- You said in the last three years
You can also use LAG with a different order by sort. If you want to calculate the difference from three years prior, I would suggest using the KEEP function.
Say, I have the following data:
select 1 id, date '2007-01-16' date_created, 5 sales, 'Bob' name from dual union all
select 2 id, date '2007-04-16' date_created, 2 sales, 'Bob' name from dual union all
select 3 id, date '2007-05-16' date_created, 6 sales, 'Bob' name from dual union all
select 4 id, date '2007-05-21' date_created, 4 sales, 'Bob' name from dual union all
select 5 id, date '2013-07-16' date_created, 24 sales, 'Bob' name from dual union all
select 6 id, date '2007-01-17' date_created, 15 sales, 'Ann' name from dual union all
select 7 id, date '2007-04-17' date_created, 12 sales, 'Ann' name from dual union all
select 8 id, date '2007-05-17' date_created, 16 sales, 'Ann' name from dual union all
select 9 id, date '2007-05-22' date_created, 14 sales, 'Ann' name from dual union all
select 10 id, date '2013-07-17' date_created, 34 sales, 'Ann' name from dual
I want to get results like the following:
Name Total_cumulative_sales Total_sales_current_month
Bob 41 24
Ann 91 34
In this table, for Bob, his total sales is 41 starting from the beginning. And for this month which is July, his sales for this entire month is 24. Same goes for Ann.
How do I write an SQL to get this result?
Try this way:
select name, sum(sales) as Total_cumulative_sales ,
sum(
case trunc(to_date(date_created), 'MM')
when trunc(sysdate, 'MM') then sales
else 0
end
) as Total_sales_current_month
from tab
group by name
SQL Fiddle Demo
More information
Trunc
Case Statement
SELECT Name,
SUM(Sales) Total_sales,
SUM(CASE WHEN MONTH(date_created) = MONTH(GetDate()) AND YEAR(date_created) = YEAR(GetDate()) THEN Sales END) Total_sales_current_month
GROUP BY Name
Should work, but there's probably a more elegant way to specify "in the current month".
This should work for sales over a number of years. It will get the cumulative sales over any number of years. It won't produce a record if there are no sales in the latest month.
WITH sales AS
(select 1 id, date '2007-01-16' date_created, 5 sales, 'Bob' sales_name from dual union all
select 2 id, date '2007-04-16' date_created, 2 sales, 'Bob' sales_name from dual union all
select 3 id, date '2007-05-16' date_created, 6 sales, 'Bob' sales_name from dual union all
select 4 id, date '2007-05-21' date_created, 4 sales, 'Bob' sales_name from dual union all
select 5 id, date '2013-07-16' date_created, 24 sales, 'Bob' sales_name from dual union all
select 6 id, date '2007-01-17' date_created, 15 sales, 'Ann' sales_name from dual union all
select 7 id, date '2007-04-17' date_created, 12 sales, 'Ann' sales_name from dual union all
select 8 id, date '2007-05-17' date_created, 16 sales, 'Ann' sales_name from dual union all
select 9 id, date '2007-05-22' date_created, 14 sales, 'Ann' sales_name from dual union all
select 10 id, date '2013-07-17' date_created, 34 sales, 'Ann' sales_name from dual)
SELECT sales_name
,total_sales
,monthly_sales
,mon
FROM (SELECT sales_name
,SUM(sales) OVER (PARTITION BY sales_name ORDER BY mon) total_sales
,SUM(sales) OVER (PARTITION BY sales_name,mon ORDER BY mon) monthly_sales
,mon
,max_mon
FROM ( SELECT sales_name
,sum(sales) sales
,mon
,max_mon
FROM (SELECT sales_name
,to_number(to_char(date_created,'YYYYMM')) mon
,sales
,MAX(to_number(to_char(date_created,'YYYYMM'))) OVER (PARTITION BY sales_name) max_mon
FROM sales
ORDER BY 2)
GROUP BY sales_name
,max_mon
,mon
)
)
WHERE max_mon = mon
;