Get a list of month last days between 2 dates? - sql

As the title states, I am trying to get the list of month last days between 2 dates.
Example:
Date 1 -> 2014-08-15
Date 2 -> 2014-10-15
Expected output
2014-08-31
2014-09-30
The query I am using to achieve this is the following:
SELECT DISTINCT LAST_DAY(TO_DATE('2014-08-15') - 1 + ROWNUM)
FROM ALL_OBJECTS
WHERE TO_DATE('2014-08-15') - 1 + ROWNUM <= TO_DATE('2014-10-15');
However I have a feeling there might be a more elegant way to achieve it.
Any input would be appreciated!

To get the same result as yours:
select add_months(LAST_DAY(date'2014-08-15'),level-1)
from dual
connect by add_months(trunc(date'2014-08-15','mm'),level-1)<= date'2014-10-15';
But I think 2014-10-31 doesn't satisfy your condition - "between 2 dates".
So probably it should be something like
select add_months(LAST_DAY(date'2014-08-15'),level-1)
from dual
connect by add_months(LAST_DAY(date'2014-08-15'),level-1)<=date'2014-10-15';
or
select add_months(LAST_DAY(date'2014-08-15'),level-1)
from dual
connect by level<=months_between(date'2014-10-15',date'2014-08-15');
Final query:
select add_months(LAST_DAY(date'2014-11-07'),level-1)
from dual where add_months(LAST_DAY(date'2014-11-07'),level-1)<=date'2014-12-07'
connect by add_months(LAST_DAY(date'2014-11-07'),level-1)<=date'2014-12-07';

Related

Using Parameter within timestamp_trunc in SQL Query for DataStudio

