Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have a table
I want output like this:
This is a type of gaps-and-islands problem. You need to find the adjacent rows -- which you can do by subtracting a sequence. That is:
select t.*,
row_number() over (partition by trend, add_months(date, -seqnum) order by date) as new_trend
from (select t.*,
row_number() over (partition by trend order by day) as seqnum
from t
) t
You can use window functions as follows:
Select day, Row_number() over (partition by sm order by day) as trend
From
(Select t.*, Sum(case when trend = lg_trend then 0 else 1 end) over(order by day) as sm
From
(Select t.*, Lag(trend) over (order by day) as lg_trend
From yourtable t) t) t
Related
i have a sql table which the following data shown in the picture
I need to create a query in sql which counts for ticker the number of consecutive days per year in which
the close_value is greater than the open_value, if close_value is less than the open value the counter must be reset to zero and I have to save the counter in that instant
This is an example of a gaps-and-islands problem. You can use the difference of row_numbers():
select ticker, min(date), max(date), min(open_value), max(close_value),
count(*) as num_rows
from (select t.*,
row_number() over (partition by ticker order by date) as seqnum,
row_number() over (partition by ticker, (case when close_value > open_value then 1 else 2 end) order by date) as seqnum_2
from t
) t
where close_value > open_value
group by ticker, (seqnum - seqnum_2);
This returns all such periods. You haven't specified what the result set should look like, but this should be pretty close.
I have a table similar to below where the same account has its fiscal years (FY) and deductions for each year broken out in multiple rows. Accounts can range from 1 - 20+ years. How do I group to one unique row that shows the current year and how many years its been since the account had a deduction?
from this:
to this:
Started to utilize the CTE approach as I have in the past, but as before it started to get ugly and I know there has to be a simpler approach...
Assuming the current year is the most recent year, you would use aggregation:
select account, max(fy),
sum(case when fy = max_fy then deductions end) as this_year_deduction,
max(fy) - max(case when deduction < 0 then fy end) as years_since_deduction
from (select t.*, max(fy) over (partition by account) as max_fy
from t
) t
group by account;
Note: I assume the third column is the most recent deduction. The query uses a window function to extract that.
Haven't used the methods below but I think it is close to what is needed. Corrections welcome. (Code not tested)
with nonZeroes as
(
select * from YourTable where deductions <> 0
)
select Account,
FY,
FY - LAST_VALUE(FY) OVER (PARTITION BY Account
ORDER BY Year Desc
RANGE BETWEEN CURRENT ROW AND UNBOUNDED PRECEDING) AS years_since_deductions
from nonZeroes
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I have one table call sales with following 5 columns
sales - order_id, book_id, date, price, qty (i.e. # of books per book_id)
I want to find the top 3 books (book_id) sold in revenue for each month. I want to avoid using the dense_rank() function.
Window functins are definitely the way to go here. This should be as simple as:
select *
from (
select
date_trunc('month', date) date_month,
book_id,
sum(qty) total_qty,
rank() over(partition by date_trunc('month', date) order by sum(qty) desc) rn
from sales
group by date_trunc('month', date), book_id
) t
where rn <= 3
order by date_month, rn
Without window functions, one option would be to use a having clause with a correlated, aggregate subquery:
select
date_trunc('month', date) date_month,
book_id,
sum(qty) total_qty
from sales s
group by date_trunc('month', date), book_id
having sum(qty) >= (
select sum(qty)
from sales s1
where date_trunc('month', s1.date) = date_trunc('month', s.date)
group by book_id
order by sum(qty) desc
limit 1 offset 2
)
order by date_month, total_qty desc
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
It might be already answered before, but apparently I am not googling it right.
Let's say I have a table:
RecordingDate
2018-07-01
2018-07-02
2018-07-06
2018-07-09
2018-07-10
2018-07-11
2018-07-12
2018-07-16
2018-07-17
2018-07-18
I want to group the data by the date of the first recording and count days in a row with recordings:
DateOfFirstRecording RecordingsInARow
2018-07-01 2
2018-07-06 1
2018-07-09 4
2018-07-16 3
How do I do that ?
You can subtract an increasing sequence, to get a constant date. This is the idea:
select min(date), max(date), count(*)
from (select t.*, row_number() over (order by date) as seqnum
from t
) t
group by date - seqnum * interval '1 day'
order by min(date);
Date operations vary significantly depending on the database, but all databases support this functionality with some syntax.
EDIT:
In SQL Server, this looks like:
select
min(RecordingDate) as DateOfFirstRecording,
count(*) as RecordingsInARow
from (select Recordings.*, row_number() over (order by RecordingDate) as seqnum
from Recordings
) t
group by dateadd(day, - seqnum, RecordingDate)
order by min(RecordingDate);
I have the sample data set below which list the water meters not working for specific reason for a certain range period (jan 2016 to december 2018).
I would like to have a query that retrieves the last maximum and minimum consecutive period where the meter was not working within that range of period.
any help will be greatly appreciated.
You have two options:
select code, to_char(min_period, 'yyyymm') min_period, to_char(max_period, 'yyyymm') max_period
from (
select code, min(period) min_period, max(period) max_period,
max(min(period)) over (partition by code) max_min_period
from (
select code, period, sum(flag) over (partition by code order by period) grp
from (
select code, period,
case when add_months(period, -1)
= lag(period) over (partition by code order by period)
then 0 else 1 end flag
from (select mrdg_acc_code code, to_date(mrdg_per_period, 'yyyymm') period from t)))
group by code, grp)
where min_period = max_min_period
Explanation:
flag rows where period is not equal previous period plus one month,
create column grp which sums flags consecutively,
group data using code and grp additionaly finding maximal start of period,
show only rows where min_period = max_min_period
Second option is recursive CTE available in Oracle 11g and above:
with
data(period, code) as (
select to_date(mrdg_per_period, 'yyyymm'), mrdg_acc_code from t
where mrdg_per_period between 201601 and 201812),
cte (period, code) as (
select to_char(period, 'yyyymm'), code from data
where (period, code) in (select max(period), code from data group by code)
union all
select to_char(data.period, 'yyyymm'), cte.code
from cte
join data on data.code = cte.code
and data.period = add_months(to_date(cte.period, 'yyyymm'), -1))
select code, min(period) min_period, max(period) max_period
from cte group by code
Explanation:
subquery data filters only rows from 2016 - 2018 additionaly converting period to date format. We need this for function add_months to work.
cte is recursive. Anchor finds starting rows, these with maximum period for each code. After union all is recursive member, which looks for the row one month older than current. If it finds it then net row, if not then stop.
final select groups data. Notice that period which were not consecutive were rejected by cte.
Though recursive queries are slower than traditional ones, there can be scenarios where second solution is better.
Here is the dbfiddle demo for both queries. Good luck.
use aggregate function with group by
select max(mdrg_per_period) mdrg_per_period, mrdg_acc_code,max(mrdg_date_read),rea_Desc,min(mdrg_per_period) not_working_as_from
from tablename
group by mrdg_acc_code,rea_Desc
This is a bit tricky. This is a gap-and-islands problem. To get all continuous periods, it will help if you have an enumeration of months. So, convert the period to a number of months and then subtract a sequence generated using row_number(). The difference is constant for a group of adjacent months.
This looks like:
select acc_code, min(period), max(period)
from (select t.*,
row_number() over (partition by acc_code order by period_num) as seqnum
from (select t.*, floor(period / 100) * 12 + mod(period, 100) as period_num
from t
) t
where rea_desc = 'METER NOT WORKING'
) t
group by (period_num - seqnum);
Then, if you want the last one for each account, you can use a subquery:
select t.*
from (select acc_code, min(period), max(period),
row_number() over (partition by acc_code order by max(period desc) as seqnum
from (select t.*,
row_number() over (partition by acc_code order by period_num) as seqnum
from (select t.*, floor(period / 100) * 12 + mod(period, 100) as period_num
from t
) t
where rea_desc = 'METER NOT WORKING'
) t
group by (period_num - seqnum)
) t
where seqnum = 1;