Can I get a whole year one table or do I have to change the query for each month? - sql

The following code is probably self explanatory
Lists number of active subscribers 1st of june
select
count(subscribers) as activeSubscribers
from subscriberlist sub
where
to_date('2012-06', 'yyyy-mm')
between sub.firstDayOfSubscription and sub.lastDayOfSubscription
But if I want to get the number of subscribers in the beginning of every month the past year, what could I do (besides changing the date in the query and repeating).

You can generate a list of months as follows:
SELECT add_months (trunc (to_date('01/01/2012','MM/DD/YYYY'), 'MM'), 1*LEVEL -1) FirstDay
FROM Dual
CONNECT BY LEVEL <= MONTHS_BETWEEN(to_date('12/31/2012','MM/DD/YYYY'), to_date('01/01/2012','MM/DD/YYYY')) + 1
ORDER BY FirstDay
This would give you the following output:
FIRSTDAY
--------
January, 01 2012 00:00:00+0000
February, 01 2012 00:00:00+0000
March, 01 2012 00:00:00+0000
April, 01 2012 00:00:00+0000
May, 01 2012 00:00:00+0000
June, 01 2012 00:00:00+0000
July, 01 2012 00:00:00+0000
August, 01 2012 00:00:00+0000
September, 01 2012 00:00:00+0000
October, 01 2012 00:00:00+0000
November, 01 2012 00:00:00+0000
December, 01 2012 00:00:00+0000
You can then include that query as an inline view as follows:
SELECT months.FirstDay,
(SELECT count(subscribers)
FROM subscriberlist sub
WHERE months.FirstDay BETWEEN sub.firstDayOfSubscription AND sub.lastDayOfSubscription
) AS activeSubscribers
FROM (SELECT add_months(trunc (to_date('01/01/2012','MM/DD/YYYY'), 'MM'), 1*LEVEL -1) FirstDay
FROM Dual
CONNECT BY LEVEL <= MONTHS_BETWEEN(to_date('12/31/2012','MM/DD/YYYY'), to_date('01/01/2012','MM/DD/YYYY')) + 1
ORDER BY FirstDay) Months
I'd probably wrap this in a sproc that accepts #startDate and #endDate parameters to define the date range you're interested, but I followed your syntax with the date range being defined with strings.
I was playing with this using this SqlFiddle

This might be a good use for a date lookup table. I've found them useful enough to try to always have one in any decent-sized database.
Build a table in your database with one line per day, for a reasonable period of time... say from ten years ago to ten years from now (depending on what sort of queries you run).
In the table add a number of fields based on the date:
Date
Week
Week_Ending
Month
Month_DayOf
Month_Beginning
Year
Business_Day
Weekend
Holiday
etc...
The handy thing about this is that once it's populated, you can join it to other tables by the date field to summarize or filter data by date however you need. For example:
select
dt.Month
count(subscribers) as activeSubscribers
from subscriberlist sub
join (select unique datelookup.Month_Beginning
from date_reference ) dt
on dt.Month_Beginning between sub.firstDayOfSubscription and sub.lastDayOfSubscription
group by
dt.Month;
(Off the top of my head, I'm not sure if this syntax is exactly right, especially for Oracle.)
If you don't have the ability to add one to your database, you can create a temporary table as part of your query and fill it with the date ranges you're concerned with.

Related

Getting calculated percentages within group using SQL

I have a dataset:
Date
June 2011
July 2011
Aug 2011
Sep 2011
Oct 2011
Jan 2012
Feb 2012
Mar 2012
Apr 2013
May 2013
that records down the date registered for each yearly project. (There are no project IDs however)
I would like to add in the additional variable Percentage, which represent the average progress made for that month. (For instance, if the project is registered for 4 months, then each month would progress incrementally by 25 %, (25,50,75,100)), specifically:
Percentage Date
20 June 2011
40 July 2011
60 Aug 2011
80 Sep 2011
100 Oct 2011
33 Jan 2012
66 Feb 2012
100 Mar 2012
50 Apr 2013
100 May 2013
However, my main problem would be that I am unable to know the starting month (period) and ending month (period) for each project for each year.
Are there any functions in SQL to create the calculated percentages in this case? I thought of creating a year variable and further using an indicator to indicate the start/end of the progress, but could not move on further.
Thank you again!
You can try this query.
Getting ROW_NUMBER() by year(dates) on subquery.
then get the percent.
SELECT (FLOOR(CAST(T2.RK AS decimal) * 100/(
SELECT COUNT(1) AS Totle
FROM T
WHERE year(dates) = T2.dates))) [Percentage],
T2.dates as [years],
T2.months as [months]
FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY year(dates) ORDER BY dates DESC) AS RK,
year(dates) as dates,
month(dates) as months
FROM T
) AS T2
GROUP BY T2.dates,T2.RK,T2.months
Here is a simple Pseudo sql to get what you want . ?
select Year(date), Month(date) ,
(select sum(Progress_percentage) from dataset b where b.date <=a.date ) as
subquery_percentage
from dataset a
group by Year(date), Month(date)