I am trying to use a custom parameter within DataStudio. The data is hosted in BigQuery.
SELECT
timestamp_trunc(o.created_at, #groupby) AS dateMain,
count(o.id) AS total_orders
FROM `x.default.orders` o
group by 1
When I try this, it returns an error saying that "A valid date part name is required at [2:35]"
I basically need to group the dates using a parameter (e.g. day, week, month).
I have also included a screenshot of how I have created the parameter in Google DataStudio. There is a default value set which is "day".
A workaround that might do the trick here is to use a rollup in the group by with the different levels of aggregation of the date, since I am not sure you can pass a DS parameter to work like that.
See the following example for clarity:
with default_orders as (
select timestamp'2021-01-01' as created_at, 1 as id
union all
select timestamp'2021-01-01', 2
union all
select timestamp'2021-01-02', 3
union all
select timestamp'2021-01-03', 4
union all
select timestamp'2021-01-03', 5
union all
select timestamp'2021-01-04', 6
),
final as (
select
count(id) as count_orders,
timestamp_trunc(created_at, day) as days,
timestamp_trunc(created_at, week) as weeks,
timestamp_trunc(created_at, month) as months
from
default_orders
group by
rollup(days, weeks, months)
)
select * from final
The output, then, would be similar to the following:
count | days | weeks | months
------+------------+----------+----------
6 | null | null | null <- this, represents the overall (counted 6 ids)
2 | 2021-01-01| null | null <- this, the 1st rollup level (day)
2 | 2021-01-01|2020-12-27| null <- this, the 1st and 2nd (day, week)
2 | 2021-01-01|2020-12-27|2021-01-01 <- this, all of them
And so on.
At the moment of visualizing this on data studio, you have two options: setting the metric as Avg instead of Sum, because as you can see there's kind of a duplication at each stage of the day column; or doing another step in the query and get rid of nulls, like this:
select
*
from
final
where
days is not null and
weeks is not null and
months is not null

how to display the number of created records in the query if you have only the first record and the last one

I have code with which to create records in the range between the first and last. To do this, I only need to specify the first record and the last. Here is the code:
SELECT SUBSTR (:P5_FIRST_SEALS, 1, 1) ||
LPAD(TO_CHAR ( TO_NUMBER (REGEXP_SUBSTR (:P5_FIRST_SEALS, '\d+$')) + level - 1), 8,'0')
AS x_SEAL_NUMBER
FROM dual
CONNECT BY LEVEL <=
TO_NUMBER (
REGEXP_SUBSTR (:P5_LAST_SEALS, '\d+$'))
- TO_NUMBER (
REGEXP_SUBSTR (:P5_FIRST_SEALS, '\d+$'))
+ 1;
For example, I said the first number A14602157 and the last A14602167. (10 entries) in response to the query would be:
After I find what records I need to make, I write a log in the table, where I specify the first record, the last record, and the date. Here's how I get data from the log table:
SELECT FIRST_SEALS, LAST_SEALS,DATE from SEC_TRANSFER_SEALS
Is it possible to make this query in such a way that not only the first and last record of the range and the number of added records appear?
I would like the answer to the second request to be this :
SELECT FIRST_SEALS, LAST_SEALS,DATE , count(FIRST_SEALS between LAST_SEALS) from SEC_TRANSFER_SEALS
A14602157 , A14602167 , 07-06-21 , 10
You have all info on your Apex page (that is Apex, right?), so just reuse it:
SQL> select :P5_FIRST_SEALS as first_seals,
2 :P5_LAST_SEALS as last_seals,
3 trunc(sysdate) as datum,
4 :P5_LAST_SEALS - :P5_FIRST_SEALS + 1 as total_number
5 from dual;
FIRST_SEALS LAST_SEALS DATUM TOTAL_NUMBER
----------- ---------- ----------- ------------
1 10 07-jul-2021 10
SQL>

12 future numbers required in Oracle SQL

I have one requirement in SQL where
We have couple of market ids
Let say 20,30,40
And here for each market using one plsql function we are getting one campaign number let's say for market 20 I got 20210306,for mrkt 30 we got 20210307, and for 40 mrkt id 20210308.
Now what I want to achieve for each market we should have current campaign no as 20210306 as well as 12 future campaign should be produced by SQL query based on each current campaign of the market.
I am doing it using union all and campaign+1,2,3 and so on which is taking time . Do we have some brief logic for it.
Please suggest
Thanks,
Vijay
You can generate series of data (numbers or dates or even characters) using the LEVEL pseudo column with a CONNECT BY clause.
In your case, it sounds like you want generate the next 10 numbers starting from 20210306.
That could be done like this:
WITH current_campaign ( nr ) AS
( SELECT 20210306 FROM dual )
SELECT
nr + level
FROM
current_campaign
CONNECT BY
level < 13;
20210307
20210308
20210309
20210310
20210311
20210312
20210313
20210314
20210315
20210316
20210317
20210318
The 20210306 could also represent the date March, 6, 2021. If you want to generate those numbers date based numbers you could do that too. For example (starting from 0629 so the result shows the month change, only showing 5 rows)
WITH current_campaign ( cdate ) AS
( SELECT DATE'2021-06-29' FROM dual ) SELECT
TO_CHAR(cdate + level,'YYYYDDMM')
FROM
current_campaign
CONNECT BY
level < 6;
20213006
20210107
20210207
20210307
20210407

Find each position of same characters in a string through SQL query

Below is my data set
Year Month Holiday list
----------------------------------------------------------
2007 1 WWHHWWWWWHHWWWWWHHWWWWWHHWWWWWH
2008 4 HWWWWWHHWWWWWHHWWWWWHHWWWWWHHWW
I want to write SQL query to get below output wherein the position of each "H" is displayed.
Year Month Holiday list
-----------------------------------------------------
2007 1 3 4 10 11 18 19 25 26 31
2008 4 1 7 8 14 15 21 22 28 29
The INSTR function in Oracle returns the position of first character. So, I cannot use it.
I could write a Function and pass the Holiday List column value to it. Loop through the string and
find the position of each "H". But, I want to achieve this through plain SQL select query.
How to find each position of same characters in a string through SQL query in Oracle database?
Something like this might do it. You can use the INSTRfunction and a hierarchical query to go through the string and then use the LISTAGG analytic function to concatenate the results.
WITH data AS (
SELECT 2007 year, 1 month, 'WWHHWWWWWHHWWWWWHHWWWWWHHWWWWWH' holiday_list FROM DUAL
UNION
SELECT 2008 year, 4 month, 'HWWWWWHHWWWWWHHWWWWWHHWWWWWHHWW' holiday_list FROM DUAL)
SELECT year, month, (SELECT LISTAGG(INSTR(holiday_list,'H',1,LEVEL),' ') WITHIN GROUP (ORDER BY LEVEL)
FROM DUAL
CONNECT BY LEVEL < INSTR(holiday_list,'H',1,LEVEL)) S
FROM data;
Well you can get a list, see the dbfiddle here
SELECT id, INSTR(some, 'H', 1, c.no) AS Pos
FROM tst CROSS JOIN consecutive c
WHERE INSTR(some, 'H', 1, c.no) > 0
This gives you a list of the days with an H. You could then try to transpose this list. But be warned, there is an intentional CROSS JOIN there, so for every row in your data you will get up to 31 rows with this statement. Like Tim Biegeleisen said, it may be better to use an UDF for this.

How to find record count of every single day for a given period in a single sql query?

I have the following table routes with their effective and expiry dates.
r_no name eff_date exp_date
1 test1 01-jun-2013 INF
2 test2 10-jun-2013 15-jun-2013
3 test3 01-jan-2013 25-jun-2013
4 test4 01-feb-2013 31-dec-2013
Input will be given by the user say for Eg: 01-Jun-2013 to 30-Jun-2013`
I need to get the count of the routes for each and every day in that entire period.
Date Routes_Count
01-Jun-2013 3
02-Jun-2013 3
.
.
10-Jun-2013 4
.
.
20-Jun-2013 3
.
.
30-Jun-2013 2
Could someone help me to get the above results using a single query in Oracle 11g?
You can do it this way,
WITH cte AS
(SELECT TO_DATE ('01-jun-2013', 'dd-mon-yyyy') + LEVEL - 1 dte
FROM DUAL
CONNECT BY LEVEL <=
TO_DATE ('30-jun-2013', 'dd-mon-yyyy') + 1
- TO_DATE ('01-jun-2013', 'dd-mon-yyyy'))
SELECT cte.dte, COUNT (1)
FROM cte, routes
WHERE cte.dte BETWEEN NVL (routes.eff_date, '01-jan-0001')
AND NVL (routes.exp_date, '31-dec-4000')
GROUP BY cte.dte
ORDER BY 1;
sql fiddle
Here cte is the temporary table that I have generated, which gives all the dates from 1st June to 30th June. Join this with your routes table, so that the date is within the effective and expiry date of the route. Note that, if there is no exp_date or eff_date, i substitute it with a date which is too far in the future or too far in the past. Now that you have got all the routes for each date, group by the date, to get the count.