Creating a field(s) that counts days within a month from date range? - sql

Similar to the following:
Count days within a month from date range
I want to find a way, within the MS-Access Query Design environment, to create fields that count the number of month/year days within a date range.
Here is what I want the data to look like:
Row | StartDate | EndDate | #DaysJan2010 | #DaysFeb2010 | #DaysMarch2010
001 01/02/2010 02/04/2012 29 28 31
002 01/02/2010 01/05/2010 4 0 0
003 04/02/2010 05/05/2010 0 0 0
004 01/02/2010 02/04/2012 29 28 31
005 02/02/2012 02/03/2012 0 2 0
Please keep in mind that both month and year are important because I need to be able to distinguish between the number of days that fall within a given date range for January 2010 and January 2011, as opposed to just the number of days within a given date range that are in January.
If there is a systematic way of performing of creating these fields by using SQL in Access, that would be my preferred method.
However, in the event that it is impossible (or very difficult) to do so, I would like to know how to build each field in the expression builder, so that I may at least be able to generate the count fields one at a time.
As always, thank you very much for your time.

There are cases where date manipulations can be aided by a "dates table". Similar to a "numbers table", a "dates table" is a table containing one row for every date in a given range, usually covering the entire range of dates that one could expect to encounter in the actual data.
For sample data in a table named [SampleData]
Row StartDate EndDate
--- ---------- ----------
001 2010-01-02 2012-02-04
002 2010-01-02 2010-01-05
003 2010-04-02 2010-05-05
004 2010-01-02 2012-02-04
005 2012-02-02 2012-02-03
and a [DatesTable] that is simply
theDate
----------
2010-01-01
2010-01-02
2010-01-03
...
2012-12-30
2012-12-31
the query
SELECT
sd.Row,
dt.theDate,
Year(dt.theDate) AS theYear,
Month(dt.theDate) AS theMonth
FROM
SampleData AS sd
INNER JOIN
DatesTable AS dt
ON dt.theDate >= sd.StartDate
AND dt.theDate <= sd.EndDate
returns a row for each date in the interval for each [SampleData].[Row] value. (For this particular sample data, that's 1568 rows in total.)
Performing an aggregation on that
SELECT
Row,
theYear,
theMonth,
COUNT(*) AS NumberOfDays
FROM
(
SELECT
sd.Row,
dt.theDate,
Year(dt.theDate) AS theYear,
Month(dt.theDate) AS theMonth
FROM
SampleData AS sd
INNER JOIN
DatesTable AS dt
ON dt.theDate >= sd.StartDate
AND dt.theDate <= sd.EndDate
) AS allDates
GROUP BY
Row,
theYear,
theMonth
gives us all of the counts
Row theYear theMonth NumberOfDays
--- ------- -------- ------------
001 2010 1 30
001 2010 2 28
001 2010 3 31
001 2010 4 30
001 2010 5 31
001 2010 6 30
001 2010 7 31
001 2010 8 31
001 2010 9 30
001 2010 10 31
001 2010 11 30
001 2010 12 31
001 2011 1 31
001 2011 2 28
001 2011 3 31
001 2011 4 30
001 2011 5 31
001 2011 6 30
001 2011 7 31
001 2011 8 31
001 2011 9 30
001 2011 10 31
001 2011 11 30
001 2011 12 31
001 2012 1 31
001 2012 2 4
002 2010 1 4
003 2010 4 29
003 2010 5 5
004 2010 1 30
004 2010 2 28
004 2010 3 31
004 2010 4 30
004 2010 5 31
004 2010 6 30
004 2010 7 31
004 2010 8 31
004 2010 9 30
004 2010 10 31
004 2010 11 30
004 2010 12 31
004 2011 1 31
004 2011 2 28
004 2011 3 31
004 2011 4 30
004 2011 5 31
004 2011 6 30
004 2011 7 31
004 2011 8 31
004 2011 9 30
004 2011 10 31
004 2011 11 30
004 2011 12 31
004 2012 1 31
004 2012 2 4
005 2012 2 2
We can then report on that, or crosstab it, or do any number of other fun things.
Side note:
One circumstance where a "dates table" can be very useful is when we have to deal with Statutory Holidays. That is because
Sometimes the "day off" for a Statutory Holiday is not the actual day. If "International Bacon Day" falls on a Sunday then we might get the Monday off.
Some Statutory Holidays can be tricky to calculate. For example, Good Friday for us Canadians is (if I remember correctly) "the Friday before the first Sunday after the first full moon after the Spring Equinox".
If we have a "dates table" then we can add a [StatutoryHoliday] Yes/No field to flag all of the (observed) holidays and then use ... WHERE NOT StatutoryHoliday to exclude them.

Related

Pandas: Group by two columns to get sum of another column

I look most of the previously asked questions but was not able to find answer for my question:
I have following data.frame
id year month score num_attempts
0 483625 2010 01 50 1
1 967799 2009 03 50 1
2 213473 2005 09 100 1
3 498110 2010 12 60 1
5 187243 2010 01 100 1
6 508311 2005 10 15 1
7 486688 2005 10 50 1
8 212550 2005 10 500 1
10 136701 2005 09 25 1
11 471651 2010 01 50 1
I want to get following data frame
year month sum_score sum_num_attempts
2009 03 50 1
2005 09 125 2
2010 12 60 1
2010 01 200 2
2005 10 565 3
Here is what I tried:
sum_df = df.groupby(by=['year','month'])['score'].sum()
But this doesn't look efficient and correct. If I have more than one column need to be aggregate this seems like a very expensive call. for example if I have another column num_attempts and just want to sum by year month as score.
This should be an efficient way:
sum_df = df.groupby(['year','month']).agg({'score': 'sum', 'num_attempts': 'sum'})

SQL Select rows based on date

I have a two tables (Table1 and Table2):
Table1
Date Name Other
2014-02-08 Alex 1
2014-06-15 Bob 1
Table2
Date Name Count
2014-02-07 Alex 1
2014-01-31 Alex 2
2014-02-09 Alex 4
2014-02-08 Alex 10
2014-02-10 Alex 0
2014-02-01 Alex 4
2014-01-08 Alex 5
2014-03-08 Alex 4
2014-06-01 Bob 22
2014-06-02 Bob 0
2014-06-10 Bob 9
2014-06-15 Bob 3
2014-06-16 Bob 3
2014-06-20 Bob 5
2014-06-14 Bob 18
2014-07-11 Bob 1
2014-08-15 Bob 2
I am having a difficult time constructing a query that accomplishes the following:
From Table1, run through each "Date" and "Name"
For a given "Date" and "Name" in Table1, go through Table2 and grab all rows that also have the same "Name" and that have dates that are 10 days before the "Date" (in Table1) and 5 days after "Date" (in Table1).
So, for Table1, "Alex" on "2014-02-08", I want to grab all rows in Table2 that also say "Alex" but whose date is between "2014-01-29" (10 days before 2014-02-08) and "2014-02-13" (5 days after 2014-02-08).
For "Bob" on "2014-06-15", I want to grab all rows in Table2 that also say "Bob" but whose date is between "2014-06-05" (10 days before 2014-06-15) and "2014-06-20" (5 days after 2014-06-15).
The expected output is:
Date Name Count
2014-02-07 Alex 1
2014-01-31 Alex 2
2014-02-09 Alex 4
2014-02-08 Alex 10
2014-02-10 Alex 0
2014-02-01 Alex 4
2014-06-10 Bob 9
2014-06-15 Bob 3
2014-06-16 Bob 3
2014-06-20 Bob 5
2014-06-14 Bob 18
In my real work, the number of rows in Table1 is much larger and the number of days I'd like to grab before/after the reference date can vary.
I think you can do something like this:
select t2.*
from table1 t2
where exists (select 1
from table1 t1
where t1.name = t2.name and t1.date >= t2.date - 'interval 10 day' and
t1.date <= t2.date + 'interval 10 day'
);
See:
http://sqlfiddle.com/#!4/82e1e/10
Assuming Oracle, but you get the picture:-). You would need to format the date in the results. That is left for your perusal.
In case, the above link cannot be opened, the sql is:
select t2.*
from
table2 t2, table1 t1
where
t1.name = t2.name
and t2.date1 > t1.date1 -10
and t2.date1 <= t1.date1 +5;
The result is:
DATE1 NAME COUNT
February, 07 2014 00:00:00+0000 Alex 1
January, 31 2014 00:00:00+0000 Alex 2
February, 09 2014 00:00:00+0000 Alex 4
February, 08 2014 00:00:00+0000 Alex 10
February, 10 2014 00:00:00+0000 Alex 0
February, 01 2014 00:00:00+0000 Alex 4
June, 10 2014 00:00:00+0000 Bob 9
June, 15 2014 00:00:00+0000 Bob 3
June, 16 2014 00:00:00+0000 Bob 3
June, 20 2014 00:00:00+0000 Bob 5
June, 14 2014 00:00:00+0000 Bob 18