oracle pivot query suggestion

I have a simple table that has data like the following
FiscalYear Month Value
2013 01 10
2013 02 15
....
2014 01 15
2014 02 20
using Oracle(11g) Pivot query is it possible to get something like this?
Month 2013 2014
01 10 15
02 15 20
SELECT month, value_2013, value_2014
FROM (SELECT fiscalyear, month, value FROM your_table)
PIVOT (SUM (value) AS value
FOR (fiscal_year)
IN ('2013', '2014'))

Select Every Date for Date Range and Insert

Using SQL Server 2008
I have a table A which has start date, end date and value. For each date within the start date and end date in Table A, I need to insert (or update if already exists) that date in table B such that the value in this table is value in A/DateDiff(Day,StartDate of A,EndDate of A).
Example:
Table A
ID StartDate EndDate Value
1 01 Jan 2014 03 Jan 2014 33
2 01 Feb 2014 02 Feb 2014 20
3 02 Jan 2014 03 Jan 2014 10
Table B
ID Date Value
1 01 Jan 2014 11
2 02 Jan 2014 16
3 03 Jan 2014 16
4 01 Feb 2014 10
5 02 Feb 2014 10
The way values are computed are - For ID 1, there are 3 days which means 11 units per day. So 1st, 2nd, 3rd Jan all get 11 units. Then because there are additional units with date range 2nd Jan to 3rd Jan which amount to 5 units per day, 2nd and 3rd Jan will be (11+5) 16. 1st and 2nd Feb just have one record so they will simply be 20/2 = 10.
I can think of a solution using loops, but want to avoid it entirely.
Is there any way I can achieve this through a set based solution? It is important for me to do this in bulk using set based approach.
I am trying to read through various articles and seems like CTE, Calendar Table or Tally Table might help but the examples I have seen require setting variables and passing start date and end date which I think will work for single record but not when doing all records at a time. Please suggest.
Thanks!
I think this should do it (DEMO):
;with cte as (
select
id
,startdate
,enddate
,value / (1+datediff(day, startdate, enddate)) as value
,startdate as date
from units
union all
select id, startdate, enddate, value, date+1 as date
from cte
where date < enddate
)
select
row_number() over (order by date) as ID
,date
,sum(value) as value
from cte
group by date
The idea is to use a Recursive CTE to explode the date ranges into one record per day. Also, the logic of value / (1+datediff(day, startdate, enddate)) distributes the total value evenly over the number of days in each range. Finally, we group by day and sum together all the values corresponding to that day to get the output:
| ID | DATE | VALUE |
|----|---------------------------------|-------|
| 1 | January, 01 2014 00:00:00+0000 | 11 |
| 2 | January, 02 2014 00:00:00+0000 | 16 |
| 3 | January, 03 2014 00:00:00+0000 | 16 |
| 4 | February, 01 2014 00:00:00+0000 | 10 |
| 5 | February, 02 2014 00:00:00+0000 | 10 |
From here you can join with your result table (Table B) by date, and update/insert the value as needed. That logic might look something like this (test it first of course before running in production!):
update B set B.VALUE = R.VALUE from TableB B join Result R on B.DATE = R.DATE
insert TableB (DATE, VALUE)
select DATE, VALUE from Result R where R.DATE not in (select DATE from TableB)

