I need to get the amount of consecutive unpayments:
with payments as
(
select '1' as ID, '20130331' as DateR, 'Not_paid' as Status from dual
union
select '1' as ID, '20130430' as DateR, 'Paid' as Status from dual
union
select '1' as ID, '20130531' as DateR, 'Not_paid' as Status from dual
union
select '2' as ID, '20130331' as DateR, 'Not_paid' as Status from dual
union
select '2' as ID, '20130430' as DateR, 'Not_paid' as Status from dual
union
select '3' as ID, '20130331' as DateR, 'Paid' as Status from dual
union
select '3' as ID, '20130430' as DateR, 'Paid' as Status from dual
union
select '3' as ID, '20130531' as DateR, 'Paid' as Status from dual
)
select ID, dater, status, dense_rank() over (partition by ID, status order by dater asc) rnk from payments
As you see from this I get the right number of unpayments from id 2: his first unpayment was in March, and the second in April. Id 3 is ok too, because I would exclude him out later on, but for id 1 it says the second unpayment was in May, while I want to make it to be the first because he unpaid in March, but paid again in April so it should start ranking from there. Once he paid his last payment the process starts again.
The idea is to keep it simple without complex queries. I just need to do the same as the dense rank but only when the dates are consecutive
I hope the example is clear enough.
Edit:
This is what I get from the current query:
ID DATER STATUS RNK
1 20130331 Not_paid 1
1 20130531 Not_paid 2
1 20130430 Paid 1
2 20130331 Not_paid 1
2 20130430 Not_paid 2
3 20130331 Paid 1
3 20130430 Paid 2
3 20130531 Paid 3
And what I would like to get is this:
ID DATER STATUS RNK
1 20130331 Not_paid 1
1 20130430 Paid 1
1 20130531 Not_Paid 1
2 20130331 Not_paid 1
2 20130430 Not_paid 2
3 20130331 Paid 1
3 20130430 Paid 2
3 20130531 Paid 3
Such that if I want to get the max(rank) to check how many unpayments a user currently has I get that ID has 1 unpayment, ID 2 two consecutive unpayments, and ID 3 has 0 unpayments. This is because on the forth consecutive unpayment I have to consider the user as churned.
Edit:29/06/2013
Someone gave me a perfect solution in another forum:
https://forums.oracle.com/thread/2555552
This isn't a complete answer to your question, but it's a possible solution to get the number of outstanding non-payments for each id. It assigns a value of 1 for a Not_paid status and a -1 for a Paid status. So we can then group the query by ID and sum the value column to get the number of outstanding payments. For sums that are negative, we assign to zero as they have no outstanding payments.
with payments as
(
select '1' as ID, '20130331' as DateR, 'Not_paid' as Status from dual
union
select '1' as ID, '20130430' as DateR, 'Paid' as Status from dual
union
select '1' as ID, '20130531' as DateR, 'Not_paid' as Status from dual
union
select '2' as ID, '20130331' as DateR, 'Not_paid' as Status from dual
union
select '2' as ID, '20130430' as DateR, 'Not_paid' as Status from dual
union
select '3' as ID, '20130331' as DateR, 'Paid' as Status from dual
union
select '3' as ID, '20130430' as DateR, 'Paid' as Status from dual
union
select '3' as ID, '20130531' as DateR, 'Paid' as Status from dual
)
SELECT id,
DECODE(SIGN(SUM(value)), -1, 0, SUM(value))
FROM (SELECT id,
dater,
status,
DECODE(status, 'Paid', -1, 1) value
FROM payments
)
GROUP BY id
ORDER BY id;
Now this query works for the data set in your example, but may not work for bigger data sets. Also it won't work if there aren't at least an equal number of Paid statuses for the Not_paid status. For instance, in your example for ID = 2, if the account is paid in full in May, there would need to be 2 Paid entries loaded into your table in order for my solution to work. If only 1 Paid entry was loaded, then my solution would still show an outstanding payment required for this ID.
Related
I have a table like this
Emplid
REQUEST_ID
Status
Status_Dttm
1234
1
New
02-Jun-2022 12.35.00.AM
1231
5
Draft
02-Jun-2022 12.30.00.AM
1234
1
In Progress
02-Jun-2022 12.47.00.AM
1234
1
Cancelled
02-Jun-2022 12.50.00.AM
1234
2
New
03-Jun-2022 12.47.00.AM
Trying to create a view with the fields as EMPLID,REQUEST_ID,REQUEST_DATE,NO_OF_DAYS_IN_NEW,IN_PROGRESS_SINCE,COMPLETED/CANCELLED DATE
Tried using this
SELECT MIN(emplid) AS emplid, request_id, MIN(status_dttm) AS request_date,
MAX(status) KEEP (DENSE_RANK LAST ORDER BY status_dttm) AS current_status
FROM sts_tbl
GROUP BY request_id
CASE WHEN (MAX(status) KEEP (DENSE_RANK LAST ORDER BY status_dttm)) = 'NEW' THEN
TRUNC(SYSDATE) - MIN(status_dttm)
END AS "No of Days in New"
CASE WHEN (MAX(status) KEEP (DENSE_RANK LAST ORDER BY status_dttm)) = 'In Progree' THEN
status_dttm
END AS "In Progress Since"
FROM sts_tbl
GROUP BY emplid,request_id
getting error as "Not a Group By expression"
You can use the LEAD analytic function to find the time of the next status change and then aggregate:
SELECT MIN(emplid) AS emplid,
request_id,
MIN(status_dttm) AS request_date,
SUM(CASE status WHEN 'New' THEN duration ELSE 0 END) AS no_of_days_in_new,
MIN(CASE status WHEN 'In Progress' THEN status_dttm END)
AS in_progress_since,
MAX(status) KEEP (DENSE_RANK LAST ORDER BY status_dttm)
AS current_status,
MIN(CASE WHEN status IN ('Completed', 'Cancelled') THEN status_dttm END)
AS completed_cancelled_date
FROM (
SELECT t.*,
LEAD(status_dttm, 1, SYSDATE) OVER (
PARTITION BY request_id ORDER BY status_dttm
) - status_dttm AS duration
FROM sts_tbl t
)
GROUP BY request_id
Which, for the sample data:
CREATE TABLE sts_tbl (Emplid, REQUEST_ID, Status, Status_Dttm) AS
SELECT 1234, 1, 'Open', DATE '2022-06-02' + INTERVAL '35' MINUTE FROM DUAL UNION ALL
SELECT 1231, 5, 'Draft', DATE '2022-06-02' + INTERVAL '30' MINUTE FROM DUAL UNION ALL
SELECT 1234, 1, 'In Progress', DATE '2022-06-02' + INTERVAL '47' MINUTE FROM DUAL UNION ALL
SELECT 1234, 1, 'Cancelled', DATE '2022-06-02' + INTERVAL '50' MINUTE FROM DUAL UNION ALL
SELECT 1234, 2, 'New', DATE '2022-06-03' + INTERVAL '47' MINUTE FROM DUAL;
Outputs:
EMPLID
REQUEST_ID
REQUEST_DATE
NO_OF_DAYS_IN_NEW
IN_PROGRESS_SINCE
CURRENT_STATUS
COMPLETED_CANCELLED_DATE
1234
1
2022-06-02 00:35:00
0
2022-06-02 00:47:00
Cancelled
2022-06-02 00:50:00
1234
2
2022-06-03 00:47:00
80.54319444444444444444444444444444444444
null
New
null
1231
5
2022-06-02 00:30:00
0
null
Draft
null
Note: there is not a next status for request_id 2 so it is assumed that the number of days in the New status is from the start of the new status until the current date.
db<>fiddle here
I have a table with
Logdate,Status
20190101 ok
20190101 notok
20190101 ok
20190102 ok
20190102 notok
I would like to get a query result like these:
date ok notok
20190101 2 1
20190102 1 1
I don't know hot make a query of same column agreggate with 2 different where's
Please any help?
Thanks!
edit--- mi querys
SELECT LOGDATE AS EXECUTION_DATE, COUNT(1) AS TOTAL_OK FROM CMR_IOALOG WHERE UPPER(STATUS) LIKE upper('% OK %') group by logdate ORDER BY LOGDATE DESC;
SELECT LOGDATE AS EXECUTION_DATE COUNT(1) AS TOTAL_NOTOK FROM CMR_IOALOG WHERE UPPER(STATUS) LIKE upper('%NOTOK%') group by logdate ORDER BY LOGDATE DESC;
You can use conditional aggregation, via a case expression inside the count() call:
select logdate,
count(case when status = 'ok' then status end) as ok,
count(case when status = 'notok' then status end) as notok
from your_table
group by logdate;
The count() function ignores nulls, so the case expression gives a not-null value for the status you want to count, and defaults to null for anything else.
Demo with your sample data as a CTE:
-- CTE for sample data
with your_table (logdate, status) as (
select 20190101, 'ok' from dual
union all select 20190101, 'notok' from dual
union all select 20190101, 'ok' from dual
union all select 20190102, 'ok' from dual
union all select 20190102, 'notok' from dual
)
-- actual query
select logdate,
count(case when status = 'ok' then status end) as ok,
count(case when status = 'notok' then status end) as notok
from your_table
group by logdate;
LOGDATE OK NOTOK
---------- ---------- ----------
20190102 1 1
20190101 2 1
Hopefully your logdate is actually a date rather than a number; I've just used a number to match the value you showed. If it is a date and has non-midnight times then you can trunc(logdate) to count values across the whole day:
with your_table (logdate, status) as (
select to_date('20190101 00:01', 'YYYYMMDD HH24:MI'), 'ok' from dual
union all select to_date('20190101 00:02', 'YYYYMMDD HH24:MI'), 'notok' from dual
union all select to_date('20190101 00:03', 'YYYYMMDD HH24:MI'), 'ok' from dual
union all select to_date('20190102 00:01', 'YYYYMMDD HH24:MI'), 'ok' from dual
union all select to_date('20190102 00:02', 'YYYYMMDD HH24:MI'), 'notok' from dual
)
select trunc(logdate) as logdate,
count(case when status = 'ok' then status end) as ok,
count(case when status = 'notok' then status end) as notok
from your_table
group by trunc(logdate);
LOGDATE OK NOTOK
---------- ---------- ----------
2019-01-02 1 1
2019-01-01 2 1
You could use sum() instead, and make the case expression evaluate to either zero or one, but the effect is the same - and I prefer to use count() when the overall aim is to count things.
You could also use an explicit pivot, but it does the same thing under the hood, and is probably overkill for this simple scenario.
-- Oracle 11+
with s (Logdate,Status) as (
select 20190101, 'ok' from dual union all
select 20190101, 'notok' from dual union all
select 20190101, 'ok' from dual union all
select 20190102, 'ok' from dual union all
select 20190102, 'notok' from dual)
select *
from s
pivot (count(*) for status in ('ok' as ok, 'notok' as notok))
order by Logdate;
LOGDATE OK NOTOK
---------- ---------- ----------
20190101 2 1
20190102 1 1
Try this:
select logdate, ok, notok from your_table
pivot (count(status) for status in ('ok' as ok, 'notok' as notok));
I am trying to get data from 1 year ago from the given date in bigquery.
date(DATE_ADD(date(DATE_ADD(timestamp(New_date),(DATEDIFF(timestamp(New_date), CURRENT_DATE())-1), "DAY")), -354, "DAY"))
The query above works, but when I try to put it in a case statement I get null.
sum(case when date(New_day) = date(DATE_ADD(date(DATE_ADD(timestamp(New_date),(DATEDIFF(timestamp(New_date), CURRENT_DATE())-1), "DAY")), -354, "DAY")) then 'this is working' else null end) as lastyeardata
How do I get data for 1 year ago on the same day to do a YoY comparison?
Edit #Mikhail's suggestions:
#standardSQl
WITH `yourTable` AS (
SELECT 1 AS id, '2017-08-14' AS New_date, '1234' AS volume UNION ALL
SELECT 2 AS id, '2017-08-13' AS New_date, '2345' AS volume UNION ALL
SELECT 3 AS id, '2017-08-14' AS New_date, '3456' AS volume UNION ALL
SELECT 4 AS id, '2017-08-14' AS New_date, '4567' AS volume UNION ALL
SELECT 5 AS id, '2016-08-14' AS New_date, '5678' AS volume UNION ALL
SELECT 6 AS id, '2016-08-13' AS New_date, '6789' AS volume UNION ALL
SELECT 7 AS id, '2016-08-12' AS New_date, '6789' AS volume UNION ALL
SELECT 8 AS id, '2016-08-11' AS New_date, '1011' AS volume
)
select
New_date
,volume as thisyeardata
,Case
WHEN PARSE_DATE('%Y-%m-%d', New_date) = DATE_ADD(PARSE_DATE('%Y-%m-%d', New_date), INTERVAL -1 YEAR)
THEN volume
ELSE NULL end as lastyearvolume
,DATE_ADD(PARSE_DATE('%Y-%m-%d', New_date), INTERVAL -1 YEAR) as lastyear
FROM `yourTable`
For some reason lastyearvolume is giving me null.
Below is for BigQuery Standard SQL (which is really recommended by BigQuery team to use versus Legacy one)
#standardSQl
WITH `yourTable` AS (
SELECT 1 AS id, '2017-08-14' AS New_date, '2016-08-14' AS New_day UNION ALL
SELECT 2 AS id, '2017-08-13' AS New_date, '2016-08-13' AS New_day UNION ALL
SELECT 3 AS id, '2017-08-14' AS New_date, '2016-08-12' AS New_day UNION ALL
SELECT 4 AS id, '2017-08-14' AS New_date, '2016-08-11' AS New_day
)
SELECT
COUNT(
CASE
WHEN PARSE_DATE('%Y-%m-%d', New_day) = DATE_ADD(PARSE_DATE('%Y-%m-%d', New_date), INTERVAL -1 YEAR)
THEN 'this is working'
ELSE NULL
END
) AS lastyeardata
FROM `yourTable`
As you would expect two rows are just counted as they have New_date and New_day year apart, the rest are not
Above example assumes your dates fields are of STRING type
If they are of Date Type - you just omit PARSE_DATE function
DATE_ADD has counterpart - DATE_SUB - so it can be used instead as DATE_SUB(PARSE_DATE('%Y-%m-%d', New_date), INTERVAL 1 YEAR)
Update based on your updated example:
simple and fast way to adjust your query t work:
#standardSQl
WITH `yourTable` AS (
SELECT 1 AS id, '2017-08-13' AS New_date, '1234' AS volume UNION ALL
SELECT 2 AS id, '2017-08-14' AS New_date, '2345' AS volume UNION ALL
SELECT 3 AS id, '2017-08-15' AS New_date, '3456' AS volume UNION ALL
SELECT 4 AS id, '2017-08-16' AS New_date, '4567' AS volume UNION ALL
SELECT 5 AS id, '2016-08-13' AS New_date, '5678' AS volume UNION ALL
SELECT 6 AS id, '2016-08-14' AS New_date, '6789' AS volume UNION ALL
SELECT 7 AS id, '2016-08-15' AS New_date, '6789' AS volume UNION ALL
SELECT 8 AS id, '2016-08-16' AS New_date, '1011' AS volume
)
SELECT
this_year.New_date
,this_year.volume AS thisyeardata
,last_year.volume AS lastyeardata
FROM `yourTable` AS this_year
JOIN `yourTable` AS last_year
ON PARSE_DATE('%Y-%m-%d', last_year.New_date) = DATE_ADD(PARSE_DATE('%Y-%m-%d', this_year.New_date), INTERVAL -1 YEAR)
I have a table with project, period and status with their periodic values. My goal is to show how many months have passed since certain project was last in 'Approved status' (or show what period this status was changed) in the period_leaving_approved_status column. for example, for periods 201005 to 201008 - i would need to show '201005' values, but for periods 201011-201012 - '201011' values. I managed to mark the line where status changes, but i don't know how to apply the condition for the following rows. My example query :
with subq as (
select 123 as project, 201002 as period, 'Approved' as status from dual
union all
select 123 as project, 201003 as period, 'Approved' as status from dual
union all
select 123 as project, 201004 as period, 'Approved' as status from dual
union all
select 123 as project, 201005 as period, 'Pending Close' as status from dual
union all
select 123 as project, 201006 as period, 'Pending Close' as status from dual
union all
select 123 as project, 201007 as period, 'Closed' as status from dual
union all
select 123 as project, 201008 as period, 'Closed' as status from dual
union all
select 123 as project, 201009 as period, 'Approved' as status from dual
union all
select 123 as project, 201010 as period, 'Approved' as status from dual
union all
select 123 as project, 201011 as period, 'Closed' as status from dual
union all
select 123 as project, 201012 as period, 'Closed' as status from dual
union all
select 123 as project, 201101 as period, 'Approved' as status from dual
union all
select 123 as project, 201102 as period, 'Approved' as status from dual
union all
select 123 as project, 201112 as period, 'Approved' as status from dual
union all
select 123 as project, 201301 as period, 'Pending Close' as status from dual
union all
select 123 as project, 201302 as period, 'Closed' as status from dual
union all
select 123 as project, 201203 as period, 'Closed' as status from dual
)
select project,
period,
status,
case when lag(status, 1, null) OVER (ORDER BY period)='Approved'
AND lag(status, 1, null) OVER (ORDER BY period) NOT IN(status) then period end as period_leaving_approved_status
from subq
I would approach this using aggregation:
select project,
max(case when status = 'Approved' then period end) as ApprovedPeriod
from subq;
If you want the time span then something like this:
select project,
months_between(max(case when status = 'Approved' then period end),
sysdate) as monthsSinceApproved,
max(case when status = 'Approved' then period end) as ApprovedPeriod
from subq
group by project;
EDIT:
If you want this information cumulatively on all rows, then use window functions:
select s.*,
max(case when status = 'Approved' then period end) over
(partition b project order by period) as LastApprovedPeriod
from subq s;
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
;