Oracle SQL Developer - Trying to return products by quarter

I am trying to return a list of products that only appear in one quarter. I have decided the best way to do this is to I have been fiddling around with my code for a while and tried a COUNT but realised that this wouldn't work as it is counting the number of entries a product has rather than the number of quarters it appears in.
These are my three tables:
SALES FACT TABLE
TIME_KEY PRODUCT_KEY BRANCH_KEY LOCATION_KEY POUNDS_SOLD AVG_SALES UNITS_SOLD
----------------------------- ----------- ---------- ------------ ----------- ---------- ----------
22-DEC-13 08.31.18.442000000 2 B1 L19 21542.39 10821.2 100
21-DEC-10 21.19.37.182000000 3 B8 L5 65487 32793.5 100
13-SEP-13 06.36.03.720000000 7 B2 L15 78541.84 39470.92 400
24-JUN-13 12.21.45.186000000 1 B7 L13 94115 47167.5 220
18-SEP-07 12.58.06.873000000 8 B2 L2 54000 27250 500
11-FEB-11 18.06.08.475000000 8 B9 L6 11123 5636.5 150
28-SEP-13 15.06.20.153000000 6 B3 L16 45896.31 23008.16 120
22-DEC-08 19.34.48.490000000 5 B6 L3 87451.01 43875.51 300
23-JUL-13 20.08.51.173000000 6 B6 L14 69542 34971 400
20-DEC-13 22.47.24.962000000 9 B4 L17 21584.39 10872.2 160
21-DEC-06 19.11.50.472000000 5 B10 L1 10000 27250 500
13-MAR-13 14.13.58.555000000 1 B2 L11 62413 31256 99
06-MAR-13 18.15.40.365000000 4 B6 L10 94785 47542.5 300
20-DEC-13 23.35.12.683000000 2 B5 L18 52359.19 26289.6 220
15-MAR-13 19.11.58.459000000 4 B9 L12 66499.84 33299.92 100
19-DEC-11 13.17.34.443000000 9 B2 L7 51449 26049.5 650
14-FEB-12 10.20.20.787000000 10 B5 L8 66589 33394.5 200
19-DEC-09 10.09.41.844000000 3 B7 L4 99125 49687.5 250
22-MAR-12 19.36.24.790000000 10 B2 L9 62331.66 31765.83 1200
11-JAN-14 19.18.58.595000000 7 B8 L20 35214.85 17667.43 120
TIME DIMENSION TABLE
TIME_KEY DAY DAY_OF_WEEK MONTH QUARTER YEAR
----------------------------- ---------- ----------- --------- ------- ----------
13-MAR-13 14.13.58.555000000 13 WEDNESDAY MARCH Q1 2013
22-DEC-13 08.31.18.442000000 22 SUNDAY DECEMBER Q4 2013
21-DEC-10 21.19.37.182000000 21 TUESDAY DECEMBER Q4 2010
15-MAR-13 19.11.58.459000000 15 FRIDAY MARCH Q1 2013
21-DEC-06 19.11.50.472000000 21 THURSDAY DECEMBER Q4 2006
28-SEP-13 15.06.20.153000000 28 SATURDAY SEPTEMBER Q3 2013
11-JAN-14 19.18.58.595000000 11 SATURDAY JANUARY Q1 2014
11-FEB-11 18.06.08.475000000 11 FRIDAY FEBRUARY Q1 2011
20-DEC-13 22.47.24.962000000 20 FRIDAY DECEMBER Q4 2013
14-FEB-12 10.20.20.787000000 14 TUESDAY FEBRUARY Q1 2012
24-JUN-13 12.21.45.186000000 24 MONDAY JUNE Q2 2013
20-DEC-13 23.35.12.683000000 20 FRIDAY DECEMBER Q4 2013
19-DEC-09 10.09.41.844000000 19 SATURDAY DECEMBER Q4 2009
06-MAR-13 18.15.40.365000000 6 WEDNESDAY MARCH Q1 2013
22-DEC-08 19.34.48.490000000 22 MONDAY DECEMBER Q4 2008
23-JUL-13 20.08.51.173000000 23 TUESDAY JULY Q3 2013
13-SEP-13 06.36.03.720000000 13 FRIDAY SEPTEMBER Q3 2013
18-SEP-07 12.58.06.873000000 18 TUESDAY SEPTEMBER Q3 2007
19-DEC-11 13.17.34.443000000 19 MONDAY DECEMBER Q4 2011
22-MAR-12 19.36.24.790000000 22 THURSDAY MARCH Q1 2012
PRODUCT DIMENSION TABLE
PRODUCT_KEY PRODUCT_NAME BRAND TYPE SUPPLIER_TYPE
----------- ------------------------- -------------------- ---------- ----------------
1 SVF1521P2EB SONY LAPTOP WHOLESALER
2 15-A003SA COMPAQ LAPTOP WHOLESALER
3 15-N271SA HP LAPTOP RETAIL
4 15-N290SA HP LAPTOP RETAIL
5 E6400 DELL LAPTOP RETAIL
6 SVF1521C2EB SONY LAPTOP WHOLESALER
7 SVF1532K4EB SONY LAPTOP WHOLESALER
8 C50-A-1CK TOSHIBA LAPTOP WHOLESALER
9 NX.MF8EK.001 ACER LAPTOP RETAIL
10 NP915S3G-K01UK SAMSUNG LAPTOP RETAIL
This is the code that I am running:
SELECT DISTINCT product.product_name, product.brand, quarter, SUM (sales.units_sold), COUNT (quarter)
FROM sales
INNER JOIN product
ON product.product_key=sales.product_key
INNER JOIN time
ON sales.time_key=time.time_key
GROUP BY quarter, product.product_name, product.brand
ORDER BY brand;
Below is the result once I run the query with the code that I have so far which is obviously not giving me what I want:
PRODUCT_NAME BRAND QUARTER SUM(SALES.UNITS_SOLD) COUNT(QUARTER)
------------------------- -------------------- ------- --------------------- --------------
NX.MF8EK.001 ACER Q4 810 2
15-A003SA COMPAQ Q4 320 2
E6400 DELL Q4 800 2
15-N271SA HP Q4 350 2
15-N290SA HP Q1 400 2
NP915S3G-K01UK SAMSUNG Q1 1400 2
SVF1521C2EB SONY Q3 520 2
SVF1521P2EB SONY Q1 99 1
SVF1521P2EB SONY Q2 220 1
SVF1532K4EB SONY Q1 120 1
SVF1532K4EB SONY Q3 400 1
C50-A-1CK TOSHIBA Q1 150 1
C50-A-1CK TOSHIBA Q3 500 1
I know its probably simple for you guys but I can sense that I am nearly there, I think I just have something the wrong way round and am not translating my intention into code.
The desired output would display products that have only been sold in one quarter. If they were sold in two quarters they would not be considered seasonal.
In your query count(quarter) is going to be the same as count(*). You need to remove quarter from the group by and do the comparison on the number of quarters in a having clause:
SELECT product.product_name, product.brand, MIN(quarter) as quarter, SUM(sales.units_sold)
FROM sales INNER JOIN
product
ON product.product_key = sales.product_key INNER JOIN
time
ON sales.time_key = time.time_key
GROUP BY product.product_name, product.brand
HAVING min(quarter) = max(quarter)
ORDER BY brand;
You could also use:
HAVING count(distinct quarter) = 1
However, count(distinct) is less efficient than most other aggregation functions.

