Oracle sql split amounts by weeks - sql

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.

Related

Output number of occurrences of id in a table

PK Date ID
=== =========== ===
1 07/04/2017 22
2 07/05/2017 22
3 07/07/2017 03
4 07/08/2017 04
5 07/09/2017 22
6 07/09/2017 22
7 07/10/2017 05
8 07/11/2017 03
9 07/11/2017 03
10 07/11/2017 03
I want to count the number of ID occurred in a given week/month, something like this.
ID Count
22 3 --> count as 1 only in the same date occurred twice one 07/09/2017
03 2 --> same as above, increment only one regardless how many times it occurred in a same date
04 1
05 1
I'm trying to implement this in a perl file, to output/print it in a csv file, I have no idea on what query will I execute.
Seems like a simple case of count distinct and group by:
SELECT Id, COUNT(DISTINCT [Date]) As [Count]
FROM TableName
WHERE [Date] >= #StartDate
AND [Date] <= #EndDate
GROUP BY Id
ORDER BY [Count] DESC
You can use COUNT with DISTINCT e.g.:
SELECT ID, COUNT(DISTINCT Date)
FROM table
GROUP BY ID;
You can read more abot how to get month from a date in get month from a date (it also works for year).
Your query will be :
select DATEPART(mm,Date) AS month, COUNT(ID) AS count from table group by month
Hope that helped you.

How to find negative number in a column, which is followed by a positive number

I have a historical table XY with these contents:
ID Person_ID Balance_on_account ts
---- ----------- -------------------- ----------
01 05 +10 10.10.14
02 05 -10 20.10.14
03 05 -50 30.10.14
04 05 +50 30.10.14
05 05 -10 30.10.14
06 06 11 11.10.14
07 06 -40 15.10.14
08 06 +5 16.10.14
09 06 -10 30.10.14
and I need to create an SQL query which will give me those Person_ID's and timestamps where are
a) the Balance_on_account is negative - that's the easy one,
b) and at the same time is the record of negative Balance_on_account followed by a positive number.
Like for Person_ID = 05 I would have the row with ID = 05, and for Person_ID = 06 the row with ID = 09.
I never used it, but you could try analytic LEAD function
SELECT *
FROM (
SELECT ID, Person_ID, Balance_on_account, ts
LEAD (Balance_on_account, 1)
OVER (PARTITION BY Person_ID ORDER BY ID) next_balance
FROM XY)
WHERE Balance_on_account < 0 and next_balance >= 0
ORDER BY ID
LEAD lets you access the following rows in a query without joining with itself.
PARTITION BY groups rows by Person_ID so it doesn't mix different person's balances and ORDER BY defines the order within each group.
The filtering cannot be done in the inner query because it'd filter out the rows with positive balance.
next_balance will be null for the last row.
source analytic functions and LEAD
The following query should give you the expected results provided the database platform you are using supports Common Table Expressions and Window Functions e.g. SQL Server 2008 and up.
SqlFiddle
WITH TsOrder AS
(
SELECT
Id
, Person_Id
, Balance_on_account
, ts
, ROW_NUMBER() OVER(PARTITION BY Person_Id
ORDER BY ts, Id) AS ts_Order
FROM
[TableName]
)
SELECT
*
FROM
TsOrder
LEFT JOIN TsOrder AS NextTs
ON TsOrder.Person_id = NextTs.Person_Id
AND TsOrder.ts_order = NextTs.ts_order - 1
WHERE
TsOrder.Balance_on_account < 0
AND NextTs.Balance_on_account > 0

Select most frequent month of a year in SQLITE3?

I have a table like the following-
year month frequency
---------- ---------- ----------
2501 04 33
2501 03 911
2503 12 377
2503 11 3956
2503 10 1409
2503 07 161
2503 06 66
2504 03 46
How to get the most frequent month of each year to produce
year month frequency
---------- ---------- ----------
2501 03 911
2503 11 3956
2504 03 46
Possible solution is to use join :
select t1.*
from t t1 join (select year, max(freq) freq from t group by year) t2
on t1.year = t2.year and t1.freq = t2.freq
SQLFiddle
The following query provides your solution. SQLFiddle here.
select year, month, max(frequency) frequency
from mytable
group by year
UPDATE:
Your required output had columns year, month, frequency where frequency was the maximum value of frequency per year. So max(frequency) frequency assigns the alias frequency to the query output so that it matches your requirement. Without the alias, the columns would be year, month, max(frequency). Here is the wiki on SQL aliases.

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)