Oracle sql split amounts by weeks

So I have a table like:
UNIQUE_ID MONTH
abc 01
93j 01
acc 01
7as 01
oks 02
ais 02
asi 03
asd 04
etc
I query:
select count(unique_id) as amount, month
from table
group by month
now everything looks great:
AMOUNT MONTH
4 01
2 02
1 03
etc
is there a way to get oracle to split the amounts by weeks?
the way that the result look something like:
AMOUNT WEEK
1 01
1 02
1 03
1 04
etc
Assuming you know the year - lets say we go with 2014 then you need to generate all the weeks a year
select rownum as week_no
from all_objects
where rownum<53) weeks
then state which months contain the weeks (for 2014)
select week_no, to_char(to_date('01-JAN-2014','DD-MON-YYYY')+7*(week_no-1),'MM') month_no
from
(select rownum as week_no
from all_objects
where rownum<53) weeks
Then join in your data
select week_no,month_no, test.unique_id from (
select week_no, to_char(to_date('01-JAN-2014','DD-MON-YYYY')+7*(week_no-1),'MM') month_no
from
(select rownum as week_no
from all_objects
where rownum<53) weeks) wm
join test on wm.month_no = test.tmonth
This gives your data for the each week as you described above. You can redo your query and count by week instead of month.

SQL query order by, not working for last 10 records in database

Hi I have a database table: VQ_log with the following structure:
Column 1: Page; Column 2: event; Column 3:msg; Column 4:event_datetime
I run this following query:
SELECT page,event,msg,event_datetime
FROM VQ_log
WHERE event_datetime >= :dateStart AND event_datetime <= :dateEnd
AND msg = :merchantID
ORDER BY event;"
The first 100 records are sorted in order by event(which is a numeric number), however, the last 10 records have a numeric record smaller than the first 100 records
Please advise
Try this instead
SELECT * FROM VQ_log
WHERE event_datetime between dateStart AND dateEnd
AND msg = :merchantID ORDER BY event desc;
BTW, from where does dateStart,dateEnd and merchantID values are coming? doesn't looks like this is your entire query.
EDIT:
Not sure what your table structure/schema is but per my understanding your event column is varchar type rather than a int type (considering you are using MYSQL) and hence you are not getting the proper order.
For example I tried this:
create table mytab (page varchar(10), event varchar(10), msg varchar(20),
event_datetime datetime);
insert into mytab values('page1',772,'Hi',curdate());
insert into mytab values('page21',700,'Hello',curdate());
insert into mytab values('page3',3422,'Go',curdate());
insert into mytab values('page31',3000,'There',curdate());
Then when I run the below query
SELECT * FROM mytab
WHERE event_datetime between curdate() AND '2013-07-29'
ORDER BY event;
This is what the result is (See the event column):
PAGE EVENT MSG EVENT_DATETIME
page31 3000 There July, 28 2013 00:00:00+0000
page3 3422 Go July, 28 2013 00:00:00+0000
page21 700 Hello July, 28 2013 00:00:00+0000
page1 772 Hi July, 28 2013 00:00:00+0000
But if I change the event column to int as
create table mytab (page varchar(10), event int, msg varchar(20),
event_datetime datetime);
Then result becomes
PAGE EVENT MSG EVENT_DATETIME
page21 700 Hello July, 28 2013 00:00:00+0000
page1 772 Hi July, 28 2013 00:00:00+0000
page31 3000 There July, 28 2013 00:00:00+0000
page3 3422 Go July, 28 2013 00:00:00+0000
Hope this helps.