Expected payments through a contracts life using MS access query?

I have the following table:
contract_ID
Contract_start_date
Contract_Duration
Contract_amount
Fixed_premium
Pay_every_month
Sample Data:
contract_ID: 5
Contract_start_date: 01/01/2014
Contract_Duration: 3 years
Contract_amount: $90.000
Fixed_premium: $15000
Pay_every_month: 6 months
There should be an access query that uses the contracts data to generate a payments schedule as follows:
Contract ID......Due Date...............Amount Due
5 01 / 01 / 2014 $15.000
5 01 / 07 / 2014 $15.000
5 01 / 01 / 2015 $15.000
5 01 / 07 / 2015 $15.000
5 01 / 01 / 2016 $15.000
5 01 / 07 / 2016 $15.000
I used this SQL statement and it's not working:
SELECT
Contracts.contract_ID,
DateAdd("m",[Pay_every_month],[Contracts]![Contract_start_date]) AS Due_Date,
Contracts.Fixed_premium AS Amount_due
FROM Contracts;
Access database file is HERE
You can accomplish your goal with Access SQL using a "numbers table". Create a table named [NumbersFromZero] containing integer values starting from zero and going as high as necessary to cover the largest number of payments for any contract, e.g.,
n
--
0
1
2
3
...
98
99
Once that is in place, if we have test data like this in [Contracts]
contract_ID Contract_start_date Contract_Duration Contract_amount Fixed_premium Pay_every_month
----------- ------------------- ----------------- --------------- ------------- ---------------
5 2014-01-01 3 90000 15000 6
then the following query
SELECT
Contracts.contract_ID,
DateAdd("m", NumbersFromZero.n * Contracts.Pay_every_month, Contracts.Contract_start_date) AS Due_date,
Contracts.Fixed_premium AS Amount_due
FROM Contracts, NumbersFromZero
WHERE NumbersFromZero.n < (Contracts.Contract_Duration * 12 / Contracts.Pay_every_month)
will return
contract_ID Due_date Amount_due
----------- ---------- ----------
5 2014-01-01 15000
5 2014-07-01 15000
5 2015-01-01 15000
5 2015-07-01 15000
5 2016-01-01 15000
5 2016-07-01 15000

I need to show the monthly inventory data

I have a table some thing like as follows for Inventory details.
InventoryTable.
InventoryTableID DateCreated quantity ItemName
-------------------------------------------------
1 2010-02-04 12 abc
2 2010-03-10 4 abc
3 2010-03-13 5 xyz
4 2010-03-13 19 def
5 2010-03-17 15 abc
6 2010-03-29 15 abc
7 2010-04-01 22 xyz
8 2010-04-13 5 abc
9 2010-04-15 6 def
from the above table if my admin wants to know the inventory details for month April 2010 (i.e. Apr 1st 2010 - Apr 30th 2010)
I need the output as shown below.
inventory as on Apr 1st 2010
ItemName Datecreated qty
----------------------------
abc 2010-03-29 15
xyz 2010-04-01 22
def 2010-03-13 19
inventory as on Apr 30th 2010
ItemName Datecreated qty
---------------------------
abc 2010-04-13 5
xyz 2010-04-01 22
def 2010-04-15 6
For your first result set, run with #YourDataParam = '2010-04-01'. For the second set, use '2010-04-30'.
;with cteMaxDate as (
select it.ItemName, max(it.DateCreated) as MaxDate
from InventoryTable it
where it.DateCreated <= #YourDataParam
group by it.ItemName
)
select it.ItemName, it.DateCreated, it.qty
from cteMaxDate c
inner join InventoryTable it
on c.ItemName = it.ItemName
and c.MaxDate = it.DateCreated